diff --git a/disk/limine/limine.conf b/disk/limine/limine.conf index b82e1bb..af0ec66 100644 --- a/disk/limine/limine.conf +++ b/disk/limine/limine.conf @@ -2,7 +2,6 @@ timeout: 0 quiet: yes /Calcite -protocol: limine -path: boot():/calcite/kernel.elf -module_path: boot():/calcite/initfs.tar -module_cmdline: initfs +protocol: limine +path: boot():/calcite/kernel.elf +cmdline: root-drive=pata2,root-fs=iso9660 diff --git a/make-build.sh b/make-build.sh index 17a1e28..8c44eb2 100644 --- a/make-build.sh +++ b/make-build.sh @@ -69,7 +69,8 @@ for dir in src/user-libs/*; do build_all $dir user_cc user_lib_ld lib$lib_name.o done -apps="" +cp_apps="" +app_deps="" for dir in src/user-apps/*; do @@ -81,21 +82,18 @@ for dir in src/user-apps/*; do app_name=$(echo $dir | sed -e 's/^src\/user-apps\///') build_all $dir user_cc user_app_ld $app_name.elf $lib_paths - build_dir=$(echo $dir | sed -e 's/^src/build/') - apps="$apps $build_dir/$app_name.elf" + + build_elf=$(echo $dir | sed -e 's/^src/build/')/$app_name.elf + disk_dir=build/disk/calcite/apps/$app_name + disk_elf=$disk_dir/$app_name.elf + + cp_apps=$(echo "$cp_apps" \ + "mkdir -p $disk_dir &&" \ + "cp $build_elf $disk_elf &&") + app_deps="$app_deps $build_elf" done -echo "rule initfs" >> build.ninja -echo " command =" \ - "rm -rf build/initfs &&" \ - "mkdir -p build/initfs/apps &&" \ - "cp build/user-apps/hello/hello.elf build/initfs/apps/ &&" \ - "cd build/initfs &&" \ - "tar cf ../initfs.tar *" >> build.ninja - -echo "build build/initfs.tar: initfs $apps" >> build.ninja - echo "rule disk" >> build.ninja echo " command =" \ "rm -rf build/disk &&" \ @@ -107,10 +105,10 @@ echo " command =" \ "cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \ "mkdir build/disk/calcite &&" \ "cp build/kernel/kernel.elf build/disk/calcite/ &&" \ - "cp build/initfs.tar build/disk/calcite/ &&" \ + $cp_apps \ "xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label build/disk -o build/disk.iso &&" \ "dependencies/limine/limine bios-install build/disk.iso" >> build.ninja -echo "build build/disk.iso: disk | build/kernel/kernel.elf build/initfs.tar" >> build.ninja +echo "build build/disk.iso: disk | build/kernel/kernel.elf $app_deps" >> build.ninja echo "default build/disk.iso" >> build.ninja diff --git a/src/kernel/drives.c b/src/kernel/drives.c index 377a856..422cf8b 100644 --- a/src/kernel/drives.c +++ b/src/kernel/drives.c @@ -46,3 +46,10 @@ struct drive_info *add_drive() { return to_return; } + +const struct drive_info *look_up_drive(const char *name) { + for (int i = 0; i < drive_list_count; ++i) + if (strequ(drive_list[i].name, name)) + return &drive_list[i]; + return 0; +} diff --git a/src/kernel/drives.h b/src/kernel/drives.h index 8eee84e..ee3992a 100644 --- a/src/kernel/drives.h +++ b/src/kernel/drives.h @@ -20,7 +20,8 @@ #include enum drive_access_result { - DAR_SUCCESS + DAR_SUCCESS, + DAR_HARDWARE_ERROR }; struct drive_info { @@ -32,7 +33,7 @@ struct drive_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); + const struct drive_info *info, void *buffer, uint64_t start, uint64_t count); }; @@ -40,4 +41,6 @@ struct drive_info { //the returned pointer might be invalidated by future calls. struct drive_info *add_drive(); +//returned pointer might be invalidated by future calls to add_drive. +//returns 0 if there is no drive with that name. const struct drive_info *look_up_drive(const char *name); diff --git a/src/kernel/entry.c b/src/kernel/entry.c index 14c1c65..49506a8 100644 --- a/src/kernel/entry.c +++ b/src/kernel/entry.c @@ -22,12 +22,13 @@ #include "syscalls.h" #include "process.h" #include "utility.h" -#include "initfs.h" +#include "drives.h" #include "paging.h" #include "panic.h" #include "heap.h" #include "pata.h" #include "ps2.h" +#include "fs.h" #include @@ -57,8 +58,8 @@ static volatile struct limine_memmap_request memmap_request = { .response = 0 }; -static volatile struct limine_module_request module_request = { - .id = LIMINE_MODULE_REQUEST, +static volatile struct limine_executable_cmdline_request cmdline_request = { + .id = LIMINE_EXECUTABLE_CMDLINE_REQUEST, .revision = 0, .response = 0 }; @@ -84,8 +85,8 @@ static void map_kernel_region( physical_start + i, (uint8_t *)virtual_start + i, writable, executable); } -static uint8_t *initfs_start; -static uint64_t initfs_length; +#define MAX_CMDLINE_LENGTH 1000 +static char cmdline_copy[MAX_CMDLINE_LENGTH + 1]; [[noreturn]] static void with_kernel_page_tables(); @@ -95,9 +96,7 @@ static uint64_t initfs_length; if (fb_request.response == 0 || hhdm_request.response == 0 || ka_request.response == 0 || memmap_request.response == 0 || - module_request.response == 0 || - fb_request.response->framebuffer_count == 0 || - module_request.response->module_count == 0) + fb_request.response->framebuffer_count == 0) die(); struct limine_framebuffer *fb = fb_request.response->framebuffers[0]; @@ -107,19 +106,6 @@ static uint64_t initfs_length; fb->blue_mask_shift != 0 || fb->blue_mask_size != 8) die(); - //find the initfs, and die if we don't have one. - - struct limine_file *initfs = 0; - struct limine_module_response *response = module_request.response; - for (uint64_t i = 0; i < response->module_count; ++i) - if (strequ(response->modules[i]->cmdline, "initfs")) { - initfs = response->modules[i]; - break; - } - - if (!initfs) - die(); - //set up page tables. we will mark the regions with bootloader structures as //usable, so we need to be careful not to allocate any physical pages until //after we are done using bootloader structures. we map the kernel into our @@ -162,17 +148,17 @@ static uint64_t initfs_length; fb_height = fb->height; fb_pitch = fb->pitch; - //remap initfs module and store information. + //make a copy of the kernel cmdline - uint64_t initfs_physical_start = - (uint64_t)initfs->address - hhdm_request.response->offset; + const char *original_cmdline = cmdline_request.response->cmdline; + int cmdline_length = 0; + while (original_cmdline[cmdline_length] != 0) + ++cmdline_length; - initfs_length = initfs->size; - uint64_t initfs_length_rounded = ((initfs_length - 1) / 4096 + 1) * 4096; + if (cmdline_length > MAX_CMDLINE_LENGTH) + die(); - initfs_start = find_free_kernel_region(initfs_length_rounded); - map_kernel_region( - initfs_physical_start, initfs_start, initfs_length_rounded, 0, 0); + memcpy(cmdline_copy, original_cmdline, cmdline_length + 1); //switch to kernel page tables! @@ -180,11 +166,78 @@ static uint64_t initfs_length; } +struct cmdline_pair { + const char *key; + const char *value; +}; + +static const struct cmdline_pair *cmdline_pairs; +static int cmdline_pair_count; + +//returns the corresponding value if the key exists, otherwise returns 0. +static const char *cmdline_look_up(const char *key) { + for (int i = 0; i < cmdline_pair_count; ++i) + if (strequ(cmdline_pairs[i].key, key)) + return cmdline_pairs[i].value; + return 0; +} + [[noreturn]] static void with_kernel_page_tables() { - //set initfs + //store cmdline as key-value pairs - set_initfs(initfs_start, initfs_length); + if (cmdline_copy[0] == 0) + cmdline_pair_count = 0; + + else { + + int comma_count = 0; + for (int i = 0; cmdline_copy[i] != 0; ++i) + if (cmdline_copy[i] == ',') + ++comma_count; + + cmdline_pair_count = comma_count + 1; + struct cmdline_pair *pairs = heap_alloc(cmdline_pair_count * sizeof(struct cmdline_pair)); + + int key_start = 0; + for (int i = 0; i < cmdline_pair_count; ++i) { + + int key_end = key_start; + while (1) { + if (cmdline_copy[key_end] == '=') + break; + if (cmdline_copy[key_end] == ',' || cmdline_copy[key_end] == 0) + die(); + ++key_end; + } + + int value_start = key_end + 1; + int value_end = value_start; + while (1) { + if (cmdline_copy[value_end] == ',' || cmdline_copy[value_end] == 0) + break; + if (cmdline_copy[value_end] == '=') + die(); + ++value_end; + } + + char *key = heap_alloc(key_end - key_start + 1); + memcpy(key, &cmdline_copy[key_start], key_end - key_start); + key[key_end - key_start] = 0; + pairs[i].key = key; + + char *value = heap_alloc(value_end - value_start + 1); + memcpy(value, &cmdline_copy[value_start], value_end - value_start); + value[value_end - value_start] = 0; + pairs[i].value = value; + + key_start = value_end + 1; + + } + + cmdline_pairs = pairs; + + } //set up interrupts @@ -203,18 +256,40 @@ static uint64_t initfs_length; probe_pata_drives(); + //load root file system + + const char *root_drive_name = cmdline_look_up("root-drive"); + if (!root_drive_name) + panic("root drive mising from cmdline") + const struct drive_info *root_drive = look_up_drive(root_drive_name); + if (!root_drive) + panic("could not find root drive") + + const char *root_fs_type = cmdline_look_up("root-fs"); + if (!root_fs_type) + panic("root fs missing from cmdline") + struct fs_info root_fs; + if (create_fs_info(root_drive, &root_fs, root_fs_type) != FAR_SUCCESS) + panic("could not create root file system info") + //load hello and start it init_scheduler(); - const uint8_t *hello_start; - uint64_t hello_length; - look_up_initfs_file("apps/hello.elf", &hello_start, &hello_length); + void *hello_node; + if ((*root_fs.look_up_file)( + &root_fs, &hello_node, + "calcite/apps/hello/hello.elf") != FAR_SUCCESS) + panic("could not look up hello.elf") struct process *hello = heap_alloc(sizeof(struct process)); uint64_t hello_entry; create_process(hello); - load_elf(hello, &hello_entry, hello_start, hello_length); + if (load_elf(hello, &hello_entry, &root_fs, hello_node) != 1) + panic("could not load hello.elf") + + if ((*root_fs.free_node)(&root_fs, hello_node) != FAR_SUCCESS) + panic("could not free hello.elf node") struct thread *hello_thread = heap_alloc(sizeof(struct thread)); create_thread(hello, hello_thread); diff --git a/src/kernel/fs.c b/src/kernel/fs.c new file mode 100644 index 0000000..583908c --- /dev/null +++ b/src/kernel/fs.c @@ -0,0 +1,30 @@ +/* Calcite, src/kernel/fs.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 "iso9660.h" +#include "utility.h" +#include "fs.h" + +enum fs_access_result create_fs_info( + const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type) { + + if (strequ(fs_type, "iso9660")) + return create_iso9660_info(drive, fs_out); + + return FAR_UNKNOWN_FS_TYPE; + +} diff --git a/src/kernel/fs.h b/src/kernel/fs.h new file mode 100644 index 0000000..7dc1886 --- /dev/null +++ b/src/kernel/fs.h @@ -0,0 +1,58 @@ +/* Calcite, src/kernel/fs.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 "drives.h" + +#include + +enum fs_access_result { + FAR_SUCCESS, + FAR_HARDWARE_ERROR, + FAR_FORMAT_ERROR, + FAR_UNKNOWN_FS_TYPE, + FAR_NOT_FOUND +}; + +struct fs_stat { + uint64_t bytes; +}; + +struct fs_info { + + const struct drive_info *drive; + const void *driver_info; + + enum fs_access_result (*free_node)( + const struct fs_info *info, void *node); + + enum fs_access_result (*look_up_file)( + const struct fs_info *info, void **node_out, const char *path); + + enum fs_access_result (*stat_file)( + const struct fs_info *info, void *node, struct fs_stat *stat_out); + + //start is in bytes + enum fs_access_result (*read_file)( + const struct fs_info *info, void *node, + void *buffer, uint64_t start, uint64_t bytes); + +}; + +enum fs_access_result create_fs_info( + const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type); diff --git a/src/kernel/initfs.c b/src/kernel/initfs.c deleted file mode 100644 index 9cf3fed..0000000 --- a/src/kernel/initfs.c +++ /dev/null @@ -1,74 +0,0 @@ -/* Calcite, src/kernel/initfs.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 "initfs.h" - -static const uint8_t *initfs_start; -static uint64_t initfs_length; - -void set_initfs(const uint8_t *start, uint64_t length) { - initfs_start = start; - initfs_length = length; -} - -static uint64_t decode_octal(const char *from) { - uint64_t value = 0; - while (*from != '\0') { - value = value * 8 + *from - '0'; - ++from; - } - return value; -} - -void look_up_initfs_file( - const char *path, const uint8_t **start_out, uint64_t *length_out) { - - int path_len = 0; - while (path[path_len] != '\0') - ++path_len; - - uint64_t offset = 0; - - while (1) { - - if (offset + 512 > initfs_length) { - *start_out = 0; - return; - } - - uint64_t length = - decode_octal((const char *)(initfs_start + offset + 0x7c)); - - int found_it = 1; - - for (int i = 0; i < path_len; ++i) - if (initfs_start[offset + i] != path[i]) { - found_it = 0; - break; - } - - if (found_it) { - *start_out = initfs_start + offset + 512; - *length_out = length; - return; - } - - offset += 512 + ((length - 1) / 512 + 1) * 512; - - } - -} diff --git a/src/kernel/iso9660.c b/src/kernel/iso9660.c new file mode 100644 index 0000000..6e7b83f --- /dev/null +++ b/src/kernel/iso9660.c @@ -0,0 +1,375 @@ +/* Calcite, src/kernel/iso9660.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 "iso9660.h" +#include "utility.h" +#include "drives.h" +#include "panic.h" +#include "heap.h" +#include "fs.h" + +//relevant sources: +// https://www.ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf + +//case-insensitive equality +static bool ciequ(char c1, char c2) { + if (c1 >= 'a' && c1 <= 'z') + c1 &= 0xdf; + if (c2 >= 'a' && c2 <= 'z') + c2 &= 0xdf; + return c1 == c2; +} + +struct iso9660_driver_info { + const void *path_table; + //in bytes + int path_table_length; +}; + +static const struct iso9660_driver_info *get_driver_info(const struct fs_info *info) { + return (const struct iso9660_driver_info *)info->driver_info; +} + +struct iso9660_node { + uint32_t first_block; + //in bytes + uint32_t length; +}; + +static enum fs_access_result free_node_iso9660(const struct fs_info *info, void *node) { + (void)info; + heap_dealloc(node, sizeof(struct iso9660_node)); + return FAR_SUCCESS; +} + +static uint8_t directory_buffer[2048]; + +static enum fs_access_result look_up_in_directory( + const struct fs_info *info, void **node_out, + const char *path, int path_length, + uint32_t first_directory_block) { + + if (first_directory_block >= info->drive->block_count) + return FAR_FORMAT_ERROR; + + switch ((*info->drive->read_blocks)(info->drive, directory_buffer, first_directory_block, 1)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + + int length = *(uint32_t *)&directory_buffer[10]; + if (length % 2048 != 0) + return FAR_FORMAT_ERROR; + + uint32_t loaded_block = first_directory_block; + int offset_in_block = 0; + int total_offset = 0; + + while (1) { + + if (total_offset == length) + return FAR_NOT_FOUND; + + if (offset_in_block == 2048 || directory_buffer[offset_in_block] == 0) { + ++loaded_block; + if (loaded_block >= info->drive->block_count) + return FAR_FORMAT_ERROR; + switch ((*info->drive->read_blocks)(info->drive, directory_buffer, loaded_block, 1)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + total_offset = total_offset - offset_in_block + 2048; + offset_in_block = 0; + if (total_offset == length) + return FAR_NOT_FOUND; + } + + int dr_length = directory_buffer[offset_in_block]; + if (offset_in_block + dr_length > 2048) + return FAR_FORMAT_ERROR; + + int id_length = directory_buffer[offset_in_block + 32]; + if (33 + id_length > dr_length) + return FAR_FORMAT_ERROR; + + for (int i = 0; i < id_length; ++i) + if (directory_buffer[offset_in_block + 33 + i] == ';') { + id_length = i; + break; + } + + if (id_length != path_length) + goto next_entry; + + for (int i = 0; i < id_length; ++i) + if (!ciequ(directory_buffer[offset_in_block + 33 + i], path[i])) + goto next_entry; + + struct iso9660_node *node = heap_alloc(sizeof(struct iso9660_node)); + node->first_block = *(uint32_t *)&directory_buffer[offset_in_block + 2]; + node->length = *(uint32_t *)&directory_buffer[offset_in_block + 10]; + + *node_out = node; + return FAR_SUCCESS; + + next_entry: + offset_in_block += dr_length; + total_offset += dr_length; + + } + +} + +static enum fs_access_result look_up_recursive( + const struct fs_info *info, void **node_out, + const char *path, int parent_path_table_offset, + int parent_directory_number) { + + //FIXME: this function is pretty unoptimized + +recurse: + + int slash_location = 0; + int path_length; + while (1) { + if (path[slash_location] == '/') + break; + if (path[slash_location] == 0) { + path_length = slash_location; + slash_location = -1; + break; + } + ++slash_location; + } + + const struct iso9660_driver_info *driver_info = get_driver_info(info); + + int on_offset = parent_path_table_offset; + int on_number = parent_directory_number; + + if (slash_location == -1) { + if (on_offset + 6 > driver_info->path_table_length) + return FAR_NOT_FOUND; + uint32_t first_directory_block = *(uint32_t *)(driver_info->path_table + on_offset + 2); + return look_up_in_directory(info, node_out, path, path_length, first_directory_block); + } + + while (1) { + + if (on_offset >= driver_info->path_table_length) + return FAR_NOT_FOUND; + + int old_id_length = *(uint8_t *)(driver_info->path_table + on_offset); + on_offset += 8 + old_id_length + (old_id_length & 0x01); + ++on_number; + + if (on_offset + 8 > driver_info->path_table_length) + return FAR_NOT_FOUND; + + int this_entry_parent = *(uint16_t *)(driver_info->path_table + on_offset + 6); + if (this_entry_parent != parent_directory_number) + continue; + + int this_entry_id_length = *(uint8_t *)(driver_info->path_table + on_offset); + if (this_entry_id_length != slash_location) + continue; + + if (on_offset + 8 + this_entry_id_length > driver_info->path_table_length) + return FAR_NOT_FOUND; + + char *this_entry_id = (char *)(driver_info->path_table + on_offset + 8); + for (int i = 0; i < slash_location; ++i) + if (!ciequ(this_entry_id[i], path[i])) + goto next_entry; + + path = path + slash_location + 1; + parent_path_table_offset = on_offset; + parent_directory_number = on_number; + goto recurse; + + next_entry: + ; + + } + +} + +static enum fs_access_result look_up_file_iso9660( + const struct fs_info *info, void **node_out, const char *path) { + + return look_up_recursive(info, node_out, path, 0, 1); + +} + +static enum fs_access_result stat_file_iso9660( + const struct fs_info *info, void *node, struct fs_stat *stat_out) { + + (void)info; + struct iso9660_node *iso9660_node = (struct iso9660_node *)node; + stat_out->bytes = iso9660_node->length; + return FAR_SUCCESS; + +} + +static uint8_t read_buffer[2048]; + +static enum fs_access_result read_file_iso9660( + const struct fs_info *info, void *node, void *buffer, uint64_t start, uint64_t bytes) { + + struct iso9660_node *iso9660_node = (struct iso9660_node *)node; + if (iso9660_node->first_block + (start + bytes - 1) / 2048 + 1 > info->drive->block_count) + return FAR_FORMAT_ERROR; + + if (start % 2048 != 0) { + + switch ((*info->drive->read_blocks)( + info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + + if (start + bytes <= ((start - 1) / 2048 + 1) * 2048) { + memcpy(buffer, read_buffer + (start % 2048), bytes); + return FAR_SUCCESS; + } + + int to_copy = 2048 - (start % 2048); + memcpy(buffer, read_buffer + (start % 2048), to_copy); + buffer += to_copy; + start += to_copy; + bytes -= to_copy; + + } + + switch ((*info->drive->read_blocks)( + info->drive, buffer, + iso9660_node->first_block + start / 2048, + bytes / 2048)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + + buffer += (bytes / 2048) * 2048; + start += (bytes / 2048) * 2048; + bytes %= 2048; + + if (bytes > 0) { + + switch ((*info->drive->read_blocks)( + info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + + memcpy(buffer, read_buffer + (start % 2048), bytes); + + } + + return FAR_SUCCESS; + +} + +static uint8_t vd_buffer[2048]; + +enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out) { + + if (drive->block_size != 2048) + panic("TODO") + + int descriptor_number = 0; + while (1) { + + if ((uint64_t)(16 + descriptor_number) >= drive->block_count) + return FAR_FORMAT_ERROR; + + switch ((*drive->read_blocks)(drive, vd_buffer, 16 + descriptor_number, 1)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + return FAR_HARDWARE_ERROR; + default: + assert(0) + } + + if (vd_buffer[0] == 0xff) + return FAR_FORMAT_ERROR; + + if (vd_buffer[0] == 0x01) + break; + + ++descriptor_number; + + } + + uint32_t path_table_length = *(uint32_t *)&vd_buffer[132]; + uint32_t path_table_start = *(uint32_t *)&vd_buffer[140]; + + uint64_t path_table_length_rounded_up = ((path_table_length - 1) / 2048 + 1) * 2048; + void *path_table = heap_alloc(path_table_length_rounded_up); + + if (path_table_start >= drive->block_count) + return FAR_FORMAT_ERROR; + + switch ((*drive->read_blocks)( + drive, path_table, path_table_start, + path_table_length_rounded_up / 2048)) { + case DAR_SUCCESS: + break; + case DAR_HARDWARE_ERROR: + heap_dealloc(path_table, path_table_length_rounded_up); + return FAR_HARDWARE_ERROR; + default: + assert(0); + } + + heap_dealloc( + path_table + path_table_length, + path_table_length_rounded_up - path_table_length); + + struct iso9660_driver_info *driver_info = heap_alloc(sizeof(struct iso9660_driver_info)); + driver_info->path_table = path_table; + driver_info->path_table_length = path_table_length; + + fs_out->drive = drive; + fs_out->driver_info = driver_info; + fs_out->free_node = &free_node_iso9660; + fs_out->look_up_file = &look_up_file_iso9660; + fs_out->stat_file = &stat_file_iso9660; + fs_out->read_file = &read_file_iso9660; + return FAR_SUCCESS; + +} diff --git a/src/kernel/initfs.h b/src/kernel/iso9660.h similarity index 71% rename from src/kernel/initfs.h rename to src/kernel/iso9660.h index e03f7fa..be3f5c7 100644 --- a/src/kernel/initfs.h +++ b/src/kernel/iso9660.h @@ -1,4 +1,4 @@ -/* Calcite, src/kernel/initfs.h +/* Calcite, src/kernel/iso9660.h * Copyright 2025 Benji Dial * * This program is free software: you can redistribute it and/or modify @@ -17,10 +17,6 @@ #pragma once -#include +#include "fs.h" -void set_initfs(const uint8_t *start, uint64_t length); - -//if the file does not exist, *start_out is set to a null pointer. -void look_up_initfs_file( - const char *path, const uint8_t **start_out, uint64_t *length_out); +enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out); diff --git a/src/kernel/pata.c b/src/kernel/pata.c index dbde548..1264a69 100644 --- a/src/kernel/pata.c +++ b/src/kernel/pata.c @@ -21,6 +21,11 @@ #include "heap.h" #include "pata.h" +//some relevant sources: +// 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. @@ -116,6 +121,51 @@ 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) { + + uint8_t controller = drive >> 1; + + 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); + if (result != PR_SUCCESS) + return result; + + uint8_t cmd[12] = { + 0xa8, 0, + start_block >> 24, + (start_block >> 16) & 0xff, + (start_block >> 8) & 0xff, + start_block & 0xff, + block_count >> 24, + (block_count >> 16) & 0xff, + (block_count >> 8) & 0xff, + block_count & 0xff, + 0, 0 }; + pata_write_data(controller, (uint16_t *)cmd, 8); + + for (uint32_t i = 0; i < block_count; ++i) { + + result = wait_pio(controller); + if (result != PR_SUCCESS) + return result; + + uint32_t actual_response_size = pata_read_lba(controller) >> 8; + if (actual_response_size != block_size) + panic("TODO") + + pata_read_data(controller, (uint16_t *)(buffer + i * block_size), block_size / 2); + + } + + return PR_SUCCESS; + +} + static uint16_t ipd_buffer[256]; struct pata_driver_info { @@ -123,10 +173,19 @@ struct pata_driver_info { }; static enum drive_access_result read_blocks_patapi( - const struct pata_driver_info *driver_info, + const struct drive_info *drive_info, void *buffer, uint64_t start, uint64_t count) { - panic("TODO"); + if (count >= (1ULL << 32) || start >= (1ULL << 32)) + panic("TODO") + + uint8_t drive = ((const struct pata_driver_info *)drive_info->driver_info)->drive; + + return + patapi_read( + drive, drive_info->block_size, + start, count, buffer) == PR_SUCCESS + ? DAR_SUCCESS : DAR_HARDWARE_ERROR; } @@ -155,7 +214,7 @@ static void probe_pata_drive(uint8_t drive) { driver_info->drive = drive; di->driver_info = driver_info; - di->read_blocks = (void *)&read_blocks_patapi; + di->read_blocks = &read_blocks_patapi; } diff --git a/src/kernel/process.c b/src/kernel/process.c index f840299..925c112 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -16,6 +16,7 @@ */ #include "framebuffer.h" +#include "fs.h" #include "scheduler.h" #include "process.h" #include "utility.h" @@ -191,26 +192,38 @@ no_mem: } -void load_elf( +int load_elf( struct process *process, uint64_t *entry_out, - const void *elf_start, uint64_t elf_length) { + const struct fs_info *fs_info, void *fs_node) { - if (elf_length < 58) - panic("malformed elf") + struct fs_stat stat; + if ((*fs_info->stat_file)(fs_info, fs_node, &stat) != FAR_SUCCESS) + return 0; - *entry_out = *(uint64_t *)(elf_start + 24); + if (stat.bytes < 58) + return 0; - uint64_t phead_start = *(uint64_t *)(elf_start + 32); - uint16_t phead_entry_size = *(uint16_t *)(elf_start + 54); - uint16_t phead_entry_count = *(uint16_t *)(elf_start + 56); + uint8_t head_part[34]; + if ((*fs_info->read_file)(fs_info, fs_node, head_part, 24, 34) != FAR_SUCCESS) + return 0; - if (elf_length < phead_start + phead_entry_count * phead_entry_size) + *entry_out = *(uint64_t *)head_part; + + uint64_t phead_start = *(uint64_t *)(head_part + 8); + uint16_t phead_entry_size = *(uint16_t *)(head_part + 30); + uint16_t phead_entry_count = *(uint16_t *)(head_part + 32); + + if (stat.bytes < phead_start + phead_entry_count * phead_entry_size) panic("malformed elf") for (uint16_t i = 0; i < phead_entry_count; ++i) { - uint64_t *entry = - (uint64_t *)(elf_start + phead_start + i * phead_entry_size); + uint64_t entry[6]; + if ((*fs_info->read_file)( + fs_info, fs_node, entry, + phead_start + i * phead_entry_size, + 6 * sizeof(uint64_t)) != FAR_SUCCESS) + return 0; if ((entry[0] & 0xffffffff) != 1) continue; @@ -223,13 +236,13 @@ void load_elf( uint64_t file_length = entry[4]; uint64_t virtual_length = ((entry[5] - 1) / 4096 + 1) * 4096; - if (elf_length < file_start + file_length) - panic("malformed elf") + if (stat.bytes < file_start + file_length) + return 0; if (file_length > virtual_length) - panic("malformed elf") + return 0; if ((uint64_t)virtual_start % 4096 != 0 || virtual_length % 4096 != 0) - panic("unaligned elf") + return 0; for (uint64_t i = 0; i < virtual_length; i += 4096) { @@ -240,12 +253,20 @@ void load_elf( void *kvma = find_free_kernel_region(4096); map_in_kernel_page_table(pma, kvma, 1, 0); - if (i + 4096 <= file_length) - memcpy(kvma, elf_start + file_start + i, 4096); + if (i + 4096 <= file_length) { + if ((*fs_info->read_file)( + fs_info, fs_node, kvma, + file_start + i, 4096) != FAR_SUCCESS) + return 0; + } else if (i >= file_length) memzero(kvma, 4096); else { - memcpy(kvma, elf_start + file_start + i, file_length & 0xfff); + if ((*fs_info->read_file)( + fs_info, fs_node, kvma, + file_start + i, + file_length & 0xfff) != FAR_SUCCESS) + return 0; memzero(kvma + (file_length & 0xfff), 4096 - (file_length & 0xfff)); } @@ -255,6 +276,8 @@ void load_elf( } + return 1; + } void destroy_process(struct process *process) { diff --git a/src/kernel/process.h b/src/kernel/process.h index a623429..e10bf0a 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -17,7 +17,7 @@ #pragma once -#include +#include "fs.h" struct process { @@ -64,12 +64,10 @@ void unmap_page_for_process( void *find_free_process_region( struct process *process, uint64_t page_count); -//loaded sections are copied to new pages. once storage drivers are written, -//this should probably just take a file handle so it can load and parse the -//header and then load sections directly into user pages. -void load_elf( +//returns 0 on failure, 1 on success. +int load_elf( struct process *process, uint64_t *entry_out, - const void *elf_start, uint64_t elf_length); + const struct fs_info *fs_info, void *fs_node); void destroy_process(struct process *process);