get ide controllers from pci instead of assuming compatibility mode; name pata drives based on order discovered instead of controller

This commit is contained in:
Benji Dial 2025-12-31 18:16:05 -05:00
parent 7d90ac7d3d
commit 6d9c3f7794
10 changed files with 251 additions and 199 deletions

View file

@ -4,4 +4,4 @@ quiet: yes
/Calcite /Calcite
protocol: limine protocol: limine
path: boot():/calcite/kernel.elf path: boot():/calcite/kernel.elf
cmdline: root-drive=pata2,root-fs=iso9660 cmdline: root-drive=pata0,root-fs=iso9660

View file

@ -27,7 +27,7 @@
#include "panic.h" #include "panic.h"
#include "timer.h" #include "timer.h"
#include "heap.h" #include "heap.h"
#include "pata.h" #include "pci.h"
#include "ps2.h" #include "ps2.h"
#include "fs.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_CREATE_THREAD, (void *)&syscall_create_thread);
register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar); register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar);
//probe for drives //probe pci devices
probe_pata_drives(); probe_pci();
//load root file system //load root file system

View file

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

View file

@ -15,6 +15,7 @@
* with this program. If not, see <https://www.gnu.org/licenses/>. * with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "pci.h"
#include "utility.h" #include "utility.h"
#include "drives.h" #include "drives.h"
#include "panic.h" #include "panic.h"
@ -22,41 +23,25 @@
#include "pata.h" #include "pata.h"
//some relevant sources: //some relevant sources:
// https://www.isdaman.com/alsos/hardware/hdc/pciide.pdf
// ANSI: AT Attachment 8 - ATA/ATAPI Command Set // ANSI: AT Attachment 8 - ATA/ATAPI Command Set
// Seagate: SCSI Commands Reference Manual // Seagate: SCSI Commands Reference Manual
// OSDev Wiki: ATAPI, PCI IDE Controller // 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 { enum pata_result {
PR_SUCCESS, PR_SUCCESS,
PR_NO_CONTROLLER, PR_NO_CONTROLLER,
PR_ABORTED PR_ABORTED
}; };
enum pata_result wait_pio(uint8_t controller) { enum pata_result wait_pio(uint16_t command_block_base) {
while (1) { 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 //0x00 happens in qemu, 0x7f happens in virtualbox
if (status == 0x00 || status == 0x7f) if (status == 0x00 || status == 0x7f)
return PR_NO_CONTROLLER; return PR_NO_CONTROLLER;
if (status & 0x01) { if (status & 0x01) {
uint8_t error = pata_read_error(controller); uint8_t error = inb(command_block_base + 1);
if (error == 0x04) if (error == 0x04)
return PR_ABORTED; return PR_ABORTED;
panic("TODO") panic("TODO")
@ -69,51 +54,50 @@ enum pata_result wait_pio(uint8_t controller) {
//buffer should have room for 256 words. //buffer should have room for 256 words.
static enum pata_result 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); enum pata_result result = wait_pio(command_block_base);
pata_set_command(controller, 0xa1);
enum pata_result result = wait_pio(controller);
if (result != PR_SUCCESS) if (result != PR_SUCCESS)
return result; return result;
pata_read_data(controller, buffer, 256); insw(command_block_base, buffer, 256);
return PR_SUCCESS; return PR_SUCCESS;
} }
static enum pata_result patapi_read_capacity( 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); enum pata_result result = wait_pio(command_block_base);
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) if (result != PR_SUCCESS)
return result; return result;
uint8_t cmd[12] = { uint8_t cmd[12] = {
0x25, 0, 0, 0, 0, 0, 0x25, 0, 0, 0, 0, 0,
0, 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) if (result != PR_SUCCESS)
return result; 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) if (actual_response_size != 8)
panic("TODO") panic("TODO")
uint32_t response[2]; 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]); *max_block_out = end_swap_u32(response[0]);
*block_size_out = end_swap_u32(response[1]); *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( 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); enum pata_result result = wait_pio(command_block_base);
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);
if (result != PR_SUCCESS) if (result != PR_SUCCESS)
return result; return result;
@ -146,19 +131,21 @@ static enum pata_result patapi_read(
(block_count >> 8) & 0xff, (block_count >> 8) & 0xff,
block_count & 0xff, block_count & 0xff,
0, 0 }; 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) { for (uint32_t i = 0; i < block_count; ++i) {
result = wait_pio(controller); result = wait_pio(command_block_base);
if (result != PR_SUCCESS) if (result != PR_SUCCESS)
return result; 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) if (actual_response_size != block_size)
panic("TODO") 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]; static uint16_t ipd_buffer[256];
struct pata_driver_info { struct pata_driver_info {
uint8_t drive; uint16_t command_block_base;
uint8_t device_byte;
}; };
static enum drive_access_result read_blocks_patapi( 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)) if (count >= (1ULL << 32) || start >= (1ULL << 32))
panic("TODO") 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 return
patapi_read( patapi_read(
drive, drive_info->block_size, driver_info->command_block_base, driver_info->device_byte,
start, count, buffer) == PR_SUCCESS drive_info->block_size, start, count, buffer) == PR_SUCCESS
? DAR_SUCCESS : DAR_HARDWARE_ERROR; ? 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; return;
uint32_t max_block, block_size; 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; return;
if (next_pata_drive_number > 9)
panic("TODO")
struct drive_info *di = add_drive(); struct drive_info *di = add_drive();
char *name = heap_alloc(6); char *name = heap_alloc(6);
memcpy(name, "pata", 4); memcpy(name, "pata", 4);
name[4] = '0' + drive; name[4] = '0' + next_pata_drive_number++;
name[5] = 0; name[5] = 0;
di->name = name; di->name = name;
@ -211,14 +205,38 @@ static void probe_pata_drive(uint8_t drive) {
struct pata_driver_info *driver_info = struct pata_driver_info *driver_info =
heap_alloc(sizeof(struct pata_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->driver_info = driver_info;
di->read_blocks = &read_blocks_patapi; di->read_blocks = &read_blocks_patapi;
} }
void probe_pata_drives() { void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc) {
for (int i = 0; i < 4; ++i)
probe_pata_drive(i); 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);
}
} }

View file

@ -19,5 +19,5 @@
#include <stdint.h> #include <stdint.h>
//probes for all four pata drives and adds any that exist to drives.h //probes for pata drives on this "card" and adds any that exist to drives.h
void probe_pata_drives(); void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc);

40
src/kernel/pci.asm Normal file
View file

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

59
src/kernel/pci.c Normal file
View file

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

32
src/kernel/pci.h Normal file
View file

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

View file

@ -32,3 +32,31 @@ memzero:
mov rcx, rsi mov rcx, rsi
rep stosb rep stosb
ret 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

View file

@ -34,3 +34,8 @@ uint32_t end_swap_u32(uint32_t value);
//4. zeroes rest of new buffer //4. zeroes rest of new buffer
//5. sets buffer and length to new buffer and twice length //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 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);