Gather HBA port-specific context in a struct

This commit is contained in:
Thomas Oltmann 2025-10-20 16:54:55 +02:00
parent f33421857a
commit df7898d216

View file

@ -6,282 +6,24 @@
#include <x86_64/sata.h> #include <x86_64/sata.h>
#if 0 struct port_context {
AHCI_CONTROLLER Sata_devices[10]; volatile HBA_PORT *port_mem;
struct pa alloc_pa;
int check_PCI_devices(AHCI_CONTROLLER *list_of_devices, int range) struct pa cmd_list_pa;
{ struct pa cmd_table_pa;
int array_index = 0; struct pa recv_fis_pa;
for (int bus = 0; bus < 256; bus++) { struct pa buffer_pa;
for (int device = 0; device < 32; device++) { // Up to 32 devices per bus. volatile uint8_t *alloc_ptr;
for (int function = 0; function < 8; function++) { // Up to 8 functions per device. volatile HBA_CMD_HEADER *cmd_list_ptr;
// --- Read the common PCI header fields --- volatile HBA_CMD_TBL *cmd_table_ptr;
uint16_t bdf_device = pci_bdf_make(bus, device, function); volatile uint8_t *recv_fis_ptr;
// Offset 0x00: Vendor and Device IDs. volatile uint8_t *buffer_ptr;
uint32_t id_val = pci_config_read_u32(bdf_device, 0x00); Timer timer;
uint16_t vendor_id = id_val & 0xFFFF; };
if (vendor_id == 0xFFFF) { // Device not present.
continue;
}
uint16_t device_id = (id_val >> 16) & 0xFFFF;
// Offset 0x04: Command (lower 16 bits) and Status (upper 16
// bits).
uint32_t cmd_status = pci_config_read_u32(bdf_device, 0x04);
uint16_t command = cmd_status & 0xFFFF;
uint16_t status = (cmd_status >> 16) & 0xFFFF;
// Offset 0x08: Revision ID and Class Codes.
// The 32-bit value at 0x08 has:
// Bits 0-7: Revision ID,
// Bits 8-15: Programming Interface,
// Bits 16-23: Subclass,
// Bits 24-31: Base Class.
uint32_t rev_class = pci_config_read_u32(bdf_device, 0x08);
uint8_t revision_id = rev_class & 0xFF;
uint8_t prog_if = (rev_class >> 8) & 0xFF;
uint8_t subclass = (rev_class >> 16) & 0xFF;
uint8_t base_class = (rev_class >> 24) & 0xFF;
// Check for AHCI SATA Controller:
// Base Class 0x01 (Mass Storage),// Macros for the Global
// Host Control (GHC) register bits. Subclass 0x06 (SATA),
// Programming Interface 0x01 (AHCI).
if (!(base_class == 0x01 && subclass == 0x06 && prog_if == 0x01)) {
continue;
}
// Offset 0x0C: Contains four 8-bit fields.
// Byte 0: Cache Line Size
// Byte 1: Master Latency Timer
// Byte 2: Header Type
// Byte 3: BIST
uint32_t misc = pci_config_read_u32(bdf_device, 0x0C);
uint8_t cache_line_size = misc & 0xFF;
uint8_t master_latency_timer = (misc >> 8) & 0xFF;
uint8_t header_type = (misc >> 16) & 0xFF;
uint8_t bist = (misc >> 24) & 0xFF;
// Offsets 0x10 - 0x23: BAR0 - BAR4.
uint32_t bars[5];
for (int i = 0; i < 5; i++) {
bars[i] = pci_config_read_u32(bdf_device, 0x10 + (i * 4));
}
// Offset 0x24: AHCI BAR (BAR5); mask lower nibble.
uint32_t abar = pci_config_read_u32(bdf_device,
0x24); // Macros for the Global Host
// Control (GHC) register bits.
// Offset 0x28: Reserved.
uint32_t reserved0 = pci_config_read_u32(bdf_device, 0x28);
// Offsets 0x2C - 0x2F: Subsystem Identifiers.
uint32_t subsys = pci_config_read_u32(bdf_device, 0x2C);
uint16_t subsystem_vendor_id = subsys & 0xFFFF;
uint16_t subsystem_id = (subsys >> 16) & 0xFFFF;
// Offset 0x30: Expansion ROM Base Address.
uint32_t expansion_rom_base = pci_config_read_u32(bdf_device, 0x30);
// Offset 0x34: Capabilities Pointer (a single byte).
uint32_t cap_val = pci_config_read_u32(bdf_device, 0x34);
uint8_t cap_ptr = cap_val & 0xFF;
// Offsets 0x35 - 0x3B: Reserved bytes.
// For simplicity, we initialize these to zero.
uint8_t reserved1[7] = {0, 0, 0, 0, 0, 0, 0};
// Offset 0x3C - 0x3F: Interrupt Information.
// Byte 0: Interrupt Line
// Byte 1: Interrupt Pin
// Byte 2: Min Grant
// Byte 3: Max Latency
uint32_t intr = pci_config_read_u32(bdf_device, 0x3C);
uint8_t interrupt_line = intr & 0xFF;
uint8_t interrupt_pin = (intr >> 8) & 0xFF;
uint8_t min_grant = (intr >> 16) & 0xFF;
uint8_t max_latency = (intr >> 24) & 0xFF;
// --- Populate the SATA_DEVICE structure --- Add only if Sata
// device
if (array_index < range && subclass == 0x06) {
AHCI_CONTROLLER dev;
dev.vendor_id = vendor_id;
dev.device_id = device_id;
dev.command = command;
dev.status = status;
dev.revision_id = revision_id;
dev.class_code[0] = prog_if; // Programming Interface.
dev.class_code[1] = subclass; // Subclass. if Subclass =
// 06h then Sata device
dev.class_code[2] = base_class; // Base Class.
dev.cache_line_size = cache_line_size;
dev.master_latency_timer = master_latency_timer;
dev.header_type = header_type;
dev.bist = bist;
for (int i = 0; i < 5; i++) {
dev.bars[i] = bars[i];
}
dev.abar = abar;
dev.reserved0 = reserved0;
dev.subsystem_vendor_id = subsystem_vendor_id;
dev.subsystem_id = subsystem_id;
dev.expansion_rom_base = expansion_rom_base;
dev.cap_ptr = cap_ptr;
for (int i = 0; i < 7; i++) {
dev.reserved1[i] = reserved1[i];
}
dev.interrupt_line = interrupt_line;
dev.interrupt_pin = interrupt_pin;
dev.min_grant = min_grant;
dev.max_latency = max_latency;
dev.bdf = bdf_device;
list_of_devices[array_index++] = dev;
}
}
}
}
return array_index;
}
void map_HBA_memory(AHCI_CONTROLLER controller)
{
// Setup Stuff
controller.virtual_HBA_address = HBA_pointer(controller);
update_PCI_BUS(controller);
reset_controller(controller);
activate_SATA_mode(controller);
scan_ahci_ports(controller.virtual_HBA_address);
update_PCI_BUS(controller);
// Allocate Memory for Command List
struct ppn page_number_command_list;
struct ram_buffer_requirements buffer_requirements = {0};
buffer_requirements.size = 0b1000000;
bool allocation_success = ram_alloc_buffer(&page_number_command_list, buffer_requirements);
struct pa phys_page = pa_from_ppn(page_number_command_list);
uint64_t value = pa_to_value(phys_page);
HBA_CMD_HEADER *cmd_list = pa_to_pointer(phys_page);
// Allocate Memeory for a single Command Table
struct ppn page_number_command_table;
bool allocation_success_command_table = ram_alloc_buffer(&page_number_command_table, buffer_requirements);
struct pa phys_page_command_table = pa_from_ppn(page_number_command_table);
uint64_t value_command_tbl = pa_to_value(phys_page_command_table);
HBA_CMD_TBL *cmd_tbl = pa_to_pointer(phys_page_command_table);
// Allocate Memory for recived FIS Strcture
struct ppn page_number_fb;
struct ram_buffer_requirements buffer_requirements_fis_receive = {0};
buffer_requirements_fis_receive.size = 0x1; // TODO
bool allocation_success_fb = ram_alloc_buffer(&page_number_fb, buffer_requirements_fis_receive);
struct pa phys_page_fb = pa_from_ppn(page_number_fb);
uint64_t value_fb = pa_to_value(phys_page_fb);
HBA_FIS *fb = pa_to_pointer(phys_page_fb);
// Set bits for command List
cmd_list[0] = (HBA_CMD_HEADER){0};
cmd_list[0].w = 0;
cmd_list[0].a = 0;
cmd_list[0].c = 1;
cmd_list[0].cfl = 4;
cmd_list[0].p = 0;
cmd_list[0].prdbc = 0;
cmd_list[0].prdtl = 0;
cmd_list[0].ctba = (uint32_t)(value_command_tbl & 0xFFFFFFFF);
cmd_list[0].ctbau = (uint32_t)(value_command_tbl >> 32);
// Set FIS for sending
FIS_REG_H2D fis_identify = { 0 };
fis_identify.fis_type = FIS_TYPE_REG_H2D;
fis_identify.command = 0xEC;
fis_identify.device = 0;
fis_identify.c = 1;
// Setting up Receive FIS
controller.virtual_HBA_address->ports[0].fb = (uint32_t)(value_fb & 0xFFFFFFFF);
controller.virtual_HBA_address->ports[0].fbu = (uint32_t)(value_fb >> 32);
*((FIS_REG_H2D *)cmd_tbl->cfis) = fis_identify;
// stolen from OSDEV wiki idfk
# define HBA_PxCMD_ST 0x0001
# define HBA_PxCMD_FRE 0x0010
# define HBA_PxCMD_FR 0x4000
# define HBA_PxCMD_CR 0x8000
// Start Command
controller.virtual_HBA_address->ports[0].cmd |= HBA_PxCMD_FRE;
controller.virtual_HBA_address->ports[0].cmd |= HBA_PxCMD_ST;
// FIS RECIVE ENABLE SETZEN
// TODO:
// Currently Ci register changes
volatile HBA_PORT *port = &controller.virtual_HBA_address->ports[0];
port->clb = value;
port->cmd &= ~1;
port->ci |= 0x01 << 0;
port->cmd |= 1;
}
HBA_MEM *HBA_pointer(AHCI_CONTROLLER controller)
{
struct pa pa = pa_from_value(controller.abar);
return pa_to_pointer(pa);
}
void scan_ahci_ports(volatile HBA_MEM *hba)
{
uint32_t pi = hba->pi;
volatile HBA_PORT *port = &hba->ports[0];
uint32_t ssts = port->ssts;
uint32_t det = ssts & 0x0F; // [3:0]
uint32_t ipm = (ssts >> 8) & 0x0F; // [11:8]
printf("Port %d: (DET=0x%x, IPM=0x%x)\n", 0, det, ipm);
}
void update_PCI_BUS(AHCI_CONTROLLER controller)
{
uint32_t command = pci_config_read_u32(controller.bdf, 0x04);
printf("Current Command Register %x\n", command);
command |= 0x06;
pci_config_write_u32(controller.bdf, 0x04, command);
}
#define HBA_GHC_HR (1 << 0) // Host reset
void reset_controller(AHCI_CONTROLLER controller)
{
controller.virtual_HBA_address->ghc |= HBA_GHC_HR;
}
#define HBA_GHC_AE (1 << 2)
void activate_SATA_mode(AHCI_CONTROLLER controller)
{
controller.virtual_HBA_address->ghc |= HBA_GHC_AE;
}
int allocate_data_entry()
{
struct ppn page_number;
struct ram_buffer_requirements buffer_requirements = {0};
buffer_requirements.size = 0x1000;
bool allocation_success = ram_alloc_buffer(&page_number, buffer_requirements);
return allocation_success;
}
/*
int allocate_list(AHCI_CONTROLLER controller) {}
*/
#endif
static uint16_t hba_bdf; static uint16_t hba_bdf;
static volatile HBA_MEM *hba_mem; static volatile HBA_MEM *hba_mem;
static volatile HBA_PORT *hba_ports[32]; static struct port_context ports[32];
static Timer hba_port_timers[32];
static volatile uint8_t *hba_port_recv_fis[32];
static volatile uint8_t *hba_port_buffers[32];
void find_ahci_controllers(void) void find_ahci_controllers(void)
{ {
@ -294,12 +36,15 @@ void find_ahci_controllers(void)
void sata_revisit_port(Timer *timer) void sata_revisit_port(Timer *timer)
{ {
int port_num = timer - hba_port_timers; struct port_context *port = container_of(timer, struct port_context, timer);
printf("Port %d command bits: %x\n", port_num, hba_ports[port_num]->ci); int port_num = port - ports;
printf("Port %d command bits: %x\n", port_num, port->port_mem->ci);
for (int r = 0; r < 1024 / 16; r++) { for (int r = 0; r < 1024 / 16; r++) {
for (int c = 0; c < 16; c++) { for (int c = 0; c < 16; c++) {
int i = r * 16 + c; int i = r * 16 + c;
printf("%x%x ", hba_port_buffers[port_num][i] >> 4, hba_port_buffers[port_num][i] & 0xF); printf("%x%x ",
port->buffer_ptr[i] >> 4,
port->buffer_ptr[i] & 0xF);
} }
printf("\n"); printf("\n");
} }
@ -307,86 +52,91 @@ void sata_revisit_port(Timer *timer)
void sata_init_port(int port_num) void sata_init_port(int port_num)
{ {
printf("Found SATA Port %d\n", port_num); struct port_context *port = &ports[port_num];
// Allocate a buffer of physical memory printf("Found SATA Port %d\n", port_num);
struct ram_buffer_requirements alloc_reqs = { 0 };
alloc_reqs.size = 1024 + 1024 + 256 + 256;
struct ppn alloc_ppn;
bool alloc_ok = ram_alloc_buffer(&alloc_ppn, alloc_reqs);
ASSERT(alloc_ok);
struct pa alloc_pa = pa_from_ppn(alloc_ppn);
// Cut up buffer into separate structures // Allocate a buffer of physical memory
struct pa cmd_list_pa = alloc_pa; struct ram_buffer_requirements alloc_reqs = { 0 };
alloc_pa = pa_from_value(pa_to_value(alloc_pa) + 1024); alloc_reqs.size = 1024 + 1024 + 256 + 256;
struct pa buffer_pa = alloc_pa; struct ppn alloc_ppn;
alloc_pa = pa_from_value(pa_to_value(alloc_pa) + 1024); bool alloc_ok = ram_alloc_buffer(&alloc_ppn, alloc_reqs);
struct pa cmd_table_pa = alloc_pa; ASSERT(alloc_ok);
alloc_pa = pa_from_value(pa_to_value(alloc_pa) + 256); port->alloc_pa = pa_from_ppn(alloc_ppn);
struct pa recv_fis_pa = alloc_pa;
alloc_pa = pa_from_value(pa_to_value(alloc_pa) + 256);
hba_port_buffers[port_num] = pa_to_pointer(buffer_pa); // Cut up buffer into separate structures
memset((void *)hba_port_buffers[port_num], 0, 1024); port->cmd_list_pa = port->alloc_pa;
port->alloc_pa = pa_from_value(pa_to_value(port->alloc_pa) + 1024);
port->buffer_pa = port->alloc_pa;
port->alloc_pa = pa_from_value(pa_to_value(port->alloc_pa) + 1024);
port->cmd_table_pa = port->alloc_pa;
port->alloc_pa = pa_from_value(pa_to_value(port->alloc_pa) + 256);
port->recv_fis_pa = port->alloc_pa;
port->alloc_pa = pa_from_value(pa_to_value(port->alloc_pa) + 256);
// TODO make sure port is in idle state // Get pointers to the contents of the structures
port->cmd_list_ptr = pa_to_pointer(port->cmd_list_pa);
// TODO determine how many command slots the HBA supports (CAP.NCS) port->cmd_table_ptr = pa_to_pointer(port->cmd_table_pa);
port->recv_fis_ptr = pa_to_pointer(port->recv_fis_pa);
port->buffer_ptr = pa_to_pointer(port->buffer_pa);
memset((void *)port->recv_fis_ptr, 0, 256);
memset((void *)port->buffer_ptr, 0, 1024);
// Register structures with the HBA port // TODO make sure port is in idle state
hba_ports[port_num]->clb = pa_to_value(cmd_list_pa);
hba_ports[port_num]->fb = pa_to_value(recv_fis_pa); // TODO determine how many command slots the HBA supports (CAP.NCS)
hba_ports[port_num]->cmd |= PORT_CMD_FIS_RECV_ENABLE;
// TODO clear the PxSERR register // Register structures with the HBA port
hba_ports[port_num]->serr |= (1 << 9) | (1 << 10) | (1 << 11); port->port_mem->clb = pa_to_value(port->cmd_list_pa);
port->port_mem->fb = pa_to_value(port->recv_fis_pa);
port->port_mem->cmd |= PORT_CMD_FIS_RECV_ENABLE;
// TODO Clear PxIS, then IS.IPS, then: // TODO clear the PxSERR register
// TODO Set PxIE register with appropriate enables port->port_mem->serr |= (1 << 9) | (1 << 10) | (1 << 11);
// TODO Also set GHC.IE to 1
// Get pointers to the contents of the structures // TODO Clear PxIS, then IS.IPS, then:
volatile HBA_CMD_HEADER *cmd_list = pa_to_pointer(cmd_list_pa); // TODO Set PxIE register with appropriate enables
volatile HBA_CMD_TBL *cmd_table = pa_to_pointer(cmd_table_pa); // TODO Also set GHC.IE to 1
volatile void *recv_fis = pa_to_pointer(recv_fis_pa); }
hba_port_recv_fis[port_num] = recv_fis;
memset((void *)recv_fis, 0, 256);
// Fill out the command table void sata_submit_command(int port_num)
FIS_REG_H2D *fis_identify = (void *)cmd_table; {
fis_identify->fis_type = FIS_TYPE_REG_H2D; struct port_context *port = &ports[port_num];
//fis_identify->command = ATA_CMD_IDENTIFY;
fis_identify->command = ATA_CMD_READ_DMA_EXT;
fis_identify->device = (1 << 6);
fis_identify->lba0 = 0;
fis_identify->lba1 = 0;
fis_identify->lba2 = 0;
fis_identify->lba3 = 0;
fis_identify->lba4 = 0;
fis_identify->lba5 = 0;
fis_identify->countl = 1;
fis_identify->counth = 0;
fis_identify->c = 1;
cmd_table->prdt_entry[0].dba = pa_to_value(buffer_pa);
cmd_table->prdt_entry[0].dbc = 512 - 1;
// Fill out the command header // Fill out the command table
memset((void *)&cmd_list[0], 0, sizeof cmd_list[0]); volatile FIS_REG_H2D *fis_identify = (volatile void *)port->cmd_table_ptr;
cmd_list[0].clr_busy_on_ok = 1; fis_identify->fis_type = FIS_TYPE_REG_H2D;
cmd_list[0].cmd_fis_length = 4 + 4; //fis_identify->command = ATA_CMD_IDENTIFY;
cmd_list[0].ctba = pa_to_value(cmd_table_pa); fis_identify->command = ATA_CMD_READ_DMA_EXT;
cmd_list[0].prdtl = 1; fis_identify->device = (1 << 6);
fis_identify->lba0 = 0;
fis_identify->lba1 = 0;
fis_identify->lba2 = 0;
fis_identify->lba3 = 0;
fis_identify->lba4 = 0;
fis_identify->lba5 = 0;
fis_identify->countl = 1;
fis_identify->counth = 0;
fis_identify->c = 1;
port->cmd_table_ptr->prdt_entry[0].dba = pa_to_value(port->buffer_pa);
port->cmd_table_ptr->prdt_entry[0].dbc = 512 - 1;
// Start processing commands // Fill out the command header
hba_ports[port_num]->cmd |= PORT_CMD_START; memset((void *)&port->cmd_list_ptr[0], 0, sizeof port->cmd_list_ptr[0]);
port->cmd_list_ptr[0].clr_busy_on_ok = 1;
port->cmd_list_ptr[0].cmd_fis_length = 4 + 4;
port->cmd_list_ptr[0].ctba = pa_to_value(port->cmd_table_pa);
port->cmd_list_ptr[0].prdtl = 1;
// Only now can be submit the command // Start processing commands
hba_ports[port_num]->ci |= (1 << 0); port->port_mem->cmd |= PORT_CMD_START;
Time time = time_current(); // Only now can be submit the command
time.sec += 1; port->port_mem->ci |= (1 << 0);
timer_set_absolute(&hba_port_timers[port_num], time, sata_revisit_port);
Time time = time_current();
time.sec += 1;
timer_set_absolute(&port->timer, time, sata_revisit_port);
} }
void sata_init() void sata_init()
@ -407,8 +157,14 @@ void sata_init()
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (hba_mem->pi & (1 << i)) { if (hba_mem->pi & (1 << i)) {
hba_ports[i] = (void *)((uintptr_t)hba_mem + 0x100 + i * 0x80); ports[i].port_mem = (void *)((uintptr_t)hba_mem + 0x100 + i * 0x80);
sata_init_port(i); sata_init_port(i);
} }
} }
for (int i = 0; i < 32; i++) {
if (hba_mem->pi & (1 << i)) {
sata_submit_command(i);
}
}
} }