starting to work

This commit is contained in:
Aaron Hance 2025-03-19 22:15:32 +00:00
parent f04f8aa411
commit 05eab23956
3 changed files with 237 additions and 83 deletions

View File

@ -461,9 +461,11 @@ begin
cmd := inl(PCI_CONFIG_DATA_PORT); cmd := inl(PCI_CONFIG_DATA_PORT);
cmd := cmd or PCI_COMMAND_MEM_SPACE; cmd := cmd or PCI_COMMAND_MEM_SPACE;
cmd := cmd or PCI_COMMAND_BUS_MASTER; cmd := cmd or PCI_COMMAND_BUS_MASTER;
cmd := cmd or (3 shl 1);
//enable interrupts, remove disable interrupt bit //enable interrupts, remove disable interrupt bit
cmd := cmd and not PCI_COMMAND_INT_DISABLE; cmd := cmd and not PCI_COMMAND_INT_DISABLE;
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

@ -42,7 +42,7 @@ 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; port : uint32); procedure identify_device(controller : PAHCI_Controller; portIndex : uint32);
procedure ahci_isr(); procedure ahci_isr();
implementation implementation
@ -65,18 +65,49 @@ end;
procedure stop_port(port : PHBA_Port); procedure stop_port(port : PHBA_Port);
begin begin
port^.cmd := port^.cmd and not $1; ///maybe also bit 4 port^.cmd := port^.cmd and not $1;
while (port^.cmd and $1) = 1 do begin port^.cmd := port^.cmd and not $100;
while ((port^.cmd and $4000) or (port^.cmd and $8000)) <> 0 do begin
//wait for the port to stop //wait for the port to stop
end; end;
end; end;
procedure start_port(port : PHBA_Port); procedure start_port(port : PHBA_Port);
begin begin
port^.cmd := port^.cmd or $1; ///maybe also bit 4
while (port^.cmd and $1) = 0 do begin while (port^.cmd and $8000) <> 0 do begin
//wait for the port to start //wait for port to not be busy
end; end;
port^.cmd := port^.cmd or $100;
port^.cmd := port^.cmd or $1;
end;
procedure reset_port(port : PHBA_Port);
var
ssts, serr, timeout : uint32;
begin
console.writestringln('AHCI: Performing a full port reset.');
port^.cmd := port^.cmd and not $1;
// Wait until CR (bit 15) is cleared.
timeout := 0;
while (port^.cmd and $8000) <> 0 do begin
timeout := timeout + 1;
if timeout > 1000000 then begin
console.writestringln('AHCI: Port reset timeout.');
break;
end;
end;
port^.sata_ctrl := port^.sata_ctrl or $FFFF0000;
while (port^.sata_status and $F) <> 3 do begin
//wait for the port to be ready
end;
// Clear the SERR register by writing 1s to it.
port^.sata_error := $FFFFFFFF;
end; end;
{ {
@ -98,13 +129,22 @@ begin
console.writehexln(controller^.mio^.ports_implimented); console.writehexln(controller^.mio^.ports_implimented);
for i:=0 to 31 do begin for i:=0 to 31 do begin
if (controller^.mio^.ports_implimented shr i) = 1 then begin port := @controller^.mio^.ports[i];
console.writehex(port^.signature);
console.writestring(' ');
end;
for i:=0 to 31 do begin
if ((controller^.mio^.ports_implimented shr i) and 1) = 1 then begin
port := @controller^.mio^.ports[i]; port := @controller^.mio^.ports[i];
console.writestring('AHCI: Port '); console.writestring('AHCI: Port ');
console.writeint(i); console.writeint(i);
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^.port := port;
//check if the port is active TODO //check if the port is active TODO
// if ((port^.sata_status shr 8) <> 1) and ((port^.sata_status and $0F) <> 3) then begin // if ((port^.sata_status shr 8) <> 1) and ((port^.sata_status and $0F) <> 3) then begin
// continue; wrong // continue; wrong
@ -135,26 +175,28 @@ begin
//NEEED TO STOP the port before doing anything //NEEED TO STOP the port before doing anything
stop_port(port); stop_port(port);
console.writestringln('AHCI: Port stopped');
device := PAHCI_Device(DL_Add(controller^.devices));
device^.port := port;
//allocate memory for the command list and ensure it is aligned to 1024 bytes //allocate memory for the command list and ensure it is aligned to 1024 bytes
cmd_list_base := kalloc(sizeof(THBA_CMD_HEADER) * 64); cmd_list_base := kalloc(sizeof(THBA_CMD_HEADER) * 64);
cmd_list_base := puint32((uint32(cmd_list_base) + 1023) and $FFFFFC00); cmd_list_base := puint32((uint32(cmd_list_base) + 1023) and $FFFFFC00);
memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER) * 32);
//allocate memory for the FIS and ensure it is aligned to 256 bytes
fis_base := kalloc(sizeof(THBA_FIS) * 8);
fis_base := puint32((uint32(fis_base) + 255) and $FFFFFF00);
//set the command list base address //set the command list base address
port^.cmdl_basel := vtop(uint32(cmd_list_base)); //todo set virtual address in device port^.cmdl_basel := vtop(uint32(cmd_list_base)); //todo set virtual address in device
port^.cmdl_baseu := 0; port^.cmdl_baseu := 0;
memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER));
device^.command_list := PHBA_CMD_HEADER(cmd_list_base); device^.command_list := PHBA_CMD_HEADER(cmd_list_base);
//print the command list base address
console.writestring('AHCI: Command list base address: ');
console.writehexln(uint32(cmd_list_base));
//allocate memory for the FIS and ensure it is aligned to 256 bytes
fis_base := kalloc(sizeof(THBA_FIS) * 8);
fis_base := puint32((uint32(fis_base) + 255) and $FFFFFF00);
//set the FIS base address //set the FIS base address
port^.fis_basel := vtop(uint32(fis_base)); port^.fis_basel := vtop(uint32(fis_base));
port^.fis_baseu := 0; port^.fis_baseu := 0;
@ -164,9 +206,10 @@ begin
device^.fis := PHBA_FIS(fis_base); device^.fis := PHBA_FIS(fis_base);
//todo check how many simultaneous commands are supported //todo check how many simultaneous commands are supported
//allocate memory for the command table and ensure it is aligned to 1024 bytes //allocate memory for the command table and ensure it is aligned to 128 bytes
cmd_table_base := kalloc(sizeof(THBA_CMD_TABLE) * 64); cmd_table_base := kalloc(sizeof(THBA_CMD_TABLE) * 64);
cmd_table_base := puint32((uint32(cmd_table_base) + 1023) and $FFFFFC00); cmd_table_base := puint32((uint32(cmd_table_base) + 127) and $FFFFFF80);
memset(uint32(cmd_table_base), 0, sizeof(THBA_CMD_TABLE) * 32);
device^.command_table := PHBA_CMD_TABLE(cmd_table_base); device^.command_table := PHBA_CMD_TABLE(cmd_table_base);
@ -174,93 +217,148 @@ begin
for ii:=0 to 31 do begin for ii:=0 to 31 do begin
//set command header locations //set command header locations
command_header := PHBA_CMD_HEADER(uint32(cmd_list_base) + (ii * sizeof(THBA_CMD_HEADER))); command_header := PHBA_CMD_HEADER(uint32(cmd_list_base) + (ii * sizeof(THBA_CMD_HEADER)));
command_header^.prdtl := 32; command_header^.prdtl := 32;
//TODO do i need to set prdbc byte count here?
command_header^.cmd_table_base := vtop(uint32(cmd_table_base)) + (ii * sizeof(THBA_CMD_TABLE)); command_header^.cmd_table_base := vtop(uint32(cmd_table_base)) + (ii * sizeof(THBA_CMD_TABLE));
command_header^.cmd_table_baseu := 0; command_header^.cmd_table_baseu := 0;
memset(uint32(cmd_table_base) + (ii * sizeof(THBA_CMD_TABLE)), 0, sizeof(THBA_CMD_TABLE));
end; end;
//reset the port
reset_port(port);
start_port(port); start_port(port);
//print sata status
console.writestring('AHCI: 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
identify_device(controller, DL_Size(controller^.devices) - 1); identify_device(controller, DL_Size(controller^.devices) - 1);
end; end;
controller^.mio^.int_status := $FFFFFFFF;
// identify_device(controller, DL_Size(controller^.devices) - 1);
end;
end; end;
end; end;
procedure identify_device(controller : PAHCI_Controller; port : uint32); procedure identify_device(controller : PAHCI_Controller; portIndex : uint32);
var var
fis : PHBA_FIS_REG_H2D; fis : PHBA_FIS_REG_H2D;
cmd_header : PHBA_CMD_HEADER; cmd_header : PHBA_CMD_HEADER;
cmd_table : PHBA_CMD_TABLE; cmd_table : PHBA_CMD_TABLE;
cmd : uint32; cmd : uint32;
i : uint32; i, timeout : uint32;
buffer :puint32; buffer : puint32;
device : PAHCI_Device; device : PAHCI_Device;
tfd : uint32;
sec_count : uint32;
b8 : puint16;
begin begin
console.writestringln('AHCI: Identifying device');
device := PAHCI_Device(DL_Get(controller^.devices, portIndex));
device := PAHCI_Device(DL_Get(controller^.devices, port)); // (Optional) Print the device type if you store it:
console.writestring('AHCI: Device type: ');
// For example: if device^.device_type = SATA then ...
console.writestring('AHCI: sata status: ');
console.writehexln(device^.port^.sata_status);
//clear any pending interrupts
device^.port^.int_status := $FFFFFFFF;
device^.port^.int_enable := 0;
// Allocate a 512-byte DMA buffer for the IDENTIFY data
buffer := kalloc(512); buffer := kalloc(512);
memset(uint32(buffer), 0, 512); memset(uint32(buffer), 0, 512);
cmd_header := device^.command_list; // Use command slot 0 for the IDENTIFY command.
cmd_header := device^.command_list; // Assuming slot 0 is at the beginning.
cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32); cmd_header^.cmd_fis_length := sizeof(THBA_FIS_REG_H2D) div sizeof(uint32);
// cmd_header^.command := ATA_CMD_IDENTIFY;
cmd_header^.wrt := 0; cmd_header^.wrt := 0;
cmd_header^.prdtl := 1; cmd_header^.prdtl := 1;
cmd_header^.clear_busy := 1;
//just use first command table for identify as no other commands are running
// Setup the command table (using slot 0)
cmd_table := device^.command_table; cmd_table := device^.command_table;
cmd_table^.prdt[0].dba := vtop(uint32(buffer)); cmd_table^.prdt[0].dba := vtop(uint32(buffer));
cmd_table^.prdt[0].dbc := 511; cmd_table^.prdt[0].dbc := 511; // 512 bytes (0-based count)
cmd_table^.prdt[0].int := 1; 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(@device^.command_table^.cmd_fis); fis := PHBA_FIS_REG_H2D(@device^.command_table^.cmd_fis);
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^.pmport := 0; fis^.c := $1;
fis^.command := ATA_CMD_IDENTIFY; fis^.command := ATA_CMD_IDENTIFY;
fis^.device := 0; fis^.device := $A0; // LBA mode 40?
fis^.lba0 := 0;
fis^.lba1 := 0;
fis^.lba2 := 0;
fis^.lba3 := 0;
fis^.lba4 := 0;
fis^.lba5 := 0;
fis^.countl := 1;
fis^.counth := 0;
fis^.featurel := 0;
fis^.featureh := 0;
//send the command //waut for the port to be ready, bit 7 in the TFD register and bit 3 tfd
cmd := device^.port^.cmd; 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; cmd := cmd or $1;
device^.port^.cmd := cmd; device^.port^.cmd_issue := cmd;
//wait for the command to complete console.writestringln('AHCI: Sent identify command');
while (cmd and $1) = 1 do begin
cmd := device^.port^.cmd;
// 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: IDENTIFY 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; end;
//check the status of the command until ((device^.port^.cmd_issue and $1) = 0); // Wait until slot 0 is cleared
if (cmd and $2) = 1 then begin
console.writestringln('AHCI: Command complete');
// Check for an error flag in the command register (bit 1)
if (cmd and $2) <> 0 then begin
console.writestringln('AHCI: Error sending identify command'); console.writestringln('AHCI: Error sending identify command');
// Check the error register for more information
console.writestring('AHCI: Error register: ');
console.writehexln(device^.port^.sata_error);
end; end;
//parse the identify data // Dump the 512-byte IDENTIFY data for debugging (display 64 DWORDs)
for i:=0 to 63 do begin for i := 0 to 63 do begin
console.writehex(uint32(buffer[i])); console.writehex(uint32(buffer[i]));
console.writestring(' '); console.writestring(' ');
end; end;
console.writestringln(''); console.writestringln('');
b8 := puint16(buffer);
sec_count := (b8[61] shl 16) or b8[60];
console.writestring('AHCI: Sector count: ');
console.writeintln(sec_count);
console.writestring('AHCI: Device size: ');
console.writeint(sec_count * 512 div 1024 div 1024);
console.writestringln(' MB');
console.writestringln('');
end; end;
function load(ptr : void) : boolean; function load(ptr : void) : boolean;
var var
device : PPCI_Device; device : PPCI_Device;
@ -271,6 +369,8 @@ var
fis_base : puint32; fis_base : puint32;
cmd_table_base : puint32; cmd_table_base : puint32;
int_no : uInt8; int_no : uInt8;
timeout : uint32;
bohc : uint32;
begin begin
console.writestringln('AHCI: initilizing a new controller'); console.writestringln('AHCI: initilizing a new controller');
@ -284,7 +384,10 @@ begin
pci.enableDevice(device^.bus, device^.slot, device^.func); pci.enableDevice(device^.bus, device^.slot, device^.func);
int_no := device^.interrupt_line; int_no := device^.interrupt_line;
registerISR(int_no, @ahci_isr);
//print interrupt number
console.writestring('AHCI: Interrupt number: ');
console.writehexln(int_no);
controller := PAHCI_Controller(DL_Add(ahciControllers)); controller := PAHCI_Controller(DL_Add(ahciControllers));
controller^.devices := DL_New(SizeOf(TAHCI_Device)); controller^.devices := DL_New(SizeOf(TAHCI_Device));
@ -299,19 +402,53 @@ begin
base := PHBA_Memory(device^.address5); base := PHBA_Memory(device^.address5);
controller^.mio := base; controller^.mio := base;
if (controller^.mio^.capabilites2 and $28) <> 0 then begin
console.writestringln('AHCI: Controller needs to be handed off to the OS');
end;
console.writestring('AHCI: Controller at: '); console.writestring('AHCI: Controller at: ');
console.writehexln(device^.address5); console.writehexln(device^.address5);
console.writehexln(uint32(page_base)); console.writehexln(uint32(page_base));
bohc := controller^.mio^.bohc;
if (bohc and $1) <> 0 or (bohc and not (1 shl 1)) then begin
console.writestringln('AHCI: BIOS Owned Semaphore is set, initiating handoff.');
// Set the OS Owned Semaphore (typically bit 1).
bohc := bohc or (1 shl 1);
controller^.mio^.bohc := bohc;
// Wait until the BIOS Owned Semaphore (bit 0) is cleared.
timeout := 0;
while ((controller^.mio^.bohc and $1) <> 0) and (timeout < 1000000) do begin
timeout := timeout + 1;
end;
if timeout = 1000000 then begin
console.writestringln('AHCI: BIOS/OS handoff timed out.');
end else begin
console.writestringln('AHCI: BIOS/OS handoff successful.');
end;
end else begin
console.writestringln('AHCI: BIOS not holding controller or handoff already complete.');
end;
base^.global_ctrl := base^.global_ctrl or 1;
while (base^.global_ctrl and 1) <> 0 do begin
end;
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
//clear any pending interrupts //clear any pending interrupts
base^.int_status := $FFFFFFFF; base^.int_status := $FFFFFFFF;
check_ports(controller); check_ports(controller);

