Browse Source

Start working on 32-bit disk driver

master
Jørn Guldberg 5 years ago
parent
commit
656525f1e3
1 changed files with 278 additions and 0 deletions
  1. +278
    -0
      driver/disk_driver.nasm

+ 278
- 0
driver/disk_driver.nasm View File

@ -0,0 +1,278 @@
; Borraowed from https://wiki.osdev.org/ATA_PIO_Mode
; do a singletasking PIO ATA read
; inputs: ebx = # of sectors to read, edi -> dest buffer, esi -> driverdata struct, ebp = 4b LBA
; Note: ebp is a "relative" LBA -- the offset from the beginning of the partition
; outputs: ebp, edi incremented past read; ebx = 0
; flags: zero flag set on success, carry set on failure (redundant)
read_ata_st:
push edx
push ecx
push eax
test ebx, ebx ; # of sectors < 0 is a "reset" request from software
js short .reset
cmp ebx, 0x3fffff ; read will be bigger than 2GB? (error)
stc
jg short .r_don
mov edx, [esi + dd_prtlen] ; get the total partition length (sectors)
dec edx ; (to avoid handling "equality" case)
cmp edx, ebp ; verify ebp is legal (within partition limit)
jb short .r_don ; (carry is set automatically on an error)
cmp edx, ebx ; verify ebx is legal (forget about the ebx = edx case)
jb short .r_don
sub edx, ebx ; verify ebp + ebx - 1 is legal
inc edx
cmp edx, ebp ; (the test actually checks ebp <= edx - ebx + 1)
jb short .r_don
mov dx, [esi + dd_dcr] ; dx = alt status/DCR
in al, dx ; get the current status
test al, 0x88 ; check the BSY and DRQ bits -- both must be clear
je short .stat_ok
.reset:
call srst_ata_st
test ebx, ebx ; bypass any read on a "reset" request
jns short .stat_ok
xor ebx, ebx ; force zero flag on, carry clear
jmp short .r_don
.stat_ok:
; preferentially use the 28bit routine, because it's a little faster
; if ebp > 28bit or esi.stLBA > 28bit or stLBA+ebp > 28bit or stLBA+ebp+ebx > 28bit, use 48 bit
cmp ebp, 0xfffffff
jg short .setreg
mov eax, [esi + dd_stLBA]
cmp eax, 0xfffffff
jg short .setreg
add eax, ebp
cmp eax, 0xfffffff
jg short .setreg
add eax, ebx
cmp eax, 0xfffffff
.setreg:
mov dx, [esi + dd_tf] ; dx = IO port base ("task file")
jle short .read28 ; test the flags from the eax cmp's above
.read48:
test ebx, ebx ; no more sectors to read?
je short .r_don
call pio48_read ; read up to 256 more sectors, updating registers
je short .read48 ; if successful, is there more to read?
jmp short .r_don
.read28:
test ebx, ebx ; no more sectors to read?
je short .r_don
call pio28_read ; read up to 256 more sectors, updating registers
je short .read28 ; if successful, is there more to read?
.r_don:
pop eax
pop ecx
pop edx
ret
;ATA PI0 28bit singletasking disk read function (up to 256 sectors)
; inputs: ESI -> driverdata info, EDI -> destination buffer
; BL = sectors to read, DX = base bus I/O port (0x1F0, 0x170, ...), EBP = 28bit "relative" LBA
; BSY and DRQ ATA status bits must already be known to be clear on both slave and master
; outputs: data stored in EDI; EDI and EBP advanced, EBX decremented
; flags: on success Zero flag set, Carry clear
pio28_read:
add ebp, [esi + dd_stLBA] ; convert relative LBA to absolute LBA
mov ecx, ebp ; save a working copy
mov al, bl ; set al= sector count (0 means 256 sectors)
or dl, 2 ; dx = sectorcount port -- usually port 1f2
out dx, al
mov al, cl ; ecx currently holds LBA
inc edx ; port 1f3 -- LBAlow
out dx, al
mov al, ch
inc edx ; port 1f4 -- LBAmid
out dx, al
bswap ecx
mov al, ch ; bits 16 to 23 of LBA
inc edx ; port 1f5 -- LBAhigh
out dx, al
mov al, cl ; bits 24 to 28 of LBA
or al, byte [esi + dd_sbits] ; master/slave flag | 0xe0
inc edx ; port 1f6 -- drive select
out dx, al
inc edx ; port 1f7 -- command/status
mov al, 0x20 ; send "read" command to drive
out dx, al
; ignore the error bit for the first 4 status reads -- ie. implement 400ns delay on ERR only
; wait for BSY clear and DRQ set
mov ecx, 4
.lp1:
in al, dx ; grab a status byte
test al, 0x80 ; BSY flag set?
jne short .retry
test al, 8 ; DRQ set?
jne short .data_rdy
.retry:
dec ecx
jg short .lp1
; need to wait some more -- loop until BSY clears or ERR sets (error exit if ERR sets)
.pior_l:
in al, dx ; grab a status byte
test al, 0x80 ; BSY flag set?
jne short .pior_l ; (all other flags are meaningless if BSY is set)
test al, 0x21 ; ERR or DF set?
jne short .fail
.data_rdy:
; if BSY and ERR are clear then DRQ must be set -- go and read the data
sub dl, 7 ; read from data port (ie. 0x1f0)
mov cx, 256
rep insw ; gulp one 512b sector into edi
or dl, 7 ; "point" dx back at the status register
in al, dx ; delay 400ns to allow drive to set new values of BSY and DRQ
in al, dx
in al, dx
in al, dx
; After each DRQ data block it is mandatory to either:
; receive and ack the IRQ -- or poll the status port all over again
inc ebp ; increment the current absolute LBA
dec ebx ; decrement the "sectors to read" count
test bl, bl ; check if the low byte just turned 0 (more sectors to read?)
jne short .pior_l
sub dx, 7 ; "point" dx back at the base IO port, so it's unchanged
sub ebp, [esi + dd_stLBA] ; convert absolute lba back to relative
; "test" sets the zero flag for a "success" return -- also clears the carry flag
test al, 0x21 ; test the last status ERR bits
je short .done
.fail:
stc
.done:
ret
;ATA PI0 33bit singletasking disk read function (up to 64K sectors, using 48bit mode)
; inputs: bx = sectors to read (0 means 64K sectors), edi -> destination buffer
; esi -> driverdata info, dx = base bus I/O port (0x1F0, 0x170, ...), ebp = 32bit "relative" LBA
; BSY and DRQ ATA status bits must already be known to be clear on both slave and master
; outputs: data stored in edi; edi and ebp advanced, ebx decremented
; flags: on success Zero flag set, Carry clear
pio48_read:
xor eax, eax
add ebp, [esi + dd_stLBA] ; convert relative LBA to absolute LBA
; special case: did the addition overflow 32 bits (carry set)?
adc ah, 0 ; if so, ah = LBA byte #5 = 1
mov ecx, ebp ; save a working copy of 32 bit absolute LBA
; for speed purposes, never OUT to the same port twice in a row -- avoiding it is messy but best
;outb (0x1F2, sectorcount high)
;outb (0x1F3, LBA4)
;outb (0x1F4, LBA5) -- value = 0 or 1 only
;outb (0x1F5, LBA6) -- value = 0 always
;outb (0x1F2, sectorcount low)
;outb (0x1F3, LBA1)
;outb (0x1F4, LBA2)
;outb (0x1F5, LBA3)
bswap ecx ; make LBA4 and LBA3 easy to access (cl, ch)
or dl, 2 ; dx = sectorcount port -- usually port 1f2
mov al, bh ; sectorcount -- high byte
out dx, al
mov al, cl
inc edx
out dx, al ; LBA4 = LBAlow, high byte (1f3)
inc edx
mov al, ah ; LBA5 was calculated above
out dx, al ; LBA5 = LBAmid, high byte (1f4)
inc edx
mov al, 0 ; LBA6 is always 0 in 32 bit mode
out dx, al ; LBA6 = LBAhigh, high byte (1f5)
sub dl, 3
mov al, bl ; sectorcount -- low byte (1f2)
out dx, al
mov ax, bp ; get LBA1 and LBA2 into ax
inc edx
out dx, al ; LBA1 = LBAlow, low byte (1f3)
mov al, ah ; LBA2
inc edx
out dx, al ; LBA2 = LBAmid, low byte (1f4)
mov al, ch ; LBA3
inc edx
out dx, al ; LBA3 = LBAhigh, low byte (1f5)
mov al, byte [esi + dd_sbits] ; master/slave flag | 0xe0
inc edx
and al, 0x50 ; get rid of extraneous LBA28 bits in drive selector
out dx, al ; drive select (1f6)
inc edx
mov al, 0x24 ; send "read ext" command to drive
out dx, al ; command (1f7)
; ignore the error bit for the first 4 status reads -- ie. implement 400ns delay on ERR only
; wait for BSY clear and DRQ set
mov ecx, 4
.lp1:
in al, dx ; grab a status byte
test al, 0x80 ; BSY flag set?
jne short .retry
test al, 8 ; DRQ set?
jne short .data_rdy
.retry:
dec ecx
jg short .lp1
; need to wait some more -- loop until BSY clears or ERR sets (error exit if ERR sets)
.pior_l:
in al, dx ; grab a status byte
test al, 0x80 ; BSY flag set?
jne short .pior_l ; (all other flags are meaningless if BSY is set)
test al, 0x21 ; ERR or DF set?
jne short .fail
.data_rdy:
; if BSY and ERR are clear then DRQ must be set -- go and read the data
sub dl, 7 ; read from data port (ie. 0x1f0)
mov cx, 256
rep insw ; gulp one 512b sector into edi
or dl, 7 ; "point" dx back at the status register
in al, dx ; delay 400ns to allow drive to set new values of BSY and DRQ
in al, dx
in al, dx
in al, dx
; After each DRQ data block it is mandatory to either:
; receive and ack the IRQ -- or poll the status port all over again
inc ebp ; increment the current absolute LBA (overflowing is OK!)
dec ebx ; decrement the "sectors to read" count
test bx, bx ; check if "sectorcount" just decremented to 0
jne short .pior_l
sub dx, 7 ; "point" dx back at the base IO port, so it's unchanged
sub ebp, [esi + dd_stLBA] ; convert absolute lba back to relative
; this sub handles the >32bit overflow cases correcty, too
; "test" sets the zero flag for a "success" return -- also clears the carry flag
test al, 0x21 ; test the last status ERR bits
je short .done
.fail:
stc
.done:
ret
; do a singletasking PIO ata "software reset" with DCR in dx
srst_ata_st:
push eax
mov al, 4
out dx, al ; do a "software reset" on the bus
xor eax, eax
out dx, al ; reset the bus to normal operation
in al, dx ; it might take 4 tries for status bits to reset
in al, dx ; ie. do a 400ns delay
in al, dx
in al, dx
.rdylp:
in al, dx
and al, 0xc0 ; check BSY and RDY
cmp al, 0x40 ; want BSY clear and RDY set
jne short .rdylp
pop eax
ret

Loading…
Cancel
Save