ATA read/write sectors
Since interrupts (like INT 13h) can't be called easily in protected mode or long mode, direct disk access through ports might be the only solution. The below code is example of subroutines to read and write disk sectors directly from the first hard disk (80h) in long mode using CHS and LBA.
ATA read sectors
Read in CHS mode
Accessing disk using CHS (cylinder,head,sector) indexes seem to be kinda old type but is the base for LBA access. The following NASM long-mode subroutine reads CH sectors to memory address indicated by RDI register.
;=============================================================================
; ATA read sectors (CHS mode)
; Max head index is 15, giving 16 possible heads
; Max cylinder index can be a very large number (up to 65535)
; Sector is usually always 1-63, sector 0 reserved, max 255 sectors/track
; If using 63 sectors/track, max disk size = 31.5GB
; If using 255 sectors/track, max disk size = 127.5GB
; See OSDev forum links in bottom of [http://wiki.osdev.org/ATA]
;
; @param EBX The CHS values; 2 bytes, 1 byte (BH), 1 byte (BL) accordingly
; @param CH The number of sectors to read
; @param RDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
ata_chs_read: pushfq
push rax
push rbx
push rcx
push rdx
push rdi
mov rdx,1f6h ;port to send drive & head numbers
mov al,bh ;head index in BH
and al,00001111b ;head is only 4 bits long
or al,10100000b ;default 1010b in high nibble
out dx,al
mov rdx,1f2h ;Sector count port
mov al,ch ;Read CH sectors
out dx,al
mov rdx,1f3h ;Sector number port
mov al,bl ;BL is sector index
out dx,al
mov rdx,1f4h ;Cylinder low port
mov eax,ebx ;byte 2 in ebx, just above BH
mov cl,16
shr eax,cl ;shift down to AL
out dx,al
mov rdx,1f5h ;Cylinder high port
mov eax,ebx ;byte 3 in ebx, just above byte 2
mov cl,24
shr eax,cl ;shift down to AL
out dx,al
mov rdx,1f7h ;Command port
mov al,20h ;Read with retry.
out dx,al
.still_going: in al,dx
test al,8 ;the sector buffer requires servicing.
jz .still_going ;until the sector buffer is ready.
mov rax,512/2 ;to read 256 words = 1 sector
xor bx,bx
mov bl,ch ;read CH sectors
mul bx
mov rcx,rax ;RCX is counter for INSW
mov rdx,1f0h ;Data port, in and out
rep insw ;in to [RDI]
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret
Read in LBA mode
This page or section is a stub. You can help the wiki by accurately contributing to it. |
;=============================================================================
; ATA read sectors (LBA mode)
;
; @param EAX Logical Block Address of sector
; @param CL Number of sectors to read
; @param RDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
ata_lba_read:
pushfq
and rax, 0x0FFFFFFF
push rax
push rbx
push rcx
push rdx
push rdi
mov rbx, rax ; Save LBA in RBX
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
shr eax, 24 ; Get bit 24 - 27 in al
or al, 11100000b ; Set bit 6 in al for LBA mode
out dx, al
mov edx, 0x01F2 ; Port to send number of sectors
mov al, cl ; Get number of sectors from CL
out dx, al
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
mov eax, ebx ; Get LBA from EBX
out dx, al
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 8 ; Get bit 8 - 15 in AL
out dx, al
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 16 ; Get bit 16 - 23 in AL
out dx, al
mov edx, 0x1F7 ; Command port
mov al, 0x20 ; Read with retry.
out dx, al
.still_going: in al, dx
test al, 8 ; the sector buffer requires servicing.
jz .still_going ; until the sector buffer is ready.
mov rax, 256 ; to read 256 words = 1 sector
xor bx, bx
mov bl, cl ; read CL sectors
mul bx
mov rcx, rax ; RCX is counter for INSW
mov rdx, 0x1F0 ; Data port, in and out
rep insw ; in to [RDI]
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret
ATA write sectors
A write is mostly equivalent to performing a read operation. The only changes needed are a change in command (0x30 for chs write), and the direction of the data, which is written (rep outsw from *SI) to the data port rather than read (rep insw to *DI).
;=============================================================================
; ATA write sectors (LBA mode)
;
; @param EAX Logical Block Address of sector
; @param CL Number of sectors to write
; @param RDI The address of data to write to the disk
;
; @return None
;=============================================================================
ata_lba_write:
pushfq
and rax, 0x0FFFFFFF
push rax
push rbx
push rcx
push rdx
push rdi
mov rbx, rax ; Save LBA in RBX
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
shr eax, 24 ; Get bit 24 - 27 in al
or al, 11100000b ; Set bit 6 in al for LBA mode
out dx, al
mov edx, 0x01F2 ; Port to send number of sectors
mov al, cl ; Get number of sectors from CL
out dx, al
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
mov eax, ebx ; Get LBA from EBX
out dx, al
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 8 ; Get bit 8 - 15 in AL
out dx, al
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
mov eax, ebx ; Get LBA from EBX
shr eax, 16 ; Get bit 16 - 23 in AL
out dx, al
mov edx, 0x1F7 ; Command port
mov al, 0x30 ; Write with retry.
out dx, al
.still_going: in al, dx
test al, 8 ; the sector buffer requires servicing.
jz .still_going ; until the sector buffer is ready.
mov rax, 256 ; to read 256 words = 1 sector
xor bx, bx
mov bl, cl ; write CL sectors
mul bx
mov rcx, rax ; RCX is counter for OUTSW
mov rdx, 0x1F0 ; Data port, in and out
mov rsi, rdi
rep outsw ; out
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret