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);