ahci reading

This commit is contained in:
Aaron Hance 2025-03-20 22:12:42 +00:00
parent 05eab23956
commit babe4290e4
4 changed files with 424 additions and 32 deletions

View File

@ -463,9 +463,9 @@ begin
cmd := cmd or PCI_COMMAND_BUS_MASTER; cmd := cmd or PCI_COMMAND_BUS_MASTER;
cmd := cmd or (3 shl 1); cmd := cmd or (3 shl 1);
//enable interrupts, remove disable interrupt bit //enable interrupts, remove disable interrupt bit maybe
cmd := cmd and not PCI_COMMAND_INT_DISABLE; cmd := cmd and not PCI_COMMAND_INT_DISABLE;
cmd := cmd and not (1 shl 9); // cmd := cmd and not (1 shl 9);
outl(PCI_CONFIG_ADDRESS_PORT, addr); outl(PCI_CONFIG_ADDRESS_PORT, addr);
outl(PCI_CONFIG_DATA_PORT, cmd); outl(PCI_CONFIG_DATA_PORT, cmd);

View File

@ -38,6 +38,12 @@ const
PCI_COMMAND_INT_DISABLE = $0400; PCI_COMMAND_INT_DISABLE = $0400;
PCI_COMMAND_SERR_ENABLE = $8000; PCI_COMMAND_SERR_ENABLE = $8000;
PCI_CAP_ID_MSI = $05;
// Bits in the MSI Control register (16-bit):
MSI_CONTROL_ENABLE = 1 shl 0; // Bit 0
MSI_CONTROL_64BIT = 1 shl 7; // Bit 7
MSI_CONTROL_PVMASK = 1 shl 8; // Bit 8 (optional: per-vector masking)
type type
PPCI_Device = ^TPCI_Device; PPCI_Device = ^TPCI_Device;

View File

