Obsah
AHCI (Advanced Host Controller Interface)
Zpúsoby připojení pevnejch diskú
IDE (Integrated Drive Electronics)
Umožňuje připojit až 4 zařízení (primary master, primary slave,
secondary master, secondary slave)
- pevnej disk (ATA)
- cd/dvd (ATAPI - ATA Packet Interface)
AHCI (Advanced Host Controller Interface)
Umožňuje připojit až 32 zařízení (port 0-31)
- pevnej disk (SATA - Serial ATA)
- cd/dvd (SATAPI - SATA Packet Interface)
Příprava
Pomocí PCI najděte zařízení, kerý má class 1 a subclass 6. abar = BAR5
Je potřeba alokovat paměť pro fb a clb všech portú.
fb musí bejt na fyzický adrese dělitelný 256, clb 1024.
Struktura
Všechny adresy sou fyzický.
Adresa | Velikost | Význam |
abar + 0 | 256 (100h) | HbaMem |
abar + 100h | 128 (80h) | HbaPort - port 0 |
abar + 180h | 128 (80h) | HbaPort - port 1 |
... | ... | ... |
abar + 1080h | 128 (80h) | HbaPort - port 31 |
struct HbaMem{
; 0x00 - 0x2B, Generic Host Control
uns4 cap ; 0x00, Host capability
uns4 ghc ; 0x04, Global host control
uns4 is ; 0x08, Interrupt status
uns4 pi ; 0x0C, Port implemented
uns4 vs ; 0x10, Version
uns4 ccc_ctl ; 0x14, Command completion coalescing control
uns4 ccc_pts ; 0x18, Command completion coalescing ports
uns4 em_loc ; 0x1C, Enclosure management location
uns4 em_ctl ; 0x20, Enclosure management control
uns4 cap2 ; 0x24, Host capabilities extended
uns4 bohc ; 0x28, BIOS/OS handoff control and status
; 0x2C - 0x9F, Reserved
uns1 rsv[0A0h-2Ch];
; 0xA0 - 0xFF, Vendor specific registers
uns1 vendor[100h-0A0h]
}
struct HbaPort{
uns4 clb
; 0x00, command list base address, 1KiB aligned
uns4 clbu
; 0x04, command list base address upper 32 bits
uns4 fb
; 0x08, FIS base address, 256-byte aligned
uns4 fbu
; 0x0C, FIS base address upper 32 bits
uns4 is
; 0x10, interrupt status
uns4 ie
; 0x14, interrupt enable
uns4 cmd
; 0x18, command and status
uns4 rsv0 ; 0x1C, Reserved
uns4 tfd ; 0x20, task file data
uns4 sig ; 0x24, signature
uns4 ssts
; 0x28, SATA status (SCR0:SStatus)
uns4 sctl
; 0x2C, SATA control (SCR2:SControl)
uns4 serr
; 0x30, SATA error (SCR1:SError)
uns4 sact
; 0x34, SATA active (SCR3:SActive)
uns4 ci
; 0x38, command issue
uns4 sntf
; 0x3C, SATA notification (SCR4:SNotification)
uns4 fbs
; 0x40, FIS-based switch control
uns4 rsv1[11] ; 0x44..0x6F, Reserved
uns4 vendor[4] ; 0x70..0x7F, vendor specific
}
Na fyzický adrese fb:
struct FisRegH2D{
; DWORD 0
uns1 fis_type ; FIS_TYPE_REG_H2D = 0x27
uns1 pmportc ; 0-4: Port multiplier, 80h: Command / 0: Control
uns1 command ; Command register
uns1 featurel ; Feature register, 7:0
; DWORD 1
uns1 lba0 ; LBA low register, 7:0
uns1 lba1 ; LBA mid register, 15:8
uns1 lba2 ; LBA high register, 23:16
uns1 device ; Device register
; DWORD 2
uns1 lba3 ; LBA register, 31:24
uns1 lba4 ; LBA register, 39:32
uns1 lba5 ; LBA register, 47:40
uns1 featureh ; Feature register, 15:8
// DWORD 3
uns1 countl ; Count register, 7:0
uns1 counth ; Count register, 15:8
uns1 icc ; Isochronous command completion
uns1 control ; Control register
// DWORD 4
uns4 rsv ; Reserved
}
Na fyzický adrese clb:
Adresa | Velikost | Význam |
clb + 0 | 32 (20h) | Command Header - slot 0 |
clb + 20h | 32 (20h) | Command Header - slot 1 |
... | ... | ... |
clb + 3E0h | 32 (20h) | Command Header - slot 31 |
Command Header:
struct HbaCmdHeader{
; DWORD 0
uns1 cflawp
; bit 0-4: Command FIS length in DWORDS (sizeof FisRegH2D / 4)
; bit 5: ATAPI
; bit 6: Write
; bit 7: Prefetchable
uns1 rbcpmp
; bit 0: reset
; bit 1: BIST
; bit 2: Clear busy upon R_OK
; bit 4-7: port multiplier
uns2 prdtl ; Physical region descriptor table length in entries
; DWORD 1
uns4 prdbc ; Physical region descriptor byte count transferred
; DWORD 2, 3
uns4 ctba ; Command table descriptor base address
uns4 ctbau ; Command table descriptor base address upper 32 bits
; DWORD 4 - 7
uns4 rsv[4] ; Reserved
}
Command Table Descriptor:
struct HbaCmdTbl{
uns1 cfis[64] ; 0x00, Command FIS
uns1 acmd[16] ; 0x40, ATAPI command, 12 or 16 bytes
uns1 rsv[48] ; 0x50, Reserved
; 0x80, HbaPrdtEntry prdt_entry[prdtl] (Physical region descriptor
table entries, 0..65535)
}
PrdtEntry:
struct HbaPrdtEntry{
uns4 dba ; Data base address
uns4 dbau ; Data base address upper 32 bits
uns4 rsv ; Reserved
uns4 dbci
; bit 0 - 21: Byte count - 1, 4M max, always odd
; bit 22-30: reserved
; bit 31: Interrupt on completion
}
Příkazy (command)
ATA_CMD_READ = 0x20
ATA_CMD_WRITE = 0x30
ATA_CMD_READ_EX = 0x24
ATA_CMD_WRITE_EX = 0x34
ATA_CMD_FLUSH = 0xE7
ATA_CMD_FLUSH_EX = 0xEA
ATA_CMD_IDENTIFY = 0xEC
ATA_CMD_READ_DMA = 0xC8
ATA_CMD_READ_DMA_EX = 0x25
ATA_CMD_WRITE_DMA = 0xCA
ATA_CMD_WRITE_DMA_EX = 0x35
ATA_CMD_PACKET = 0xA0
ATA_CMD_IDENTIFYP = 0xA1
Čtení
uns4 hdt_count_remain
uns8 sd_sect
// IN: hdt_pdiskn, edx:ebx = sector, ecx = count, es:edi -> buffer
// OUT: al = erc
// USES: hdt_count_remain, sd_sect, sd_port_n, sd_port_ebx
h_sd_p_read:
mov dword ptr sd_sect[0], ebx
mov dword ptr sd_sect[4], edx
mov esi, edi
l_h_sd_p_read_1:
xor eax, eax
cmp ecx, 8
jna l_h_sd_p_read_11
mov eax, ecx
sub eax, 8
mov ecx, 8
l_h_sd_p_read_11:
mov hdt_count_remain, eax
mov al, hdt_pdiskn
and al, 31
movzx eax, al
mov sd_port_n, eax
shl eax, 7 // * 80h
add eax, abar
add eax, 100h
mov sd_port_ebx, eax
mov ebx, eax
add ebx, offset HbaPortRec.is
mov eax, -1
push ecx
call m_phys_set // destroys ecx
pop ecx
mov eax, sd_port_n
shl eax, 10 // * 1024
add eax, 256 * 32 + VA_SD
mov edi, eax // es:edi -> cmdheader
mov al, sizeof FisRegH2D / 4
//or al, 20h // ATAPI
//or al, 40h // write
or al, 80h // Prefetch
mov [es:edi + offset HbaCmdHeader.cflawp], al
mov word ptr [es:edi + offset HbaCmdHeader.prdtl], 1 // prdtl - prdt count
mov dword ptr [es:edi + offset HbaCmdHeader.prdbc], 4095 // byte count - 1
mov edi, [es:edi + offset HbaCmdHeader.ctba]
sub edi, sd_base
add edi, VA_SD
mov al, FIS_TYPE_REG_H2D
mov [es:edi + offset FisRegH2D.fis_type], al
mov al, 80h // command
mov [es:edi + offset FisRegH2D.pmportc], al
mov al, ATA_CMD_READ_DMA_EX
mov [es:edi + offset FisRegH2D.command], al
mov al, byte ptr sd_sect[0]
mov [es:edi + offset FisRegH2D.lba0], al
mov al, byte ptr sd_sect[1]
mov [es:edi + offset FisRegH2D.lba1], al
mov al, byte ptr sd_sect[2]
mov [es:edi + offset FisRegH2D.lba2], al
mov al, 1 << 6 // LBA mode
mov [es:edi + offset FisRegH2D.device], al
mov al, byte ptr sd_sect[3]
mov [es:edi + offset FisRegH2D.lba3], al
mov al, byte ptr sd_sect[4]
mov [es:edi + offset FisRegH2D.lba4], al
mov al, byte ptr sd_sect[5]
mov [es:edi + offset FisRegH2D.lba5], al
mov [es:edi + offset FisRegH2D.countl], cx // sector count
add edi, 80h // es:edi -> PRDT
mov eax, sd_base
add eax, RA_SD_BUFFER
mov [es:edi + offset HbaPrdtEntry.dba], eax
mov eax, ecx
shl eax, 9
dec eax
//or eax, 1 << 31 // int on completion
mov [es:edi + offset HbaPrdtEntry.dbci], eax
// wait not busy
l_h_sd_p_read_65:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.tfd
call m_phys_get
test eax, ATA_DEV_BUSY | ATA_DEV_DRQ
jnz l_h_sd_p_read_65
// issue command
mov eax, 1 // 1 << slot
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
push ecx
call m_phys_set // destroys ecx
pop ecx
// wait for completion
l_h_sd_p_read_75:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
call m_phys_get
test eax, 1 // 1 << slot
jz l_h_sd_p_read_78
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.is
call m_phys_get
test eax, HBA_PxIS_TFES
jz l_h_sd_p_read_75
// error
mov al, ERC_DISK_IO
jmp short l_h_sd_p_read_9
l_h_sd_p_read_78:
// copy
shl ecx, 9 - 2 // 512 / 4
mov edi, esi
mov esi, RA_SD_BUFFER + VA_SD
push ds
push es
pop ds
cld
rep movsd
pop ds
mov esi, edi
// remaining sectors
mov ecx, hdt_count_remain
or ecx, ecx
jnz near l_h_sd_p_read_1
mov al, 0
l_h_sd_p_read_9:
ret
Zápis
// IN: hdt_pdiskn, edx:ebx = sector, ecx = count, es:edi -> buffer
// OUT: al = erc
// USES: hdt_count_remain, sd_sect, sd_port_n, sd_port_ebx
h_sd_p_write:
mov dword ptr sd_sect[0], ebx
mov dword ptr sd_sect[4], edx
mov esi, edi
l_h_sd_p_write_1:
xor eax, eax
cmp ecx, 8
jna l_h_sd_p_write_11
mov eax, ecx
sub eax, 8
mov ecx, 8
l_h_sd_p_write_11:
mov hdt_count_remain, eax
// copy
push ecx
shl ecx, 9 - 2 // 512 / 4
mov edi, RA_SD_BUFFER + VA_SD
push ds
push es
pop ds
cld
rep movsd
pop ds
pop ecx
// prepare IO
mov al, hdt_pdiskn
and al, 31
movzx eax, al
mov sd_port_n, eax
shl eax, 7 // * 80h
add eax, abar
add eax, 100h
mov sd_port_ebx, eax
mov ebx, eax
add ebx, offset HbaPortRec.is
mov eax, -1
push ecx
call m_phys_set // destroys ecx
pop ecx
mov eax, sd_port_n
shl eax, 10 // * 1024
add eax, 256 * 32 + VA_SD
mov edi, eax // es:edi -> cmdheader
mov al, sizeof FisRegH2D / 4
//or al, 20h // ATAPI
or al, 40h // write
//or al, 80h // Prefetch
mov [es:edi + offset HbaCmdHeader.cflawp], al
mov word ptr [es:edi + offset HbaCmdHeader.prdtl], 1 // prdtl - prdt count
mov dword ptr [es:edi + offset HbaCmdHeader.prdbc], 4095 // byte count - 1
mov edi, [es:edi + offset HbaCmdHeader.ctba]
sub edi, sd_base
add edi, VA_SD
mov al, FIS_TYPE_REG_H2D
mov [es:edi + offset FisRegH2D.fis_type], al
mov al, 80h // command
mov [es:edi + offset FisRegH2D.pmportc], al
mov al, ATA_CMD_WRITE_DMA_EX
mov [es:edi + offset FisRegH2D.command], al
mov al, byte ptr sd_sect[0]
mov [es:edi + offset FisRegH2D.lba0], al
mov al, byte ptr sd_sect[1]
mov [es:edi + offset FisRegH2D.lba1], al
mov al, byte ptr sd_sect[2]
mov [es:edi + offset FisRegH2D.lba2], al
mov al, 1 << 6 // LBA mode
mov [es:edi + offset FisRegH2D.device], al
mov al, byte ptr sd_sect[3]
mov [es:edi + offset FisRegH2D.lba3], al
mov al, byte ptr sd_sect[4]
mov [es:edi + offset FisRegH2D.lba4], al
mov al, byte ptr sd_sect[5]
mov [es:edi + offset FisRegH2D.lba5], al
mov [es:edi + offset FisRegH2D.countl], cx // sector count
add edi, 80h // es:edi -> PRDT
mov eax, sd_base
add eax, RA_SD_BUFFER
mov [es:edi + offset HbaPrdtEntry.dba], eax
mov eax, ecx
shl eax, 9
dec eax
//or eax, 1 << 31 // int on completion
mov [es:edi + offset HbaPrdtEntry.dbci], eax
// wait not busy
l_h_sd_p_write_65:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.tfd
call m_phys_get
test eax, ATA_DEV_BUSY | ATA_DEV_DRQ
jnz l_h_sd_p_write_65
// issue command
mov eax, 1 // 1 << slot
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
push ecx
call m_phys_set // destroys ecx
pop ecx
// wait for completion
l_h_sd_p_write_75:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
call m_phys_get
test eax, 1 // 1 << slot
jz l_h_sd_p_write_78
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.is
call m_phys_get
test eax, HBA_PxIS_TFES
jz l_h_sd_p_write_75
// error
mov al, ERC_DISK_IO
jmp short l_h_sd_p_write_9
l_h_sd_p_write_78:
// remaining sectors
mov ecx, hdt_count_remain
or ecx, ecx
jnz near l_h_sd_p_write_1
mov al, 0
l_h_sd_p_write_9:
ret
Čtení z CD/DVD
SCD_BUFFER_SIZE = 800h
uns1 cd_packet[12]
uns1 cd_buffer[2048]
// IN: hdt_pdiskn, cd_packet, sd_base = physical adr, VA_SD = virtual adr
// USES: sd_port_n, sd_port_ebx
h_sd_p_cd_read:
mov al, hdt_pdiskn
and al, 31
movzx eax, al
mov sd_port_n, eax
shl eax, 7 // * 80h
add eax, abar
add eax, 100h
mov sd_port_ebx, eax
mov ebx, eax
add ebx, offset HbaPortRec.is
mov eax, -1
call m_phys_set // destroys ecx
mov eax, sd_port_n
shl eax, 10 // * 1024
add eax, 256 * 32 + VA_SD
mov edi, eax // es:edi -> cmdheader
mov al, sizeof FisRegH2D / 4
or al, 20h // ATAPI
//or al, 40h // write
or al, 80h // Prefetch
mov [es:edi + offset HbaCmdHeader.cflawp], al
mov word ptr [es:edi + offset HbaCmdHeader.prdtl], 1 // prdtl - prdt count
mov dword ptr [es:edi + offset HbaCmdHeader.prdbc], SCD_BUFFER_SIZE - 1 // byte count - 1
mov edi, [es:edi + offset HbaCmdHeader.ctba]
sub edi, sd_base
add edi, VA_SD
mov al, FIS_TYPE_REG_H2D
mov [es:edi + offset FisRegH2D.fis_type], al
mov al, 80h // command
mov [es:edi + offset FisRegH2D.pmportc], al
mov al, ATA_CMD_PACKET
mov [es:edi + offset FisRegH2D.command], al
add edi, 40h // es:edi -> acmd
mov eax, dword ptr cd_packet[0]
mov [es:edi + 0], eax
mov eax, dword ptr cd_packet[4]
mov [es:edi + 4], eax
mov eax, dword ptr cd_packet[8]
mov [es:edi + 8], eax
add edi, 40h // es:edi -> PRDT
mov eax, offset cd_buffer // physical adr
mov [es:edi + offset HbaPrdtEntry.dba], eax
mov eax, SCD_BUFFER_SIZE - 1
//or eax, 1 << 31 // int on completion
mov [es:edi + offset HbaPrdtEntry.dbci], eax
// wait not busy
l_h_sd_p_cd_read_65:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.tfd
call m_phys_get
test eax, ATA_DEV_BUSY | ATA_DEV_DRQ
jnz l_h_sd_p_cd_read_65
// issue command
mov eax, 1 // 1 << slot
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
call m_phys_set // destroys ecx
// wait for completion
l_h_sd_p_cd_read_75:
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.ci
call m_phys_get
test eax, 1 // 1 << slot
jz l_h_sd_p_cd_read_78
mov ebx, sd_port_ebx
add ebx, offset HbaPortRec.is
call m_phys_get
test eax, HBA_PxIS_TFES
jz l_h_sd_p_cd_read_75
// error
mov al, ERC_DISK_IO
jmp short l_h_sd_p_cd_read_9
l_h_sd_p_cd_read_78:
mov al, 0
l_h_sd_p_cd_read_9:
ret