Files
ViperOS/VKernel/Kernel/drivers/usb/uhci.vp
T
TermiNexus 9745f8832a fix(vkernel): UHCI frame list 步长越界 12KB 砸坏 buddy 堆(堆损坏根因);paging 页表记录指针缩放;清理临时探针
根因: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 装载,便于隔离内核问题)。
2026-10-15 13:35:34 +08:00

421 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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