From 05eab239566f5208c2b605513dedfd6a65c7ec94 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 19 Mar 2025 22:15:32 +0000 Subject: [PATCH] starting to work --- src/driver/bus/PCI.pas | 2 + src/driver/storage/AHCI/AHCI.pas | 271 +++++++++++++++++++------- src/driver/storage/AHCI/AHCITypes.pas | 47 +++-- 3 files changed, 237 insertions(+), 83 deletions(-) diff --git a/src/driver/bus/PCI.pas b/src/driver/bus/PCI.pas index 13439b68..f07fcd4f 100644 --- a/src/driver/bus/PCI.pas +++ b/src/driver/bus/PCI.pas @@ -461,9 +461,11 @@ begin cmd := inl(PCI_CONFIG_DATA_PORT); cmd := cmd or PCI_COMMAND_MEM_SPACE; cmd := cmd or PCI_COMMAND_BUS_MASTER; + cmd := cmd or (3 shl 1); //enable interrupts, remove disable interrupt bit cmd := cmd and not PCI_COMMAND_INT_DISABLE; + cmd := cmd and not (1 shl 9); outl(PCI_CONFIG_ADDRESS_PORT, addr); outl(PCI_CONFIG_DATA_PORT, cmd); diff --git a/src/driver/storage/AHCI/AHCI.pas b/src/driver/storage/AHCI/AHCI.pas index f4cb8e6f..f688d2c1 100644 --- a/src/driver/storage/AHCI/AHCI.pas +++ b/src/driver/storage/AHCI/AHCI.pas @@ -42,7 +42,7 @@ var procedure init(); function load(ptr : void) : boolean; 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(); implementation @@ -65,18 +65,49 @@ end; procedure stop_port(port : PHBA_Port); begin - port^.cmd := port^.cmd and not $1; ///maybe also bit 4 - while (port^.cmd and $1) = 1 do begin + port^.cmd := port^.cmd and not $1; + 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 end; end; procedure start_port(port : PHBA_Port); begin - port^.cmd := port^.cmd or $1; ///maybe also bit 4 - while (port^.cmd and $1) = 0 do begin - //wait for the port to start + + while (port^.cmd and $8000) <> 0 do begin + //wait for port to not be busy 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; { @@ -98,13 +129,22 @@ begin console.writehexln(controller^.mio^.ports_implimented); 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]; console.writestring('AHCI: Port '); console.writeint(i); console.writestring(' implemented on controller '); console.writehexln(uint32(controller^.pci_device^.address5)); + device := PAHCI_Device(DL_Add(controller^.devices)); + device^.port := port; + //check if the port is active TODO // if ((port^.sata_status shr 8) <> 1) and ((port^.sata_status and $0F) <> 3) then begin // continue; wrong @@ -135,26 +175,28 @@ begin //NEEED TO STOP the port before doing anything stop_port(port); - - device := PAHCI_Device(DL_Add(controller^.devices)); - device^.port := port; + console.writestringln('AHCI: Port stopped'); //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 := puint32((uint32(cmd_list_base) + 1023) and $FFFFFC00); - - //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); + memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER) * 32); //set the command list base address port^.cmdl_basel := vtop(uint32(cmd_list_base)); //todo set virtual address in device port^.cmdl_baseu := 0; - memset(uint32(cmd_list_base), 0, sizeof(THBA_CMD_HEADER)); - 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 port^.fis_basel := vtop(uint32(fis_base)); port^.fis_baseu := 0; @@ -164,9 +206,10 @@ begin device^.fis := PHBA_FIS(fis_base); //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 := 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); @@ -174,93 +217,148 @@ begin for ii:=0 to 31 do begin //set command header locations command_header := PHBA_CMD_HEADER(uint32(cmd_list_base) + (ii * sizeof(THBA_CMD_HEADER))); - 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_baseu := 0; - memset(uint32(cmd_table_base) + (ii * sizeof(THBA_CMD_TABLE)), 0, sizeof(THBA_CMD_TABLE)); end; + //reset the port + reset_port(port); start_port(port); + //print sata status + console.writestring('AHCI: sata status: '); + console.writehexln(port^.sata_status); + //pass devices count as second param - identify_device(controller, DL_Size(controller^.devices) - 1); + if (device^.device_type = SATA) then begin + identify_device(controller, DL_Size(controller^.devices) - 1); + end; + + controller^.mio^.int_status := $FFFFFFFF; + + // identify_device(controller, DL_Size(controller^.devices) - 1); end; end; end; -procedure identify_device(controller : PAHCI_Controller; port : uint32); +procedure identify_device(controller : PAHCI_Controller; portIndex : uint32); var - fis : PHBA_FIS_REG_H2D; - cmd_header : PHBA_CMD_HEADER; - cmd_table : PHBA_CMD_TABLE; - cmd : uint32; - i : uint32; - buffer :puint32; - device : PAHCI_Device; + fis : PHBA_FIS_REG_H2D; + cmd_header : PHBA_CMD_HEADER; + cmd_table : PHBA_CMD_TABLE; + cmd : uint32; + i, timeout : uint32; + buffer : puint32; + device : PAHCI_Device; + tfd : uint32; + sec_count : uint32; + b8 : puint16; begin + console.writestringln('AHCI: Identifying device'); + device := PAHCI_Device(DL_Get(controller^.devices, portIndex)); + + // (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); - device := PAHCI_Device(DL_Get(controller^.devices, port)); - + //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); 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^.command := ATA_CMD_IDENTIFY; cmd_header^.wrt := 0; cmd_header^.prdtl := 1; + cmd_header^.clear_busy := 1; - //just use first command table for identify as no other commands are running - cmd_table := device^.command_table; + + // Setup the command table (using slot 0) + cmd_table := device^.command_table; cmd_table^.prdt[0].dba := vtop(uint32(buffer)); - cmd_table^.prdt[0].dbc := 511; - cmd_table^.prdt[0].int := 1; + cmd_table^.prdt[0].dbc := 511; // 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(@device^.command_table^.cmd_fis); + memset(uint32(fis), 0, sizeof(THBA_FIS_REG_H2D)); fis^.fis_type := uint8(FIS_TYPE_REG_H2D); - fis^.pmport := 0; - fis^.command := ATA_CMD_IDENTIFY; - fis^.device := 0; - 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; + fis^.c := $1; + fis^.command := ATA_CMD_IDENTIFY; + fis^.device := $A0; // LBA mode 40? - //send the command - cmd := device^.port^.cmd; + //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; - device^.port^.cmd := cmd; + device^.port^.cmd_issue := cmd; - //wait for the command to complete - while (cmd and $1) = 1 do begin - cmd := device^.port^.cmd; - end; + console.writestringln('AHCI: Sent identify command'); - //check the status of the command - if (cmd and $2) = 1 then begin + + // 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; + + until ((device^.port^.cmd_issue and $1) = 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 $2) <> 0 then begin 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; - //parse the identify data - for i:=0 to 63 do begin + // Dump the 512-byte IDENTIFY data for debugging (display 64 DWORDs) + for i := 0 to 63 do begin console.writehex(uint32(buffer[i])); console.writestring(' '); end; + 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; + function load(ptr : void) : boolean; var device : PPCI_Device; @@ -271,6 +369,8 @@ var fis_base : puint32; cmd_table_base : puint32; int_no : uInt8; + timeout : uint32; + bohc : uint32; begin console.writestringln('AHCI: initilizing a new controller'); @@ -284,7 +384,10 @@ begin pci.enableDevice(device^.bus, device^.slot, device^.func); 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^.devices := DL_New(SizeOf(TAHCI_Device)); @@ -299,19 +402,53 @@ begin base := PHBA_Memory(device^.address5); 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.writehexln(device^.address5); 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 base^.global_ctrl := base^.global_ctrl or AHCI_CONTROLLER_MODE; + base^.global_ctrl := base^.global_ctrl or $2; //enable interrupts + //clear any pending interrupts base^.int_status := $FFFFFFFF; - - - check_ports(controller); diff --git a/src/driver/storage/AHCI/AHCITypes.pas b/src/driver/storage/AHCI/AHCITypes.pas index 8a699cc5..52186c84 100644 --- a/src/driver/storage/AHCI/AHCITypes.pas +++ b/src/driver/storage/AHCI/AHCITypes.pas @@ -80,6 +80,7 @@ type sata_noti: uint32; // SATA Notification fis_switch_ctrl: uint32;// FIS-based Switch Control rsv1: array[0..10] of uint32; + vendor: array[0..3] of uint32; end; PHBA_Port = ^THBA_Port; @@ -102,8 +103,16 @@ type em_control: uint32; // Enclosure Management Control capabilites2: uint32; // Host Capabilities Extended 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; + end; PHBA_Memory = ^THBA_Memory; @@ -145,7 +154,10 @@ type } THBA_FIS_REG_H2D = bitpacked record 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 featurel: uint8; // Feature Lower 8-bits lba0: uint8; // LBA0 @@ -160,7 +172,7 @@ type counth: uint8; // Count Upper 8-bits icc: uint8; // Isochronous Command Completion control: uint8; // Control - rsv0: array[0..2] of uint8; + rsv0: array[0..3] of uint8; end; PHBA_FIS_REG_H2D = ^THBA_FIS_REG_H2D; @@ -171,19 +183,22 @@ type AHCI Host Bus Adapter (HBA) Command Header Interface } THBA_CMD_HEADER = bitpacked record - cmd_fis_length: uint8; // Command FIS Length - atapi: uint8; // ATAPI - wrt: uint8; // Write - prefetchable: uint8; // Prefetchable - reset: uint8; // Reset - bist: uint8; // BIST - clear_busy: uint8; // Clear Busy - reserved0: uint8; // Reserved + cmd_fis_length: UBit5; // Command FIS Length + atapi: UBit1; // ATAPI + wrt: UBit1; // Write + prefetchable: UBit1; // Prefetchable + + reset: UBit1; // Reset + bist: UBit1; // BIST + clear_busy: UBit1; // Clear Busy + reserved0: UBit1; // Reserved + port_multiplier: UBit4; // Port Multiplier Port + prdtl: uint16; // Physical Region Descriptor Table Length prdbc: uint32; // Physical Region Descriptor Byte Count cmd_table_base: uint32; // Command Table Base Address cmd_table_baseu: uint32; // Command Table Base Address Upper 32-bits - rsv0: array[0..4] of uint32; + rsv0: array[0..3] of uint32; end; PHBA_CMD_HEADER = ^THBA_CMD_HEADER; @@ -195,8 +210,8 @@ type dba: uint32; // Data Base Address dbau: uint32; // Data Base Address Upper 32-bits rsv0: uint32; // Reserved - reserved: ubit9; // Reserved dbc: ubit22; // Data Byte Count + reserved: ubit9; // Reserved int: ubit1; // Interrupt // dbc: uint32; // Data Byte Count, bit 1 is Interrupt, then 22 bits of Byte Count, then 9 bits of Reserved end; @@ -211,7 +226,7 @@ type cmd_fis: array[0..63] of uint8; // Command FIS acmd: array[0..15] of uint8; // ATAPI Command rsv0: array[0..47] of uint8; - prdt: TPRDT; // Physical Region Descriptor Table + prdt: array[0..31] of THBA_PRD; // Physical Region Descriptor Table end; PHBA_CMD_TABLE = ^THBA_CMD_TABLE; @@ -227,8 +242,8 @@ type Device type enum } TDeviceType = ( - SATA, - ATAPI, + SATA = 1, + ATAPI = 2, SEMB, PM );