From fc79e09922f6d274dccf52b7a9fb00424d26a8e8 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Sat, 27 Dec 2025 13:07:01 -0500 Subject: [PATCH] identify patapi drives --- src/kernel/drives.c | 48 +++++++++++++ src/kernel/drives.h | 43 +++++++++++ src/kernel/entry.c | 7 +- src/kernel/pata.asm | 129 +++++++++++++++++++++++++++++++++ src/kernel/pata.c | 165 +++++++++++++++++++++++++++++++++++++++++++ src/kernel/pata.h | 23 ++++++ src/kernel/utility.c | 8 +++ src/kernel/utility.h | 3 + 8 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 src/kernel/drives.c create mode 100644 src/kernel/drives.h create mode 100644 src/kernel/pata.asm create mode 100644 src/kernel/pata.c create mode 100644 src/kernel/pata.h 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);