identify patapi drives

This commit is contained in:
Benji Dial 2025-12-27 13:07:01 -05:00
parent f409903f55
commit fc79e09922
8 changed files with 425 additions and 1 deletions

48
src/kernel/drives.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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;
}

43
src/kernel/drives.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
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);

View file

@ -15,9 +15,9 @@
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#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 <limine.h>
@ -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();

129
src/kernel/pata.asm Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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

165
src/kernel/pata.c Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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);
}

23
src/kernel/pata.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
//probes for all four pata drives and adds any that exist to drives.h
void probe_pata_drives();

View file

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

View file

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