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)

AHCI (Advanced Host Controller Interface)

Umožňuje připojit až 32 zařízení (port 0-31)


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ý.

AdresaVelikostVýznam
abar + 0256 (100h)HbaMem
abar + 100h128 (80h)HbaPort - port 0
abar + 180h128 (80h)HbaPort - port 1
.........
abar + 1080h128 (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:

AdresaVelikostVýznam
clb + 032 (20h)Command Header - slot 0
clb + 20h32 (20h)Command Header - slot 1
.........
clb + 3E0h32 (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