根因:uhci.vp 两处写成 _frame_list + uint64(i) * 4 —— uint32* 的 +n 已按元素(4字节)缩放, 再乘 4 使实际步长变成 16 字节;1024 次循环写到 16KB,而缓冲只有 UHCI_NUM_FRAMES*4 = 4KB ⇒ 越界 12KB 覆盖相邻 buddy 空闲块头(现场:[0E][PF] cr2=0xba4c000 err=0x3 rip=0x10e598 rax=0x3000=12288),把空闲链 next/prev 写坏 ⇒ 随机 [mm] BAD blk、malloc 失败、app 崩溃。 定位手段:把空闲链表头页在内核页表标只读(paging.init 已置 CR0.WP=1),任何写入立即 #PF 并给出 rip/cr2 直接指认写者(该探针定位完已删除)。 paging:save_pt_range / restore_pt_range 里的 save_buf + i*16 在 uint64* 上实际是 128 字节 步长、+8 实际是 +64 字节(IR 取证:getelementptr i64 ⇒ 按 8 字节元素缩放),改为显式字节 地址推进记录 + 元素索引(+1 = +8 字节),两处布局保持一致。 保留的加固(非探针):memory_block.magic 与 allocate_block/free_block/merge_blocks 的块头 校验、free() 的堆范围校验、slab 建页的 total 边界护栏、页表池按真实 buf_size 计页与 pt_pool_magic/pt_pool_limit 哨兵、#GP 分支补打 rax 便于定位非规范地址写。 elf.vp 新增 ELF_LOAD 开关(=1 正常装载;置 0 可整体跳过 app/.so 装载,便于隔离内核问题)。
421 lines
14 KiB
Plaintext
421 lines
14 KiB
Plaintext
import t, c
|
||
import asm
|
||
import mm.mm as mm
|
||
import string
|
||
import drivers.serial.uart.serial as serial
|
||
import platform.pch.timer as timer
|
||
import intr.idt as idt
|
||
import platform.pch.pic as pic
|
||
import drivers.usb.pci as pci
|
||
import viperlib
|
||
|
||
|
||
define UHCI_USBCMD = 0x00
|
||
define UHCI_USBSTS = 0x02
|
||
define UHCI_USBINTR = 0x04
|
||
define UHCI_FRNUM = 0x06
|
||
define UHCI_FLBASEADD = 0x08
|
||
define UHCI_SOFMOD = 0x0C
|
||
define UHCI_PORTSC1 = 0x10
|
||
define UHCI_PORTSC2 = 0x12
|
||
|
||
define UHCI_CMD_RUN = 0x0001
|
||
define UHCI_CMD_HCRESET = 0x0002
|
||
define UHCI_CMD_GRESET = 0x0004
|
||
define UHCI_CMD_MAXPKT = 0x0080
|
||
define UHCI_CMD_CF = 0x0040
|
||
|
||
define UHCI_STS_HCHALTED = 0x0020
|
||
define UHCI_STS_USBINT = 0x0001
|
||
define UHCI_STS_USBERRINT = 0x0002
|
||
define UHCI_STS_RESUME = 0x0004
|
||
define UHCI_INTR_TIMEOUT = 0x0001
|
||
define UHCI_INTR_RESUME = 0x0002
|
||
define UHCI_INTR_IOC = 0x0004
|
||
define UHCI_INTR_SHORT = 0x0008
|
||
|
||
define UHCI_PORT_CONNECT = 0x0001
|
||
define UHCI_PORT_CONNECT_CHANGE = 0x0002
|
||
define UHCI_PORT_ENABLE = 0x0004
|
||
define UHCI_PORT_ENABLE_CHANGE = 0x0008
|
||
define UHCI_PORT_RESET = 0x0200
|
||
define UHCI_PORT_LOW_SPEED = 0x0100
|
||
|
||
define TD_CTRL_ACTIVE = 0x00800000
|
||
define TD_CTRL_IOC = 0x01000000
|
||
define TD_CTRL_LS = 0x04000000
|
||
define TD_CTRL_SPD = 0x20000000
|
||
define TD_CTRL_CERR_SHIFT = 27
|
||
define TD_CTRL_CERR_MASK = 0x18000000
|
||
|
||
define TD_TOKEN_SETUP = 0x2D
|
||
define TD_TOKEN_IN = 0x69
|
||
define TD_TOKEN_OUT = 0xE1
|
||
|
||
define TD_TOKEN_DATA0 = 0
|
||
define TD_TOKEN_DATA1 = 1
|
||
|
||
define TD_LINK_TERMINATE = 0x00000001
|
||
define TD_LINK_QH = 0x00000002
|
||
define TD_LINK_DEPTH_FIRST = 0x00000004
|
||
|
||
define UHCI_NUM_FRAMES = 1024
|
||
define UHCI_INT_SLOTS = 4
|
||
|
||
class uhci_td:
|
||
link: uint32
|
||
ctrl_status: uint32
|
||
token: uint32
|
||
buffer: uint32
|
||
|
||
class uhci_qh:
|
||
link: uint32
|
||
element: uint32
|
||
|
||
class int_slot:
|
||
td: uhci_td*
|
||
callback: int*
|
||
active: int
|
||
qh: uhci_qh*
|
||
|
||
_io_base: uint16 = 0
|
||
_frame_list: uint32* = None
|
||
_ctrl_qh: uhci_qh* = None
|
||
_bulk_qh: uhci_qh* = None
|
||
_irq: int = -1
|
||
_int_slots: [UHCI_INT_SLOTS]int_slot
|
||
_int_slot_count: int = 0
|
||
|
||
def _reg_read16(offset: uint16) -> uint16:
|
||
return asm.inw(_io_base + offset)
|
||
|
||
def _reg_write16(offset: uint16, value: uint16):
|
||
asm.outw(_io_base + offset, value)
|
||
|
||
def _reg_read32(offset: uint16) -> uint32:
|
||
return asm.inl(_io_base + offset)
|
||
|
||
def _reg_write32(offset: uint16, value: uint32):
|
||
asm.outl(_io_base + offset, value)
|
||
|
||
def _uhci_irq_handler() -> int:
|
||
sts: uint16 = _reg_read16(UHCI_USBSTS)
|
||
_reg_write16(UHCI_USBSTS, sts)
|
||
if (sts & (UHCI_STS_USBINT | UHCI_STS_USBERRINT)) == 0:
|
||
return 0
|
||
for si in range(_int_slot_count):
|
||
slot: int_slot* = &_int_slots[si]
|
||
if slot.active and slot.td is not None and slot.callback is not None:
|
||
status: uint32 = slot.td.ctrl_status
|
||
if (status & TD_CTRL_ACTIVE) == 0:
|
||
slot.active = 0
|
||
__asm__(f"""mov rbp, rsp
|
||
and rsp, -16
|
||
call {asminp(slot.callback, ASM_DESCR.REG_ANY)}
|
||
mov rsp, rbp""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
||
ASM_DESCR.CLOBBER_RBX,
|
||
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
||
ASM_DESCR.CLOBBER_RSI, ASM_DESCR.CLOBBER_RDI,
|
||
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
|
||
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11,
|
||
ASM_DESCR.CLOBBER_RBP])
|
||
return 0
|
||
|
||
def register_int_callback(cb: int*) -> int:
|
||
global _int_slot_count
|
||
if _int_slot_count >= UHCI_INT_SLOTS:
|
||
return -1
|
||
si: int = _int_slot_count
|
||
qh: uhci_qh* = mm.malloc_direct(uhci_qh.__sizeof__())
|
||
if qh is None:
|
||
return -2
|
||
string.memset(qh, 0, uhci_qh.__sizeof__())
|
||
_qh_set_link(qh, 0, 0, 1)
|
||
_qh_set_element(qh, 0, 1)
|
||
td: uhci_td* = mm.malloc_direct(uhci_td.__sizeof__())
|
||
if td is None:
|
||
return -3
|
||
string.memset(td, 0, uhci_td.__sizeof__())
|
||
slot: int_slot* = &_int_slots[si]
|
||
slot.td = td
|
||
slot.callback = cb
|
||
slot.active = 0
|
||
slot.qh = qh
|
||
_int_slot_count = si + 1
|
||
_rebuild_int_chain()
|
||
return si
|
||
|
||
def schedule_int_transfer(slot_id: int, dev_addr: uint8, endpoint: uint8, ls: int, max_pkt: uint8, toggle: uint8, buf: rawptr):
|
||
if slot_id < 0 or slot_id >= _int_slot_count:
|
||
return
|
||
slot: int_slot* = &_int_slots[slot_id]
|
||
td: uhci_td* = slot.td
|
||
qh: uhci_qh* = slot.qh
|
||
if td is None or qh is None:
|
||
return
|
||
string.memset(td, 0, uhci_td.__sizeof__())
|
||
_td_set_link(td, 0, 0, 1)
|
||
_td_set_ctrl(td, ls, max_pkt)
|
||
_td_set_token(td, TD_TOKEN_IN, dev_addr, endpoint, toggle, max_pkt)
|
||
td.ctrl_status = td.ctrl_status | TD_CTRL_IOC
|
||
td.buffer = uint32(uint64(buf))
|
||
_qh_set_element(qh, uint32(uint64(td)), 0)
|
||
slot.active = 1
|
||
|
||
def _rebuild_int_chain():
|
||
if _int_slot_count == 0:
|
||
return
|
||
first_qh: uhci_qh* = None
|
||
prev_qh: uhci_qh* = None
|
||
for si in range(_int_slot_count):
|
||
cur_qh: uhci_qh* = _int_slots[si].qh
|
||
if cur_qh is not None:
|
||
if first_qh is None:
|
||
first_qh = cur_qh
|
||
if prev_qh is not None:
|
||
_qh_set_link(prev_qh, uint32(uint64(cur_qh)) | TD_LINK_QH, 1, 0)
|
||
prev_qh = cur_qh
|
||
if prev_qh is not None:
|
||
_qh_set_link(prev_qh, uint32(uint64(_ctrl_qh)) | TD_LINK_QH, 1, 0)
|
||
if first_qh is not None:
|
||
qh_addr: uint32 = uint32(uint64(first_qh)) | TD_LINK_QH
|
||
i: int
|
||
for i in range(UHCI_NUM_FRAMES):
|
||
# 注意:`uint32* + n` 的指针算术**已按元素(4 字节)缩放**,不要再乘 4 ——
|
||
# 之前写成 `_frame_list + uint64(i) * 4`,实际步长 16 字节,1024 次循环写到 16KB,
|
||
# 而缓冲只有 UHCI_NUM_FRAMES*4 = 4KB ⇒ 越界 12KB 直接砸坏 buddy 空闲块头
|
||
# (现场:[0E][PF] cr2=0xba4c000 err=0x3 rip=0x10e598 rax=0x3000,rax 正是 12288)。
|
||
entry_ptr: uint32* = _frame_list + uint64(i)
|
||
*entry_ptr = qh_addr
|
||
|
||
def _td_set_link(td: uhci_td*, addr: uint32, is_qh: int, terminate: int):
|
||
val: uint32 = (addr & 0xFFFFFFF0)
|
||
if is_qh: val = val | TD_LINK_QH
|
||
if terminate: val = val | TD_LINK_TERMINATE
|
||
td.link = val
|
||
|
||
def _td_set_token(td: uhci_td*, pid: uint8, dev_addr: uint8, endpoint: uint8, toggle: uint8, max_len: uint16):
|
||
len_bits: uint32
|
||
if max_len == 0:
|
||
len_bits = 0x7FF
|
||
else:
|
||
len_bits = uint32(max_len) - 1
|
||
td.token = (uint32(pid) << 0) | (uint32(dev_addr) << 8) | (uint32(endpoint & 0x0F) << 15) | (uint32(toggle & 1) << 19) | (len_bits << 21)
|
||
|
||
def _td_set_ctrl(td: uhci_td*, ls: int, maxlen: uint16):
|
||
td.ctrl_status = TD_CTRL_ACTIVE | (3 << TD_CTRL_CERR_SHIFT)
|
||
if ls: td.ctrl_status = td.ctrl_status | TD_CTRL_LS
|
||
|
||
def _qh_set_link(qh: uhci_qh*, addr: uint32, is_qh: int, terminate: int):
|
||
val: uint32 = (addr & 0xFFFFFFF0)
|
||
if is_qh: val = val | TD_LINK_QH
|
||
if terminate: val = val | TD_LINK_TERMINATE
|
||
qh.link = val
|
||
|
||
def _qh_set_element(qh: uhci_qh*, addr: uint32, terminate: int):
|
||
val: uint32 = (addr & 0xFFFFFFF0)
|
||
if terminate: val = val | TD_LINK_TERMINATE
|
||
qh.element = val
|
||
|
||
def _wait_for_complete(td: uhci_td*, timeout_ms: int) -> int:
|
||
start: uint64 = timer.timer_get_ticks()
|
||
deadline: uint64 = start + uint64(timeout_ms)
|
||
poll_cnt: int = 0
|
||
while timer.timer_get_ticks() < deadline:
|
||
status: uint32 = td.ctrl_status
|
||
if (status & TD_CTRL_ACTIVE) == 0:
|
||
if (status & 0x00600000) != 0:
|
||
return -1
|
||
return 0
|
||
poll_cnt += 1
|
||
if poll_cnt >= 100:
|
||
poll_cnt = 0
|
||
asm.sti()
|
||
asm.hlt()
|
||
return -2
|
||
|
||
def reset(io_base: uint16) -> int:
|
||
global _io_base
|
||
_io_base = io_base
|
||
_reg_write16(UHCI_USBCMD, UHCI_CMD_HCRESET)
|
||
for i in range(1000):
|
||
if (_reg_read16(UHCI_USBCMD) & UHCI_CMD_HCRESET) == 0:
|
||
break
|
||
_reg_write16(UHCI_USBSTS, 0xFFFF)
|
||
_reg_write16(UHCI_USBINTR, 0x0000)
|
||
return 0
|
||
|
||
def init(io_base: uint16) -> int:
|
||
global _irq, _io_base, _frame_list, _ctrl_qh, _bulk_qh, _int_slot_count
|
||
_io_base = io_base
|
||
_int_slot_count = 0
|
||
|
||
serial.puts("[uhci] resetting...\n")
|
||
if reset(io_base) != 0:
|
||
serial.puts("[uhci] reset failed\n")
|
||
return -1
|
||
|
||
serial.puts("[uhci] allocating frame list...\n")
|
||
_frame_list = mm.malloc(UHCI_NUM_FRAMES * 4)
|
||
if _frame_list is None:
|
||
serial.puts("[uhci] frame list alloc failed\n")
|
||
return -2
|
||
string.memset(_frame_list, 0, UHCI_NUM_FRAMES * 4)
|
||
|
||
_ctrl_qh = mm.malloc_direct(uhci_qh.__sizeof__())
|
||
if _ctrl_qh is None:
|
||
serial.puts("[uhci] ctrl_qh alloc failed\n")
|
||
return -3
|
||
string.memset(_ctrl_qh, 0, uhci_qh.__sizeof__())
|
||
_qh_set_link(_ctrl_qh, 0, 0, 1)
|
||
_qh_set_element(_ctrl_qh, 0, 1)
|
||
|
||
_bulk_qh = mm.malloc_direct(uhci_qh.__sizeof__())
|
||
if _bulk_qh is None:
|
||
serial.puts("[uhci] bulk_qh alloc failed\n")
|
||
return -4
|
||
string.memset(_bulk_qh, 0, uhci_qh.__sizeof__())
|
||
_qh_set_link(_bulk_qh, 0, 0, 1)
|
||
_qh_set_element(_bulk_qh, 0, 1)
|
||
|
||
serial.puts("[uhci] setting up frame list...\n")
|
||
qh_addr: uint32 = uint32(_ctrl_qh) | TD_LINK_QH
|
||
for i in range(UHCI_NUM_FRAMES):
|
||
# 同上:不要乘 4(`uint32* + n` 已按 4 字节缩放)。两处都曾以 16 字节步长越界 12KB。
|
||
entry_ptr: uint32* = _frame_list + uint64(i)
|
||
*entry_ptr = qh_addr
|
||
|
||
_reg_write32(UHCI_FLBASEADD, uint32(uint64(_frame_list)))
|
||
_reg_write16(UHCI_FRNUM, 0)
|
||
_reg_write16(UHCI_SOFMOD, 64)
|
||
|
||
_reg_write16(UHCI_USBCMD, UHCI_CMD_CF | UHCI_CMD_MAXPKT | UHCI_CMD_RUN)
|
||
|
||
timer.timer_msleep(10)
|
||
|
||
_reg_write16(UHCI_USBSTS, 0xFFFF)
|
||
_reg_write16(UHCI_USBINTR, UHCI_INTR_IOC | UHCI_INTR_SHORT)
|
||
|
||
_irq = int(pci._uhci_dev.irq)
|
||
if _irq > 0 and _irq < 16:
|
||
idt.irqInstallHandler(_irq, _uhci_irq_handler, "uhci")
|
||
pic.clearMask(uint8(_irq))
|
||
|
||
serial.puts("[uhci] started\n")
|
||
return 0
|
||
|
||
def port_reset(port: int) -> int:
|
||
sc: uint16
|
||
if port == 0:
|
||
sc = _reg_read16(UHCI_PORTSC1)
|
||
else:
|
||
sc = _reg_read16(UHCI_PORTSC2)
|
||
|
||
if (sc & UHCI_PORT_CONNECT) == 0:
|
||
return -1
|
||
|
||
if port == 0:
|
||
_reg_write16(UHCI_PORTSC1, sc | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
|
||
else:
|
||
_reg_write16(UHCI_PORTSC2, sc | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
|
||
|
||
if port == 0:
|
||
_reg_write16(UHCI_PORTSC1, UHCI_PORT_RESET)
|
||
else:
|
||
_reg_write16(UHCI_PORTSC2, UHCI_PORT_RESET)
|
||
|
||
timer.timer_msleep(50)
|
||
|
||
if port == 0:
|
||
_reg_write16(UHCI_PORTSC1, 0x0000)
|
||
else:
|
||
_reg_write16(UHCI_PORTSC2, 0x0000)
|
||
|
||
timer.timer_msleep(10)
|
||
|
||
if port == 0:
|
||
sc = _reg_read16(UHCI_PORTSC1)
|
||
_reg_write16(UHCI_PORTSC1, sc | UHCI_PORT_ENABLE | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
|
||
else:
|
||
sc = _reg_read16(UHCI_PORTSC2)
|
||
_reg_write16(UHCI_PORTSC2, sc | UHCI_PORT_ENABLE | UHCI_PORT_CONNECT_CHANGE | UHCI_PORT_ENABLE_CHANGE)
|
||
|
||
timer.timer_msleep(10)
|
||
|
||
if port == 0:
|
||
sc = _reg_read16(UHCI_PORTSC1)
|
||
else:
|
||
sc = _reg_read16(UHCI_PORTSC2)
|
||
|
||
if (sc & UHCI_PORT_ENABLE) == 0:
|
||
return -2
|
||
|
||
return 0
|
||
|
||
def port_is_connected(port: int) -> int:
|
||
sc: uint16
|
||
if port == 0:
|
||
sc = _reg_read16(UHCI_PORTSC1)
|
||
else:
|
||
sc = _reg_read16(UHCI_PORTSC2)
|
||
if sc & UHCI_PORT_CONNECT: return 1
|
||
return 0
|
||
|
||
def port_is_low_speed(port: int) -> int:
|
||
sc: uint16
|
||
if port == 0:
|
||
sc = _reg_read16(UHCI_PORTSC1)
|
||
else:
|
||
sc = _reg_read16(UHCI_PORTSC2)
|
||
if sc & UHCI_PORT_LOW_SPEED: return 1
|
||
return 0
|
||
|
||
def control_transfer(dev_addr: uint8, endpoint: uint8, ls: int, max_pkt: uint8, setup_buf: rawptr, data_buf: rawptr, data_len: uint16, data_in: int) -> int:
|
||
td_count: int = 2
|
||
if data_len > 0:
|
||
td_count = td_count + 1
|
||
total: uint64 = uint64(td_count) * uhci_td.__sizeof__()
|
||
tds: uhci_td* = mm.malloc_direct(total)
|
||
if tds is None: return -1
|
||
string.memset(tds, 0, total)
|
||
|
||
setup_td: uhci_td* = tds
|
||
_td_set_link(setup_td, uint32(uint64(tds) + uhci_td.__sizeof__()), 0, 0)
|
||
_td_set_ctrl(setup_td, ls, 8)
|
||
_td_set_token(setup_td, TD_TOKEN_SETUP, dev_addr, endpoint, TD_TOKEN_DATA0, 8)
|
||
setup_td.buffer = uint32(uint64(setup_buf))
|
||
|
||
if data_len > 0:
|
||
data_td: uhci_td* = uhci_td*(uint64(tds) + uhci_td.__sizeof__())
|
||
_td_set_link(data_td, uint32(uint64(data_td) + uhci_td.__sizeof__()), 0, 0)
|
||
_td_set_ctrl(data_td, ls, max_pkt)
|
||
if data_in:
|
||
_td_set_token(data_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, data_len)
|
||
else:
|
||
_td_set_token(data_td, TD_TOKEN_OUT, dev_addr, endpoint, TD_TOKEN_DATA1, data_len)
|
||
data_td.buffer = uint32(uint64(data_buf))
|
||
|
||
status_td: uhci_td* = uhci_td*(uint64(data_td) + uhci_td.__sizeof__())
|
||
_td_set_link(status_td, 0, 0, 1)
|
||
_td_set_ctrl(status_td, ls, max_pkt)
|
||
if data_in:
|
||
_td_set_token(status_td, TD_TOKEN_OUT, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
|
||
else:
|
||
_td_set_token(status_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
|
||
status_td.buffer = 0
|
||
else:
|
||
status_td: uhci_td* = uhci_td*(uint64(tds) + uhci_td.__sizeof__())
|
||
_td_set_link(status_td, 0, 0, 1)
|
||
_td_set_ctrl(status_td, ls, max_pkt)
|
||
_td_set_token(status_td, TD_TOKEN_IN, dev_addr, endpoint, TD_TOKEN_DATA1, 0)
|
||
status_td.buffer = 0
|
||
|
||
_qh_set_element(_ctrl_qh, uint32(uint64(setup_td)), 0)
|
||
|
||
result: int = _wait_for_complete(status_td, 500)
|
||
|
||
_qh_set_element(_ctrl_qh, 0, 1)
|
||
mm.free(tds)
|
||
return result
|