diff --git a/src/kernel/drives.c b/src/kernel/drives.c
new file mode 100644
index 0000000..377a856
--- /dev/null
+++ b/src/kernel/drives.c
@@ -0,0 +1,48 @@
+/* Calcite, src/kernel/drives.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 "utility.h"
+#include "drives.h"
+#include "heap.h"
+
+struct drive_info *drive_list;
+int drive_list_buffer_length = 0;
+int drive_list_count = 0;
+
+#define DRIVE_LIST_INITIAL_LENGTH 16
+
+struct drive_info *add_drive() {
+
+ if (drive_list_buffer_length == 0) {
+ drive_list = heap_alloc(DRIVE_LIST_INITIAL_LENGTH * sizeof(struct drive_info));
+ drive_list_buffer_length = DRIVE_LIST_INITIAL_LENGTH;
+ }
+
+ else if (drive_list_count == drive_list_buffer_length) {
+ struct drive_info *new_drive_list =
+ heap_alloc(2 * drive_list_buffer_length * sizeof(struct drive_info));
+ memcpy(new_drive_list, drive_list, drive_list_count * sizeof(struct drive_info));
+ heap_dealloc(drive_list, drive_list_count * sizeof(struct drive_info));
+ drive_list = new_drive_list;
+ drive_list_buffer_length *= 2;
+ }
+
+ struct drive_info *to_return = &drive_list[drive_list_count];
+ ++drive_list_count;
+ return to_return;
+
+}
diff --git a/src/kernel/drives.h b/src/kernel/drives.h
new file mode 100644
index 0000000..8eee84e
--- /dev/null
+++ b/src/kernel/drives.h
@@ -0,0 +1,43 @@
+/* Calcite, src/kernel/drives.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
+
+enum drive_access_result {
+ DAR_SUCCESS
+};
+
+struct drive_info {
+
+ const char *name;
+ uint64_t block_size;
+ uint64_t block_count;
+ const void *driver_info;
+
+ //start and count are both in blocks
+ enum drive_access_result (*read_blocks)(
+ const void *driver_info, void *buffer, uint64_t start, uint64_t count);
+
+};
+
+//add a drive by calling this and writing to the returned pointer.
+//the returned pointer might be invalidated by future calls.
+struct drive_info *add_drive();
+
+const struct drive_info *look_up_drive(const char *name);
diff --git a/src/kernel/entry.c b/src/kernel/entry.c
index 51fd941..14c1c65 100644
--- a/src/kernel/entry.c
+++ b/src/kernel/entry.c
@@ -15,9 +15,9 @@
* with this program. If not, see .
*/
+#include "kernel-public/syscall-numbers.h"
#include "framebuffer.h"
#include "interrupts.h"
-#include "kernel-public/syscall-numbers.h"
#include "scheduler.h"
#include "syscalls.h"
#include "process.h"
@@ -26,6 +26,7 @@
#include "paging.h"
#include "panic.h"
#include "heap.h"
+#include "pata.h"
#include "ps2.h"
#include
@@ -198,6 +199,10 @@ static uint64_t initfs_length;
register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread);
register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer);
+ //probe for drives
+
+ probe_pata_drives();
+
//load hello and start it
init_scheduler();
diff --git a/src/kernel/pata.asm b/src/kernel/pata.asm
new file mode 100644
index 0000000..a369e39
--- /dev/null
+++ b/src/kernel/pata.asm
@@ -0,0 +1,129 @@
+ ; 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
+
+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
new file mode 100644
index 0000000..dbde548
--- /dev/null
+++ b/src/kernel/pata.c
@@ -0,0 +1,165 @@
+/* Calcite, src/kernel/pata.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 "utility.h"
+#include "drives.h"
+#include "panic.h"
+#include "heap.h"
+#include "pata.h"
+
+//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) {
+ while (1) {
+ uint8_t status = pata_read_status(controller);
+ //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);
+ if (error == 0x04)
+ return PR_ABORTED;
+ panic("TODO")
+ }
+ if ((status & 0x80) || !(status & 0x08))
+ continue;
+ return PR_SUCCESS;
+ }
+}
+
+//buffer should have room for 256 words.
+static enum pata_result
+ pata_identify_packet_device(uint8_t drive, uint16_t *buffer) {
+
+ uint8_t controller = drive >> 1;
+
+ pata_set_device(controller, (drive & 0x01) << 4);
+ pata_set_command(controller, 0xa1);
+
+ enum pata_result result = wait_pio(controller);
+ if (result != PR_SUCCESS)
+ return result;
+
+ pata_read_data(controller, 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) {
+
+ uint8_t controller = drive >> 1;
+
+ 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);
+ 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);
+
+ result = wait_pio(controller);
+ if (result != PR_SUCCESS)
+ return result;
+
+ uint32_t actual_response_size = pata_read_lba(controller) >> 8;
+ if (actual_response_size != 8)
+ panic("TODO")
+
+ uint32_t response[2];
+ pata_read_data(controller, (uint16_t *)response, 4);
+
+ *max_block_out = end_swap_u32(response[0]);
+ *block_size_out = end_swap_u32(response[1]);
+ return PR_SUCCESS;
+
+}
+
+static uint16_t ipd_buffer[256];
+
+struct pata_driver_info {
+ uint8_t drive;
+};
+
+static enum drive_access_result read_blocks_patapi(
+ const struct pata_driver_info *driver_info,
+ void *buffer, uint64_t start, uint64_t count) {
+
+ panic("TODO");
+
+}
+
+static void probe_pata_drive(uint8_t drive) {
+
+ if (pata_identify_packet_device(drive, ipd_buffer) != PR_SUCCESS)
+ return;
+
+ uint32_t max_block, block_size;
+ if (patapi_read_capacity(drive, &max_block, &block_size) != PR_SUCCESS)
+ return;
+
+ struct drive_info *di = add_drive();
+
+ char *name = heap_alloc(6);
+ memcpy(name, "pata", 4);
+ name[4] = '0' + drive;
+ name[5] = 0;
+ di->name = name;
+
+ di->block_size = block_size;
+ di->block_count = max_block + 1;
+
+ struct pata_driver_info *driver_info =
+ heap_alloc(sizeof(struct pata_driver_info));
+ driver_info->drive = drive;
+ di->driver_info = driver_info;
+
+ di->read_blocks = (void *)&read_blocks_patapi;
+
+}
+
+void probe_pata_drives() {
+ for (int i = 0; i < 4; ++i)
+ probe_pata_drive(i);
+}
diff --git a/src/kernel/pata.h b/src/kernel/pata.h
new file mode 100644
index 0000000..ce98f93
--- /dev/null
+++ b/src/kernel/pata.h
@@ -0,0 +1,23 @@
+/* Calcite, src/kernel/pata.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
+
+//probes for all four pata drives and adds any that exist to drives.h
+void probe_pata_drives();
diff --git a/src/kernel/utility.c b/src/kernel/utility.c
index 742ac5d..605619f 100644
--- a/src/kernel/utility.c
+++ b/src/kernel/utility.c
@@ -37,3 +37,11 @@ void memzero(void *start, uint64_t bytes) {
for (uint64_t i = 0; i < bytes; ++i)
*(uint8_t *)(start + i) = 0;
}
+
+uint32_t end_swap_u32(uint32_t value) {
+ return
+ (value >> 24) |
+ (((value >> 16) & 0xff) << 8) |
+ (((value >> 8) & 0xff) << 16) |
+ ((value & 0xff) << 24);
+}
diff --git a/src/kernel/utility.h b/src/kernel/utility.h
index cc679a6..24b38e1 100644
--- a/src/kernel/utility.h
+++ b/src/kernel/utility.h
@@ -24,3 +24,6 @@ int strequ(const char *str1, const char *str2);
void memcpy(void *to, const void *from, uint64_t bytes);
void memzero(void *start, uint64_t bytes);
+
+//swaps the endianness of the value
+uint32_t end_swap_u32(uint32_t value);