@ -42,8 +42,13 @@ var
procedure init(); procedure init();
function load(ptr : void) : boolean; function load(ptr : void) : boolean;
procedure check_ports(controller : PAHCI_Controller); procedure check_ports(controller : PAHCI_Controller);
procedure identify_device(controller : PAHCI_Controller; portIndex : uint32); procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean);
procedure ahci_isr(); procedure ahci_isr();
function find_cmd_slot(device : PAHCI_Device) : uint32;
function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
// function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
implementation implementation
@ -142,7 +147,7 @@ begin
console.writestring(' implemented on controller '); console.writestring(' implemented on controller ');
console.writehexln(uint32(controller^.pci_device^.address5)); console.writehexln(uint32(controller^.pci_device^.address5));
device := PAHCI_Device(DL_Add(controller^.devices)); device := PAHCI_Device(@controller^.devices[i]);
device^.port := port; device^.port := port;
//check if the port is active TODO //check if the port is active TODO
@ -230,9 +235,13 @@ begin
console.writestring('AHCI: sata status: '); console.writestring('AHCI: sata status: ');
console.writehexln(port^.sata_status); console.writehexln(port^.sata_status);
//pass devices count as second param // //pass devices count as second param
if (device^.device_type = SATA) then begin if (device^.device_type = SATA) then begin
identify_device(controller, DL_Size(controller^.devices) - 1); identify_device(controller, i, false);
end else if (device^.device_type = ATAPI) then begin
identify_device(controller, i, true);
end else begin
console.writestringln('AHCI: Unsupported device type');
end; end;
controller^.mio^.int_status := $FFFFFFFF; controller^.mio^.int_status := $FFFFFFFF;
@ -242,7 +251,7 @@ begin
end; end;
end; end;
procedure identify_device(controller : PAHCI_Controller; portIndex : uint32); procedure identify_device(controller : PAHCI_Controller; portIndex : uint32; isATAPI : boolean);
var var
fis : PHBA_FIS_REG_H2D; fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER; cmd_header : PHBA_CMD_HEADER;
@ -255,18 +264,23 @@ var
sec_count : uint32; sec_count : uint32;
b8 : puint16; b8 : puint16;
begin begin
console.writestringln('AHCI: Identifying device'); console.writestring('AHCI: Identifying device ');
device := PAHCI_Device(DL_Get(controller^.devices, portIndex)); console.writeintln(portIndex);
device := PAHCI_Device(@controller^.devices[portIndex]);
// (Optional) Print the device type if you store it: // (Optional) Print the device type if you store it:
console.writestring('AHCI: Device type: '); console.writestring('AHCI: Device type: ');
// For example: if device^.device_type = SATA then ... // For example: if device^.device_type = SATA then ...
console.writestring('AHCI: sata status: '); console.writestring('AHCI: sata status: ');
console.writehexln(device^.port^.sata_status); console.writehexln(device^.port^.sata_status);
console.writestring('AHCI: signature: ');
console.writehexln(device^.port^.signature);
console.writestring('AHCI: device type: ');
console.writeintln(uint32(device^.device_type));
//clear any pending interrupts //clear any pending interrupts
device^.port^.int_status := $FFFFFFFF; device^.port^.int_status := $0;
device^.port^.int_enable := 0; device^.port^.int_enable := $FFFFFFFF;
// Allocate a 512-byte DMA buffer for the IDENTIFY data // Allocate a 512-byte DMA buffer for the IDENTIFY data
buffer := kalloc(512); buffer := kalloc(512);
@ -279,6 +293,9 @@ begin
cmd_header^.wrt := 0; cmd_header^.wrt := 0;
cmd_header^.prdtl := 1; cmd_header^.prdtl := 1;
cmd_header^.clear_busy := 1; cmd_header^.clear_busy := 1;
if isATAPI then begin
cmd_header^.atapi := 1;
end;
// Setup the command table (using slot 0) // Setup the command table (using slot 0)
@ -287,18 +304,27 @@ begin
cmd_table^.prdt[0].dbc := 511; // 512 bytes (0-based count) cmd_table^.prdt[0].dbc := 511; // 512 bytes (0-based count)
cmd_table^.prdt[0].int := 1; // Interrupt on completion cmd_table^.prdt[0].int := 1; // Interrupt on completion
if isATAPI then begin
// ATAPI Identify Device command
buffer[0] := $A1; // ATAPI Identify Device
end;
// Construct the Command FIS in the command table's CFIS area // Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@device^.command_table^.cmd_fis); fis := PHBA_FIS_REG_H2D(@device^.command_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D); fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1; fis^.c := $1;
if isATAPI then begin
fis^.command := ATA_CMD_IDENTIFY_PACKET;
end else begin
fis^.command := ATA_CMD_IDENTIFY; fis^.command := ATA_CMD_IDENTIFY;
end;
fis^.device := $A0; // LBA mode 40? fis^.device := $A0; // LBA mode 40?
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin while (device^.port^.tfd and $88) <> 0 do begin
console.writestring('AHCI: tfd: '); // console.writestring('AHCI: tfd: ');
console.writehexln(device^.port^.tfd); // console.writehexln(device^.port^.tfd);
end; end;
// Issue the command by setting bit 0 in the port's Command Issue register. // Issue the command by setting bit 0 in the port's Command Issue register.
@ -356,6 +382,25 @@ begin
console.writestringln(''); console.writestringln('');
buffer := puint32(kalloc(512));
memset(uint32(buffer), 0, 512);
//send a read DMA command to the device
if isATAPI then begin
read_atapi(device, 16, 512, buffer);
end else begin
send_read_dma(device, 10, 512, buffer);
end;
//print the buffer
for i:=0 to 63 do begin
console.writehex(uint32(buffer[i]));
console.writestring(' ');
end;
console.writestringln('');
end; end;
@ -382,7 +427,8 @@ begin
device := PPCI_Device(ptr); device := PPCI_Device(ptr);
pci.enableDevice(device^.bus, device^.slot, device^.func); pci.enableDevice(device^.bus, device^.slot, device^.func);
int_no := device^.interrupt_line; psleep(5);
int_no := device^.interrupt_line + 32;
//print interrupt number //print interrupt number
@ -390,7 +436,6 @@ begin
console.writehexln(int_no); console.writehexln(int_no);
controller := PAHCI_Controller(DL_Add(ahciControllers)); controller := PAHCI_Controller(DL_Add(ahciControllers));
controller^.devices := DL_New(SizeOf(TAHCI_Device));
controller^.pci_device := PPCI_Device(device); controller^.pci_device := PPCI_Device(device);
@ -439,40 +484,381 @@ begin
while (base^.global_ctrl and 1) <> 0 do begin while (base^.global_ctrl and 1) <> 0 do begin
end; end;
registerISR(int_no, @ahci_isr); registerISR(int_no, @ahci_isr);
//enable AHCI mode, TODO check if is not already in AHCI mode and enable it, also perhaps remove if loaded as IDE //enable AHCI mode, TODO check if is not already in AHCI mode and enable it, also perhaps remove if loaded as IDE
base^.global_ctrl := base^.global_ctrl or AHCI_CONTROLLER_MODE; base^.global_ctrl := base^.global_ctrl or AHCI_CONTROLLER_MODE;
base^.global_ctrl := base^.global_ctrl or $2; //enable interrupts // base^.global_ctrl := base^.global_ctrl or $2; //enable interrupts
//disable interrupts
base^.global_ctrl := base^.global_ctrl or $2;
//clear any pending interrupts //clear any pending interrupts
base^.int_status := $FFFFFFFF; base^.int_status := $FFFFFFFF;
check_ports(controller); check_ports(controller);
// console.writestringln('AHCI: initilizing a new controller');
// ahciControllers[ahciControllerCount] := ptr;
// hba[ahciControllerCount] := PPCI_Device(ptr)^.address5;
// new_page_at_address(hba[ahciControllerCount]);
// //here would be any controller setup needed
// check_ports(ahciControllerCount);
// ahciControllerCount += 1;
// exit(true);
end; end;
procedure ahci_isr(); procedure ahci_isr();
begin begin
console.writestringln('AHCI: ISR'); console.writestringln('AHCI: ISR');
console.redrawWindows();
//welp there is no way to know what port caused the interrupt or even if it was the controller //welp there is no way to know what port caused the interrupt or even if it was the controller
//so we will just have to check all ports, and figure the operation that caused the interrupt //so we will just have to check all ports, and figure the operation that caused the interrupt
end; end;
function find_cmd_slot(device : PAHCI_Device) : uint32;
var
i : uint32;
begin
// for i:=0 to 31 do begin
// if (device^.port^.cmd_issue[i] and $80000000) = 0 then begin
// exit(i);
// end;
// end;
exit(0);
end;
function send_read_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending read DMA command');
reset_port(device^.port);
start_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
send_read_dma := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 0;
cmd_header^.prdtl := prdt_count;
cmd_header^.clear_busy := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
for i:=0 to prdt_count - 1 do begin
cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304));
if i = prdt_count - 1 then begin
cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1; // 512 bytes (0-based count)
end else begin
cmd_table^.prdt[i].dbc := 4194304 - 1; // 512 bytes (0-based count)
end;
cmd_table^.prdt[i].int := 1; // Interrupt on completion
end;
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_READ_DMA_EXT;
fis^.device := $40; // LBA mode 48?
fis^.lba0 := lba and $FF;
fis^.lba1 := (lba shr 8) and $FF;
fis^.lba2 := (lba shr 16) and $FF;
fis^.lba3 := (lba shr 24) and $FF;
fis^.lba4 := (lba shr 32) and $FF;
fis^.countl := count and $FF;
fis^.counth := (count shr 8) and $FF;
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent read DMA command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Read DMA command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending read DMA command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
send_read_dma := true;
end;
function send_write_dma(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending write DMA command');
reset_port(device^.port);
start_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
send_write_dma := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 1;
cmd_header^.prdtl := prdt_count;
cmd_header^.clear_busy := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
for i:=0 to prdt_count - 1 do begin
cmd_table^.prdt[i].dba := vtop(uint32(buffer) + (i * 4194304));
if i = prdt_count - 1 then begin
cmd_table^.prdt[i].dbc := count - (i * 4194304) - 1; // 512 bytes (0-based count)
end else begin
cmd_table^.prdt[i].dbc := 4194304 - 1; // 512 bytes (0-based count)
end;
cmd_table^.prdt[i].int := 1; // Interrupt on completion
end;
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_WRITE_DMA_EXT;
fis^.device := $40; // LBA mode 48?
fis^.lba0 := lba and $FF;
fis^.lba1 := (
lba shr 8) and $FF;
fis^.lba2 := (
lba shr 16) and $FF;
fis^.lba3 := (
lba shr 24) and $FF;
fis^.lba4 := (
lba shr 32) and $FF;
fis^.countl := count and $FF;
fis^.counth := (
count shr 8) and $FF;
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent write DMA command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Write DMA command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending write DMA command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
send_write_dma := true;
end;
//read atapi
function read_atapi(device : PAHCI_Device; lba : uint64; count : uint32; buffer : puint32) : boolean;
var
fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE;
cmd : uint32;
i, timeout : uint32;
tfd : uint32;
sec_count : uint32;
prdt_count : uint32;
b8 : puint16;
slot : uint32;
begin
console.writestringln('AHCI: Sending read ATAPI command');
reset_port(device^.port);
start_port(device^.port);
//prdt count is 22 bits
prdt_count := (count div 4194304) + 1;
if prdt_count > 32 then begin
read_atapi := false;
exit;
end;
// Use command slot 0 for the IDENTIFY command.
slot := find_cmd_slot(device);
cmd_header := PHBA_CMD_HEADER(uint32(device^.command_list) + (slot * sizeof(THBA_CMD_HEADER)));
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
cmd_header^.wrt := 0;
cmd_header^.prdtl := 1;
cmd_header^.atapi := 1;
// Setup the command table (using slot 0)
cmd_table := PHBA_CMD_TABLE(uint32(device^.command_table) + (slot * sizeof(THBA_CMD_TABLE)));
cmd_table^.prdt[0].dba := vtop(uint32(buffer));
cmd_table^.prdt[0].dbc := 512 - 1; // 512 bytes (0-based count
cmd_table^.prdt[0].int := 1; // Interrupt on completion
// Construct the Command FIS in the command table's CFIS area
fis := PHBA_FIS_REG_H2D(@cmd_table^.cmd_fis);
memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D));
fis^.fis_type := uint8(FIS_TYPE_REG_H2D);
fis^.c := $1;
fis^.command := ATA_CMD_PACKET;
fis^.device := $A0; // LBA mode 48?
fis^.featurel := fis^.featurel or 1; //setting both of these might not work on real hardware
fis^.featurel := fis^.featurel or (1 shl 2);
cmd_table^.acmd[0] := $A8; //ATAPI command
cmd_table^.acmd[1] := $0; //ATAPI command
cmd_table^.acmd[2] := uInt8((lba shr 24) and $FF);
cmd_table^.acmd[3] := uInt8((lba shr 16) and $FF);
cmd_table^.acmd[4] := uInt8((lba shr 8) and $FF);
cmd_table^.acmd[5] := uInt8(lba and $FF);
cmd_table^.acmd[9] := uInt8((1) and $FF);
//waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
while (device^.port^.tfd and $88) <> 0 do begin
// console.writestring('AHCI: tfd: ');
// console.writehexln(device^.port^.tfd);
end;
// Issue the command by setting bit 0 in the port's Command Issue register.
cmd := device^.port^.cmd_issue;
cmd := cmd or (1 shl slot);
device^.port^.cmd_issue := cmd;
console.writestringln('AHCI: Sent read ATAPI command');
// Wait for command completion with a timeout.
timeout := 0;
repeat
cmd := device^.port^.cmd_issue; // Command Issue register
tfd := device^.port^.tfd; // Task File Data often contains error/status info.
timeout := timeout + 10;
if timeout > 100000 then begin
console.writestringln('AHCI: Read ATAPI command timeout');
break;
end;
// check for bit 30 in the tfd register
if (tfd and $40000000) <> 0 then begin
console.writestringln('AHCI: error');
break;
end;
until ((device^.port^.cmd_issue and (1 shl slot)) = 0); // Wait until slot 0 is cleared
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and (1 shl slot)) <> 0 then begin
console.writestringln('AHCI: Error sending read ATAPI command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end;
read_atapi := true;
end;
end. end.

View File

@ -269,7 +269,7 @@ type
pci_device : PPCI_Device; pci_device : PPCI_Device;
mio: PHBA_Memory; mio: PHBA_Memory;
ata_info : TIdentResponse; ata_info : TIdentResponse;
devices : PDList; devices : array[0..31] of TAHCI_Device;
end; end;
PAHCI_Controller = ^TAHCI_Controller; PAHCI_Controller = ^TAHCI_Controller;