From 6d9c3f779453242da4fe11500cdc7c513a11d171 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Wed, 31 Dec 2025 18:16:05 -0500 Subject: [PATCH] get ide controllers from pci instead of assuming compatibility mode; name pata drives based on order discovered instead of controller --- disk/limine/limine.conf | 2 +- src/kernel/entry.c | 6 +- src/kernel/pata.asm | 130 ------------------------------------ src/kernel/pata.c | 144 ++++++++++++++++++++++------------------ src/kernel/pata.h | 4 +- src/kernel/pci.asm | 40 +++++++++++ src/kernel/pci.c | 59 ++++++++++++++++ src/kernel/pci.h | 32 +++++++++ src/kernel/utility.asm | 28 ++++++++ src/kernel/utility.h | 5 ++ 10 files changed, 251 insertions(+), 199 deletions(-) delete mode 100644 src/kernel/pata.asm create mode 100644 src/kernel/pci.asm create mode 100644 src/kernel/pci.c create mode 100644 src/kernel/pci.h diff --git a/disk/limine/limine.conf b/disk/limine/limine.conf index af0ec66..9dd1bae 100644 --- a/disk/limine/limine.conf +++ b/disk/limine/limine.conf @@ -4,4 +4,4 @@ quiet: yes /Calcite protocol: limine path: boot():/calcite/kernel.elf -cmdline: root-drive=pata2,root-fs=iso9660 +cmdline: root-drive=pata0,root-fs=iso9660 diff --git a/src/kernel/entry.c b/src/kernel/entry.c index 1b4ac20..1382190 100644 --- a/src/kernel/entry.c +++ b/src/kernel/entry.c @@ -27,7 +27,7 @@ #include "panic.h" #include "timer.h" #include "heap.h" -#include "pata.h" +#include "pci.h" #include "ps2.h" #include "fs.h" @@ -273,9 +273,9 @@ static const char *cmdline_look_up(const char *key) { register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread); register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar); - //probe for drives + //probe pci devices - probe_pata_drives(); + probe_pci(); //load root file system diff --git a/src/kernel/pata.asm b/src/kernel/pata.asm deleted file mode 100644 index f25e9ae..0000000 --- a/src/kernel/pata.asm +++ /dev/null @@ -1,130 +0,0 @@ - ; Calcite, src/kernel/pata.asm - ; Copyright 2025 Benji Dial - ; - ; This program is free software: you can redistribute it and/or modify - ; it under the terms of the GNU General Public License as published by - ; the Free Software Foundation, either version 3 of the License, or - ; (at your option) any later version. - ; - ; This program is distributed in the hope that it will be useful, but - ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - ; for more details. - ; - ; You should have received a copy of the GNU General Public License along - ; with this program. If not, see . - - -bits 64 -default rel - -section .text - -;referenced in pata.c -global pata_set_device -pata_set_device: - mov dx, 0x01f6 - shl dil, 7 - xor dl, dil - mov al, sil - out dx, al - ret - -;referenced in pata.c -global pata_set_features -pata_set_features: - mov dx, 0x01f1 - shl dil, 7 - xor dl, dil - mov al, sil - out dx, al - ret - -;referenced in pata.c -global pata_set_lba -pata_set_lba: - - mov dx, 0x01f3 - shl dil, 7 - xor dl, dil - mov eax, esi - out dx, al - - inc dx - shr eax, 8 - out dx, al - - inc dx - shr ax, 8 - out dx, al - - ret - -;referenced in pata.c -global pata_set_command -pata_set_command: - mov dx, 0x01f7 - shl dil, 7 - xor dl, dil - mov al, sil - out dx, al - ret - -;referenced in pata.c -global pata_read_status -pata_read_status: - mov dx, 0x01f7 - shl dil, 7 - xor dl, dil - in al, dx - ret - -;referenced in pata.c -global pata_read_error -pata_read_error: - mov dx, 0x01f1 - shl dil, 7 - xor dl, dil - in al, dx - ret - -;referenced in pata.c -global pata_read_lba -pata_read_lba: - - mov dx, 0x01f5 - shl dil, 7 - xor dl, dil - xor eax, eax - in al, dx - - dec dx - shl ax, 8 - in al, dx - - dec dx - shl eax, 8 - in al, dx - - ret - -;referenced in pata.c -global pata_read_data -pata_read_data: - movzx rcx, dx - mov dx, 0x01f0 - shl dil, 7 - xor dl, dil - mov rdi, rsi - rep insw - ret - -;referenced in pata.c -global pata_write_data -pata_write_data: - movzx rcx, dx - mov dx, 0x01f0 - shl dil, 7 - xor dl, dil - rep outsw - ret diff --git a/src/kernel/pata.c b/src/kernel/pata.c index 1264a69..4b1e496 100644 --- a/src/kernel/pata.c +++ b/src/kernel/pata.c @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "pci.h" #include "utility.h" #include "drives.h" #include "panic.h" @@ -22,41 +23,25 @@ #include "pata.h" //some relevant sources: +// https://www.isdaman.com/alsos/hardware/hdc/pciide.pdf // ANSI: AT Attachment 8 - ATA/ATAPI Command Set // Seagate: SCSI Commands Reference Manual // OSDev Wiki: ATAPI, PCI IDE Controller -//in any function that takes a "controller", 0 means the primary controller -//and 1 means the secondary controller. in any function that takes a "drive", -//bit 1 is the controller, and bit 0 is bit 4 of the device register. - -//all defined in pata.asm -void pata_set_device(uint8_t controller, uint8_t value); -void pata_set_features(uint8_t controller, uint8_t value); -void pata_set_lba(uint8_t controller, uint32_t value); -void pata_set_command(uint8_t controller, uint8_t value); -uint8_t pata_read_status(uint8_t controller); -uint8_t pata_read_error(uint8_t controller); -uint32_t pata_read_lba(uint8_t controller); -void pata_read_data( - uint8_t controller, uint16_t *buffer, uint16_t word_count); -void pata_write_data( - uint8_t controller, const uint16_t *buffer, uint16_t word_count); - enum pata_result { PR_SUCCESS, PR_NO_CONTROLLER, PR_ABORTED }; -enum pata_result wait_pio(uint8_t controller) { +enum pata_result wait_pio(uint16_t command_block_base) { while (1) { - uint8_t status = pata_read_status(controller); + uint8_t status = inb(command_block_base + 7); //0x00 happens in qemu, 0x7f happens in virtualbox if (status == 0x00 || status == 0x7f) return PR_NO_CONTROLLER; if (status & 0x01) { - uint8_t error = pata_read_error(controller); + uint8_t error = inb(command_block_base + 1); if (error == 0x04) return PR_ABORTED; panic("TODO") @@ -69,51 +54,50 @@ enum pata_result wait_pio(uint8_t controller) { //buffer should have room for 256 words. static enum pata_result - pata_identify_packet_device(uint8_t drive, uint16_t *buffer) { + pata_identify_packet_device(uint16_t command_block_base, uint8_t device_byte, uint16_t *buffer) { - uint8_t controller = drive >> 1; + outb(command_block_base + 6, device_byte); + outb(command_block_base + 7, 0xa1); - pata_set_device(controller, (drive & 0x01) << 4); - pata_set_command(controller, 0xa1); - - enum pata_result result = wait_pio(controller); + enum pata_result result = wait_pio(command_block_base); if (result != PR_SUCCESS) return result; - pata_read_data(controller, buffer, 256); + insw(command_block_base, buffer, 256); return PR_SUCCESS; } static enum pata_result patapi_read_capacity( - uint8_t drive, uint32_t *max_block_out, uint32_t *block_size_out) { + uint16_t command_block_base, uint8_t device_byte, uint32_t *max_block_out, uint32_t *block_size_out) { - uint8_t controller = drive >> 1; + outb(command_block_base + 6, device_byte); + outb(command_block_base + 1, 0); + outb(command_block_base + 3, 0); + outb(command_block_base + 4, 8); + outb(command_block_base + 5, 0); + outb(command_block_base + 7, 0xa0); - pata_set_device(controller, (drive & 0x01) << 4); - pata_set_features(controller, 0); - pata_set_lba(controller, 8 << 8); - pata_set_command(controller, 0xa0); - - enum pata_result result = wait_pio(controller); + enum pata_result result = wait_pio(command_block_base); if (result != PR_SUCCESS) return result; uint8_t cmd[12] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - pata_write_data(controller, (uint16_t *)cmd, 6); + outsw(command_block_base, cmd, 6); - result = wait_pio(controller); + result = wait_pio(command_block_base); if (result != PR_SUCCESS) return result; - uint32_t actual_response_size = pata_read_lba(controller) >> 8; + uint32_t actual_response_size = + inb(command_block_base + 4) | (inb(command_block_base + 5) << 8); if (actual_response_size != 8) panic("TODO") uint32_t response[2]; - pata_read_data(controller, (uint16_t *)response, 4); + insw(command_block_base, response, 4); *max_block_out = end_swap_u32(response[0]); *block_size_out = end_swap_u32(response[1]); @@ -122,16 +106,17 @@ static enum pata_result patapi_read_capacity( } static enum pata_result patapi_read( - uint8_t drive, uint16_t block_size, uint32_t start_block, uint32_t block_count, void *buffer) { + uint16_t command_block_base, uint8_t device_byte, uint16_t block_size, + uint32_t start_block, uint32_t block_count, void *buffer) { - uint8_t controller = drive >> 1; + outb(command_block_base + 6, device_byte); + outb(command_block_base + 1, 0); + outb(command_block_base + 3, 0); + outb(command_block_base + 4, block_size & 0xff); + outb(command_block_base + 5, block_size >> 8); + outb(command_block_base + 7, 0xa0); - pata_set_device(controller, (drive & 0x01) << 4); - pata_set_features(controller, 0); - pata_set_lba(controller, (uint32_t)block_size << 8); - pata_set_command(controller, 0xa0); - - enum pata_result result = wait_pio(controller); + enum pata_result result = wait_pio(command_block_base); if (result != PR_SUCCESS) return result; @@ -146,19 +131,21 @@ static enum pata_result patapi_read( (block_count >> 8) & 0xff, block_count & 0xff, 0, 0 }; - pata_write_data(controller, (uint16_t *)cmd, 8); + outsw(command_block_base, cmd, 6); for (uint32_t i = 0; i < block_count; ++i) { - result = wait_pio(controller); + result = wait_pio(command_block_base); if (result != PR_SUCCESS) return result; - uint32_t actual_response_size = pata_read_lba(controller) >> 8; + uint32_t actual_response_size = + inb(command_block_base + 4) | (inb(command_block_base + 5) << 8); + if (actual_response_size != block_size) panic("TODO") - pata_read_data(controller, (uint16_t *)(buffer + i * block_size), block_size / 2); + insw(command_block_base, buffer + i * block_size, block_size / 2); } @@ -169,7 +156,8 @@ static enum pata_result patapi_read( static uint16_t ipd_buffer[256]; struct pata_driver_info { - uint8_t drive; + uint16_t command_block_base; + uint8_t device_byte; }; static enum drive_access_result read_blocks_patapi( @@ -179,30 +167,36 @@ static enum drive_access_result read_blocks_patapi( if (count >= (1ULL << 32) || start >= (1ULL << 32)) panic("TODO") - uint8_t drive = ((const struct pata_driver_info *)drive_info->driver_info)->drive; + const struct pata_driver_info *driver_info = drive_info->driver_info; return patapi_read( - drive, drive_info->block_size, - start, count, buffer) == PR_SUCCESS + driver_info->command_block_base, driver_info->device_byte, + drive_info->block_size, start, count, buffer) == PR_SUCCESS ? DAR_SUCCESS : DAR_HARDWARE_ERROR; } -static void probe_pata_drive(uint8_t drive) { +int next_pata_drive_number = 0; - if (pata_identify_packet_device(drive, ipd_buffer) != PR_SUCCESS) +static void probe_pata_drive(uint16_t command_block_base, uint8_t device_byte) { + + if (pata_identify_packet_device(command_block_base, device_byte, ipd_buffer) != PR_SUCCESS) return; uint32_t max_block, block_size; - if (patapi_read_capacity(drive, &max_block, &block_size) != PR_SUCCESS) + if (patapi_read_capacity( + command_block_base, device_byte, &max_block, &block_size) != PR_SUCCESS) return; + if (next_pata_drive_number > 9) + panic("TODO") + struct drive_info *di = add_drive(); char *name = heap_alloc(6); memcpy(name, "pata", 4); - name[4] = '0' + drive; + name[4] = '0' + next_pata_drive_number++; name[5] = 0; di->name = name; @@ -211,14 +205,38 @@ static void probe_pata_drive(uint8_t drive) { struct pata_driver_info *driver_info = heap_alloc(sizeof(struct pata_driver_info)); - driver_info->drive = drive; + driver_info->command_block_base = command_block_base; + driver_info->device_byte = device_byte; di->driver_info = driver_info; di->read_blocks = &read_blocks_patapi; } -void probe_pata_drives() { - for (int i = 0; i < 4; ++i) - probe_pata_drive(i); +void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc) { + + if (pci_class_etc & 0x00000100) { + uint32_t bar = read_pci_config(pci_address_base + 0x10); + if ((bar & 0xffff0003) != 0x00000001) + panic("TODO") + probe_pata_drive(bar & 0xfffc, 0x00); + probe_pata_drive(bar & 0xfffc, 0x10); + } + else { + probe_pata_drive(0x01f0, 0x00); + probe_pata_drive(0x01f0, 0x10); + } + + if (pci_class_etc & 0x00000400) { + uint32_t bar = read_pci_config(pci_address_base + 0x18); + if ((bar & 0xffff0003) != 0x00000001) + panic("TODO") + probe_pata_drive(bar & 0xfffc, 0x00); + probe_pata_drive(bar & 0xfffc, 0x10); + } + else { + probe_pata_drive(0x0170, 0x00); + probe_pata_drive(0x0170, 0x10); + } + } diff --git a/src/kernel/pata.h b/src/kernel/pata.h index ce98f93..908e3ae 100644 --- a/src/kernel/pata.h +++ b/src/kernel/pata.h @@ -19,5 +19,5 @@ #include -//probes for all four pata drives and adds any that exist to drives.h -void probe_pata_drives(); +//probes for pata drives on this "card" and adds any that exist to drives.h +void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc); diff --git a/src/kernel/pci.asm b/src/kernel/pci.asm new file mode 100644 index 0000000..5f3c739 --- /dev/null +++ b/src/kernel/pci.asm @@ -0,0 +1,40 @@ + ; Calcite, src/kernel/pci.asm + ; Copyright 2025 Benji Dial + ; + ; This program is free software: you can redistribute it and/or modify + ; it under the terms of the GNU General Public License as published by + ; the Free Software Foundation, either version 3 of the License, or + ; (at your option) any later version. + ; + ; This program is distributed in the hope that it will be useful, but + ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + ; for more details. + ; + ; You should have received a copy of the GNU General Public License along + ; with this program. If not, see . + + +bits 64 +default rel + +section .text + +global read_pci_config +read_pci_config: + mov dx, 0x0cf8 + mov eax, edi + out dx, eax + mov dx, 0x0cfc + in eax, dx + ret + +global write_pci_config +write_pci_config: + mov dx, 0x0cf8 + mov eax, edi + out dx, eax + mov dx, 0x0cfc + mov eax, esi + out dx, eax + ret diff --git a/src/kernel/pci.c b/src/kernel/pci.c new file mode 100644 index 0000000..1f6eed1 --- /dev/null +++ b/src/kernel/pci.c @@ -0,0 +1,59 @@ +/* Calcite, src/kernel/pci.c + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pata.h" +#include "pci.h" + +#include + +static void probe_bus(uint32_t bus_address_base) { + + for (uint32_t device_address_base = bus_address_base; + device_address_base < bus_address_base + 0x00010000; + device_address_base += 0x00000800) + for (uint32_t function_address_base = device_address_base; + function_address_base < device_address_base + 0x00000800; + function_address_base += 0x00000100) { + + uint8_t header_type = (read_pci_config(function_address_base | 0x0c) >> 16) & 0xff; + if (header_type == 0xff) + break; + + if ((header_type & 0x7f) == 0x01) + //this is a pci-to-pci bridge + probe_bus((read_pci_config(function_address_base | 0x18) >> 8) & 0xff); + + else if ((header_type & 0x7f) == 0x00) { + //this is a normal function + uint32_t class_etc = read_pci_config(function_address_base | 0x08); + switch (class_etc & 0xffff0000) { + case 0x01010000: + probe_pata_drives(function_address_base, class_etc); + break; + } + } + + if (!(header_type & 0x80)) + break; + + } + +} + +void probe_pci() { + probe_bus(0x80000000); +} diff --git a/src/kernel/pci.h b/src/kernel/pci.h new file mode 100644 index 0000000..dc1ddc1 --- /dev/null +++ b/src/kernel/pci.h @@ -0,0 +1,32 @@ +/* Calcite, src/kernel/pci.h + * Copyright 2025 Benji Dial + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#pragma once + +#include + +//reads one dword from config space. +//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space). +//bytes into that function's space must be dword-aligned. +uint32_t read_pci_config(uint32_t address); + +//writes one dword to config space. +//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space). +//bytes into that function's space must be dword-aligned. +void write_pci_config(uint32_t address, uint32_t value); + +void probe_pci(); diff --git a/src/kernel/utility.asm b/src/kernel/utility.asm index c8e0b98..a29b82d 100644 --- a/src/kernel/utility.asm +++ b/src/kernel/utility.asm @@ -32,3 +32,31 @@ memzero: mov rcx, rsi rep stosb ret + +global outb +outb: + mov dx, di + mov al, sil + out dx, al + ret + +global inb +inb: + mov dx, di + in al, dx + ret + +global outsw +outsw: + mov rcx, rdx + mov dx, di + rep outsw + ret + +global insw +insw: + mov rcx, rdx + mov dx, di + mov rdi, rsi + rep insw + ret diff --git a/src/kernel/utility.h b/src/kernel/utility.h index a7d99db..6bde244 100644 --- a/src/kernel/utility.h +++ b/src/kernel/utility.h @@ -34,3 +34,8 @@ uint32_t end_swap_u32(uint32_t value); //4. zeroes rest of new buffer //5. sets buffer and length to new buffer and twice length void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry); + +void outb(uint16_t port, uint8_t byte); +uint8_t inb(uint16_t port); +void outsw(uint16_t port, const void *buffer, uint64_t words); +void insw(uint16_t port, void *buffer, uint64_t words);