View File

@ -80,6 +80,7 @@ type
sata_noti: uint32; // SATA Notification sata_noti: uint32; // SATA Notification
fis_switch_ctrl: uint32;// FIS-based Switch Control fis_switch_ctrl: uint32;// FIS-based Switch Control
rsv1: array[0..10] of uint32; rsv1: array[0..10] of uint32;
vendor: array[0..3] of uint32;
end; end;
PHBA_Port = ^THBA_Port; PHBA_Port = ^THBA_Port;
@ -102,8 +103,16 @@ type
em_control: uint32; // Enclosure Management Control em_control: uint32; // Enclosure Management Control
capabilites2: uint32; // Host Capabilities Extended capabilites2: uint32; // Host Capabilities Extended
bohc: uint32; // BIOS/OS Handoff Control and Status bohc: uint32; // BIOS/OS Handoff Control and Status
rsv0: array[0..$210] of boolean;
//0x2c to 0x9f reserved
rsv0: array[0..115] of uint8;
//vendor specific
vendor: array[0..95] of uint8;
//port registers
ports: array[0..31] of THBA_Port; ports: array[0..31] of THBA_Port;
end; end;
PHBA_Memory = ^THBA_Memory; PHBA_Memory = ^THBA_Memory;
@ -145,7 +154,10 @@ type
} }
THBA_FIS_REG_H2D = bitpacked record THBA_FIS_REG_H2D = bitpacked record
fis_type: uint8; // FIS Type fis_type: uint8; // FIS Type
pmport: uint8; // Port Multiplier Port // pmport: uint8; // Port Multiplier Port is pmport:4 and rsv0:3 and i:1
pmport: ubit4; // Port Multiplier Port
rsv: ubit3; // Reserved
c: ubit1; // command or control
command: uint8; // Command command: uint8; // Command
featurel: uint8; // Feature Lower 8-bits featurel: uint8; // Feature Lower 8-bits
lba0: uint8; // LBA0 lba0: uint8; // LBA0
@ -160,7 +172,7 @@ type
counth: uint8; // Count Upper 8-bits counth: uint8; // Count Upper 8-bits
icc: uint8; // Isochronous Command Completion icc: uint8; // Isochronous Command Completion
control: uint8; // Control control: uint8; // Control
rsv0: array[0..2] of uint8; rsv0: array[0..3] of uint8;
end; end;
PHBA_FIS_REG_H2D = ^THBA_FIS_REG_H2D; PHBA_FIS_REG_H2D = ^THBA_FIS_REG_H2D;
@ -171,19 +183,22 @@ type
AHCI Host Bus Adapter (HBA) Command Header Interface AHCI Host Bus Adapter (HBA) Command Header Interface
} }
THBA_CMD_HEADER = bitpacked record THBA_CMD_HEADER = bitpacked record
cmd_fis_length: uint8; // Command FIS Length cmd_fis_length: UBit5; // Command FIS Length
atapi: uint8; // ATAPI atapi: UBit1; // ATAPI
wrt: uint8; // Write wrt: UBit1; // Write
prefetchable: uint8; // Prefetchable prefetchable: UBit1; // Prefetchable
reset: uint8; // Reset
bist: uint8; // BIST reset: UBit1; // Reset
clear_busy: uint8; // Clear Busy bist: UBit1; // BIST
reserved0: uint8; // Reserved clear_busy: UBit1; // Clear Busy
reserved0: UBit1; // Reserved
port_multiplier: UBit4; // Port Multiplier Port
prdtl: uint16; // Physical Region Descriptor Table Length prdtl: uint16; // Physical Region Descriptor Table Length
prdbc: uint32; // Physical Region Descriptor Byte Count prdbc: uint32; // Physical Region Descriptor Byte Count
cmd_table_base: uint32; // Command Table Base Address cmd_table_base: uint32; // Command Table Base Address
cmd_table_baseu: uint32; // Command Table Base Address Upper 32-bits cmd_table_baseu: uint32; // Command Table Base Address Upper 32-bits
rsv0: array[0..4] of uint32; rsv0: array[0..3] of uint32;
end; end;
PHBA_CMD_HEADER = ^THBA_CMD_HEADER; PHBA_CMD_HEADER = ^THBA_CMD_HEADER;
@ -195,8 +210,8 @@ type
dba: uint32; // Data Base Address dba: uint32; // Data Base Address
dbau: uint32; // Data Base Address Upper 32-bits dbau: uint32; // Data Base Address Upper 32-bits
rsv0: uint32; // Reserved rsv0: uint32; // Reserved
reserved: ubit9; // Reserved
dbc: ubit22; // Data Byte Count dbc: ubit22; // Data Byte Count
reserved: ubit9; // Reserved
int: ubit1; // Interrupt int: ubit1; // Interrupt
// dbc: uint32; // Data Byte Count, bit 1 is Interrupt, then 22 bits of Byte Count, then 9 bits of Reserved // dbc: uint32; // Data Byte Count, bit 1 is Interrupt, then 22 bits of Byte Count, then 9 bits of Reserved
end; end;
@ -211,7 +226,7 @@ type
cmd_fis: array[0..63] of uint8; // Command FIS cmd_fis: array[0..63] of uint8; // Command FIS
acmd: array[0..15] of uint8; // ATAPI Command acmd: array[0..15] of uint8; // ATAPI Command
rsv0: array[0..47] of uint8; rsv0: array[0..47] of uint8;
prdt: TPRDT; // Physical Region Descriptor Table prdt: array[0..31] of THBA_PRD; // Physical Region Descriptor Table
end; end;
PHBA_CMD_TABLE = ^THBA_CMD_TABLE; PHBA_CMD_TABLE = ^THBA_CMD_TABLE;
@ -227,8 +242,8 @@ type
Device type enum Device type enum
} }
TDeviceType = ( TDeviceType = (
SATA, SATA = 1,
ATAPI, ATAPI = 2,
SEMB, SEMB,
PM PM
); );