Files
ViperOS/VKernel/Kernel/drivers/storage/ide/ide.vp
T

163 lines
6.4 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 asm
import drivers.usb.pci as pci
enum IDError:
IDE_ERR_NONE, IDE_ERR_TIMEOUT, IDE_ERR_DEVICE_ERROR
IDE_ERR_DEVICE_BUSY, IDE_ERR_INVALID_PARAM, IDE_ERR_BUFFER_NULL
IDE_ERR_SECTOR_COUNT, IDE_ERR_DEVICE_NOT_READY, IDE_ERR_DEVICE_FAILURE
IDE_ERR_COMMAND_FAILED, IDE_ERR_INVALID_LBA
define IDE_baseport_PRIMARY = 0x1F0
define IDE_baseport_SECONDARY = 0x170
define IDE_STATUS_BSY = 0x80
define IDE_STATUS_DRDY = 0x40
define IDE_STATUS_DF = 0x20
define IDE_STATUS_DRQ = 0x08
define IDE_STATUS_ERR = 0x01
define SECTOR_SIZE = 512
_ide_initialized: int = 0
def waitReady(baseport: uint16) -> static IDError:
timeout: int = 100000
for _ in range(timeout, 0, -1):
status: uint8 = asm.inb(baseport + 7)
if status & IDE_STATUS_DF:
return IDError.IDE_ERR_DEVICE_FAILURE
if not status & IDE_STATUS_BSY and status & IDE_STATUS_DRDY:
return IDError.IDE_ERR_NONE
return IDError.IDE_ERR_TIMEOUT
def waitDataReady(baseport: uint16) -> static IDError:
timeout: int = 100000
for _ in range(timeout, 0, -1):
status: uint8 = asm.inb(baseport + 7)
if status & IDE_STATUS_ERR: return IDError.IDE_ERR_DEVICE_ERROR
if status & IDE_STATUS_DF: return IDError.IDE_ERR_DEVICE_FAILURE
if not status & IDE_STATUS_BSY and (status & IDE_STATUS_DRQ or status & IDE_STATUS_DRDY):
return IDError.IDE_ERR_NONE
return IDError.IDE_ERR_TIMEOUT
def init() -> int:
global _ide_initialized
if _ide_initialized: return IDError.IDE_ERR_NONE
bus: uint8
dev_n: uint8
func: uint8
for bus in range(2):
for dev_n in range(32):
for func in range(8):
vendor: uint16 = pci.pci_read16(bus, dev_n, func, 0x00)
if vendor == 0xFFFF: continue
cls: uint32 = pci.pci_get_class(bus, dev_n, func)
if cls == 0x010180 or cls == 0x0101:
cmd: uint16 = pci.pci_read16(bus, dev_n, func, 0x04)
pci.pci_write32(bus, dev_n, func, 0x04, uint32(cmd | 0x0007))
asm.outb(0x3F6, 0x04)
for _ in range(10000): asm.nop()
asm.outb(0x3F6, 0x00)
for _ in range(10000): asm.nop()
status: uint8 = asm.inb(0x1F7)
_ide_initialized = 1
return IDError.IDE_ERR_NONE
def readSector(lba: uint32, buffer: rawptr) -> int:
if buffer is None: return IDError.IDE_ERR_BUFFER_NULL
err: IDError = waitReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE:
return err
asm.outb(0x3F6, 0x00)
asm.io_wait()
# 命令块寄存器**连写之间必须插 I/O 延时**:QEMU 的 IDE 是逐条采样寄存器的,缺延时
# 会出现"只写入部分寄存器"⇒ LBA 保留上一次的值 ⇒ 读到**另一个扇区**的数据(现场:
# 读根目录扇区 LBA 8192 却读回 ELF 文件头,导致目录项全是垃圾 → spawn 失败)。
asm.outb(0x1F2, 1)
asm.io_wait()
asm.outb(0x1F3, uint8(lba & 0xFF))
asm.io_wait()
asm.outb(0x1F4, uint8((lba >> 8) & 0xFF))
asm.io_wait()
asm.outb(0x1F5, uint8((lba >> 16) & 0xFF))
asm.io_wait()
asm.outb(0x1F6, uint8(0xE0 | ((lba >> 24) & 0x0F)))
asm.io_wait()
asm.outb(0x1F7, 0x20)
asm.io_wait()
err = waitDataReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE:
st: uint8 = asm.inb(0x1F7)
return err
st2: uint8 = asm.inb(0x1F7)
# 读完再核一次 ERR:置位说明这次数据无效,不能当成功返回(否则上层会拿到错内容)
if st2 & IDE_STATUS_ERR:
return IDError.IDE_ERR_DEVICE_ERROR
buf: uint16* = uint16*(buffer)
for j in range(256):
*buf = asm.inw(0x1F0)
# buf 是 i16*(uint16*):+1 表示前进 1 个元素 = 2 字节,256 次恰好填满 512B
buf = buf + 1
first_byte: uint8 = *(uint8*(buffer))
# viperlib.snprintf(c.Addr(dbg), 120, "[readSector] lba=%lu status=0x%02x first=%02x\n", lba, uint32(st2), uint32(first_byte))
# serial.puts(dbg)
return IDError.IDE_ERR_NONE
def writeSector(lba: uint32, buffer: const rawptr) -> int:
if buffer is None: return IDError.IDE_ERR_BUFFER_NULL
err: IDError = waitReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE: return err
asm.outb(0x3F6, 0x00)
asm.io_wait()
# 与 readSector 同理:命令块寄存器连写之间必须插 I/O 延时,否则 QEMU 只采样到部分寄存器
# ⇒ LBA 残留旧值 ⇒ 把数据写到**错误的扇区**(写盘路径同样致命)。
asm.outb(0x1F2, 1)
asm.io_wait()
asm.outb(0x1F3, uint8(lba & 0xFF))
asm.io_wait()
asm.outb(0x1F4, uint8((lba >> 8) & 0xFF))
asm.io_wait()
asm.outb(0x1F5, uint8((lba >> 16) & 0xFF))
asm.io_wait()
asm.outb(0x1F6, uint8(0xE0 | ((lba >> 24) & 0x0F)))
asm.io_wait()
asm.outb(0x1F7, 0x30)
asm.io_wait()
err = waitDataReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE: return err
buf: uint16* = uint16*(buffer)
for j in range(256):
val: uint16 = *buf
asm.outw(0x1F0, val)
# buf 是 i16*(uint16*):+1 表示前进 1 个元素 = 2 字节,256 次恰好读完 512B
# (IR 取证:`buf + 2` 生成 getelementptr ..., i16,按元素缩放 → +4 字节,越界)
buf = buf + 1
asm.outb(0x1F7, 0xE7)
waitReady(IDE_baseport_PRIMARY)
return IDError.IDE_ERR_NONE
def getDiskSize() -> uint64:
err: IDError = waitReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE: return 0
asm.outb(0x1F6, 0xE0)
asm.outb(0x1F7, 0xEC)
err = waitDataReady(IDE_baseport_PRIMARY)
if err != IDError.IDE_ERR_NONE: return 0
status: uint8 = asm.inb(0x1F7)
if not status & IDE_STATUS_DRDY: return 0
if status & IDE_STATUS_ERR: return 0
identify_data: t.CArray[uint16, 256]
for i in range(256):
identify_data[i] = asm.inw(0x1F0)
totalSectors: uint64 = identify_data[60] | uint64(identify_data[61]) << 16
if identify_data[83] & (1 << 10):
lba48_sectors: uint64 = (
uint64(identify_data[100]) |
uint64(identify_data[101]) << 16 |
uint64(identify_data[102]) << 32 |
uint64(identify_data[103]) << 48)
if lba48_sectors > totalSectors:
totalSectors = lba48_sectors
return totalSectors