diff --git a/.gitignore b/.gitignore index 2d251ca3..756c1fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ /dependencies/include/ /dependencies/needs_download.cmake /qemu_log.txt +/tar +/src/oboskrnl/inc/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 09898026..a37cc3b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,10 +106,20 @@ add_compile_definitions( set_property(GLOBAL PROPERTY C_STANDARD 17 PROPERTY C_STANDARD_REQUIRED true) -if (OBOS_ENABLE_WERROR) +if (DEFINED OBOS_ENABLE_WERROR AND NOT DEFINED OBOS_ENABLE_FANALYZER) add_compile_options("-Werror") endif() +if (DEFINED OBOS_ENABLE_FANALYZER) + add_compile_options($<$:-fanalyzer>) +endif() +if (DEFINED OBOS_ENABLE_FANALYZER AND DEFINED OBOS_ENABLE_WERROR) + message(WARNING "OBOS_ENABLE_FANALYZER and OBOS_ENABLE_WERROR are mutually exclusive. Preferring OBOS_ENABLE_FANALYZER.") +endif() +if (NOT DEFINED OBOS_DEV_PREFIX) + set(OBOS_DEV_PREFIX "/dev/") +endif() +configure_file(${CMAKE_SOURCE_DIR}/src/inc/dev_prefix.h.in ${CMAKE_SOURCE_DIR}/src/oboskrnl/inc/dev_prefix.h @ONLY) # Include the kernel add_subdirectory("src/oboskrnl") @@ -119,9 +129,10 @@ get_target_property(uacpi_INCLUDES uacpi INCLUDE_DIRECTORIES) include_directories(${uacpi_INCLUDES}) if (OBOS_ARCHITECTURE STREQUAL "x86_64") add_subdirectory("src/drivers/x86_64/uart") -endif() -add_subdirectory("src/isogen") + endif() +add_subdirectory("src/drivers/generic/initrd") add_subdirectory("src/drivers/test_driver") +add_subdirectory("src/isogen") if (OBOS_ARCHITECTURE STREQUAL "m68k") add_subdirectory("src/oboskrnl/arch/m68k/loader") endif() diff --git a/README.md b/README.md index db4158a2..a11dfaea 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ git clone https://github.com/OBOS-dev/obos.git 3. Run cmake to build the OS (x86_64) ```sh -mkdir build -alias rem=eval -cmake -GNinja -DCMAKE_BUILD_TYPE=Release --toolchain=src/build/x86_64/toolchain.cmake -B build . -rem chmod +x dependencies/hyper/hyper_install-linux-x86_64 -cmake --build build +cmake -GNinja -DCMAKE_BUILD_TYPE=Debug --toolchain=src/build/x86_64/toolchain.cmake -B build/x86_64 . +chmod +x dependencies/hyper/hyper_install-linux-x86_64 +cmake --build build/x86_64 +scripts/generate_initrd-x86_64.sh +cmake --build build/x86_64 ``` 4. To run the kernel, run the script under scripts/launch_qemu.\[bat,sh\] diff --git a/config/hyper.cfg b/config/hyper.cfg index ac61392f..99e3e686 100644 --- a/config/hyper.cfg +++ b/config/hyper.cfg @@ -5,7 +5,7 @@ protocol=ultra higher-half-exclusive=true kernel-as-module=true video-mode=auto -cmdline='' +cmdline='-initrd-module=initrd -initrd-driver-module=initrd_driver --root-fs-partid initrd' binary: path="/obos/oboskrnl" @@ -22,5 +22,9 @@ module: name="INITIAL_SWAP_BUFFER" module: type="file" - path="/uart" - name="uart_driver" \ No newline at end of file + path="/obos/initrd.tar" + name="initrd" +module: + type="file" + path="/obos/initrd" + name="initrd_driver" \ No newline at end of file diff --git a/config/initrd.tar b/config/initrd.tar new file mode 100644 index 00000000..5f81a652 Binary files /dev/null and b/config/initrd.tar differ diff --git a/scripts/generate_initrd-x86_64.sh b/scripts/generate_initrd-x86_64.sh new file mode 100755 index 00000000..1265a79a --- /dev/null +++ b/scripts/generate_initrd-x86_64.sh @@ -0,0 +1,14 @@ +export old_pwd=$PWD +if [[ ! -d tar ]] +then + mkdir tar +fi +cd $(git rev-parse --show-toplevel) +cp out/uart tar +if [[ ! -d tar/dev ]] +then + mkdir tar/dev +fi +cd tar +tar -H ustar -cf ../config/initrd.tar * +cd $old_pwd diff --git a/scripts/launch_qemu-m68k.sh b/scripts/launch_qemu-m68k.sh index 52aff0a6..bfedfb23 100755 --- a/scripts/launch_qemu-m68k.sh +++ b/scripts/launch_qemu-m68k.sh @@ -11,6 +11,8 @@ qemu-system-m68k \ -m 512M \ -d int \ -D qemu_log.txt \ --serial file:/dev/stdout +-serial file:/dev/stdout \ +-append "--root-fs-uuid=initrd" \ +-initrd "config/initrd.tar" cd scripts \ No newline at end of file diff --git a/src/build/m68k/driver_link.ld b/src/build/m68k/driver_link.ld index f3735666..5347fdb4 100644 --- a/src/build/m68k/driver_link.ld +++ b/src/build/m68k/driver_link.ld @@ -11,8 +11,8 @@ PHDRS interp PT_INTERP ; text PT_LOAD FILEHDR PHDRS ; data PT_LOAD ; - pageable_text PT_LOAD FLAGS(PF_R|PF_X|0x00010000); - pageable_data PT_LOAD FLAGS(PF_R|PF_W|0x00010000); + pageable_text PT_LOAD FLAGS(PF_X|PF_R|0x00010000); + pageable_data PT_LOAD FLAGS(PF_W|PF_R|0x00010000); dynamic PT_DYNAMIC ; } SECTIONS @@ -194,6 +194,7 @@ SECTIONS pad the .data section. */ . = ALIGN(. != 0 ? 32 / 8 : 1); } :pageable_data + . = ALIGN(32 / 8); _end = .; PROVIDE (end = .); . = DATA_SEGMENT_END (.); /* Stabs debugging sections. */ diff --git a/src/build/m68k/toolchain.cmake b/src/build/m68k/toolchain.cmake index b9d1473a..6378975b 100644 --- a/src/build/m68k/toolchain.cmake +++ b/src/build/m68k/toolchain.cmake @@ -59,6 +59,7 @@ add_compile_definitions( OBOS_USER_ADDRESS_SPACE_BASE=0x1000 OBOS_USER_ADDRESS_SPACE_LIMIT=0x80000000 OBOS_TIMER_IS_DEADLINE= OBOS_ARCH_USES_SOFT_FLOAT=1 + OBOS_ARCH_EMULATED_IRQL=1 ) add_compile_options($<$:-g>) diff --git a/src/build/x86_64/toolchain.cmake b/src/build/x86_64/toolchain.cmake index 29c3e5bd..a2779f30 100644 --- a/src/build/x86_64/toolchain.cmake +++ b/src/build/x86_64/toolchain.cmake @@ -72,6 +72,7 @@ add_compile_definitions( OBOS_KERNEL_ADDRESS_SPACE_BASE=0xffffff0000000000 OBOS_KERNEL_ADDRESS_SPACE_LIMIT=0xfffffffffffff000 OBOS_USER_ADDRESS_SPACE_BASE=0x1000 OBOS_USER_ADDRESS_SPACE_LIMIT=0x7FFFFFFFFFFF OBOS_ARCH_USES_SOFT_FLOAT=0 + OBOS_ARCH_EMULATED_IRQL=0 ) add_compile_options($<$:-g>) diff --git a/src/drivers/generic/initrd/CMakeLists.txt b/src/drivers/generic/initrd/CMakeLists.txt new file mode 100644 index 00000000..28db2403 --- /dev/null +++ b/src/drivers/generic/initrd/CMakeLists.txt @@ -0,0 +1,32 @@ +# drivers/generic/initrd/CMakeLists.txt +# +# Copyright (c) 2024 Omar Berrow + +add_executable(initrd "main.c" "parse.c") + +target_compile_options(initrd + PRIVATE $<$:${TARGET_DRIVER_COMPILE_OPTIONS_C}> + PRIVATE $<$:-ffreestanding> + PRIVATE $<$:-Wall> + PRIVATE $<$:-Wextra> + PRIVATE $<$:-fstack-protector-all> + PRIVATE $<$:-fno-builtin-memset> + PRIVATE $<$:-fvisibility=hidden> + PRIVATE $<$:-fPIC> +) + +set_property(TARGET initrd PROPERTY link_depends ${DRIVER_LINKER_SCRIPT}) + +target_include_directories(initrd + PRIVATE "${CMAKE_SOURCE_DIR}/src/oboskrnl" + PRIVATE ${OBOSKRNL_EXTERNAL_INCLUDES}) + +target_link_options(initrd + PRIVATE "-nostdlib" + PRIVATE "-Wl,-pic" + PRIVATE "-T${DRIVER_LINKER_SCRIPT}" + PRIVATE "-Wl,-shared" +# PRIVATE "-Wl,--allow-shlib-undefined" + PRIVATE ${TARGET_DRIVER_LINKER_OPTIONS} +) +target_compile_definitions(initrd PRIVATE OBOS_DRIVER=1) \ No newline at end of file diff --git a/src/drivers/generic/initrd/main.c b/src/drivers/generic/initrd/main.c new file mode 100644 index 00000000..6e1d1369 --- /dev/null +++ b/src/drivers/generic/initrd/main.c @@ -0,0 +1,297 @@ +/* + * drivers/generic/initrd/main.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include "name.h" +#include "ustar_hdr.h" +#include "parse.h" + +OBOS_PAGEABLE_FUNCTION obos_status get_blk_size(dev_desc desc, size_t* blkSize) +{ + OBOS_UNUSED(desc); + if (!blkSize) + return OBOS_STATUS_INVALID_ARGUMENT; + *blkSize = 1; + return OBOS_STATUS_SUCCESS; +} +OBOS_WEAK obos_status get_max_blk_count(dev_desc desc, size_t* count); +OBOS_WEAK obos_status read_sync(dev_desc desc, void* buf, size_t blkCount, size_t blkOffset, size_t* nBlkRead); +OBOS_PAGEABLE_FUNCTION obos_status write_sync(dev_desc desc, const void* buf, size_t blkCount, size_t blkOffset, size_t* nBlkWritten) +{ + OBOS_UNUSED(desc); + OBOS_UNUSED(buf); + OBOS_UNUSED(blkCount); + OBOS_UNUSED(blkOffset); + OBOS_UNUSED(nBlkWritten); + return OBOS_STATUS_READ_ONLY; +} +OBOS_WEAK obos_status foreach_device(iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* u), void* u); +OBOS_WEAK obos_status query_user_readable_name(dev_desc what, const char** name); // unrequired for fs drivers. +OBOS_WEAK OBOS_PAGEABLE_FUNCTION obos_status ioctl_var(size_t nParameters, uint64_t request, va_list list) +{ + OBOS_ASSERT(nParameters); + OBOS_ASSERT(request); + OBOS_ASSERT(list); + return OBOS_STATUS_INVALID_IOCTL; // we don't support any +} +OBOS_WEAK OBOS_PAGEABLE_FUNCTION obos_status ioctl(size_t nParameters, uint64_t request, ...) +{ + va_list list; + va_start(list, request); + obos_status status = ioctl_var(nParameters, request, list); + va_end(list); + return status; +} +struct hashmap* names; +void driver_cleanup_callback() +{ + FreeCache(); + if (names) + hashmap_free(names); +} + +OBOS_WEAK obos_status query_path(dev_desc desc, const char** path); +OBOS_WEAK obos_status path_search(dev_desc* found, const char* what); +OBOS_WEAK obos_status get_linked_desc(dev_desc desc, dev_desc* found); +OBOS_PAGEABLE_FUNCTION obos_status move_desc_to(dev_desc desc, const char* where) +{ + OBOS_UNUSED(desc); + OBOS_UNUSED(where); + return OBOS_STATUS_READ_ONLY; +} +OBOS_PAGEABLE_FUNCTION obos_status mk_file(dev_desc* newDesc, dev_desc parent, const char* name, file_type type) { + OBOS_UNUSED(newDesc); + OBOS_UNUSED(parent); + OBOS_UNUSED(name); + OBOS_UNUSED(type); + return OBOS_STATUS_READ_ONLY; +} +OBOS_PAGEABLE_FUNCTION obos_status remove_file(dev_desc desc) +{ + OBOS_UNUSED(desc); + return OBOS_STATUS_READ_ONLY; +} +OBOS_PAGEABLE_FUNCTION obos_status set_file_perms(dev_desc desc, driver_file_perm newperm) +{ + OBOS_UNUSED(desc); + OBOS_UNUSED(newperm); + return OBOS_STATUS_READ_ONLY; +} +OBOS_WEAK obos_status get_file_perms(dev_desc desc, driver_file_perm *perm); +OBOS_WEAK obos_status get_file_type(dev_desc desc, file_type *type); +OBOS_WEAK obos_status list_dir(dev_desc dir, iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* userdata), void* userdata); + +__attribute__((section(OBOS_DRIVER_HEADER_SECTION))) driver_header drv_hdr = { + .magic = OBOS_DRIVER_MAGIC, + .flags = DRIVER_HEADER_HAS_STANDARD_INTERFACES|OBOS_STATUS_NO_ENTRY_POINT, + .ftable = { + .driver_cleanup_callback = driver_cleanup_callback, + .ioctl = ioctl, + .ioctl_var = ioctl_var, + .get_blk_size = get_blk_size, + .get_max_blk_count = get_max_blk_count, + .query_user_readable_name = query_user_readable_name, + .foreach_device = foreach_device, + .read_sync = read_sync, + .write_sync = write_sync, + + .query_path = query_path, + .path_search = path_search, + .get_linked_desc = get_linked_desc, + .move_desc_to = move_desc_to, + .mk_file = mk_file, + .remove_file = remove_file, + .get_file_perms = get_file_perms, + .set_file_perms = set_file_perms, + .get_file_type = get_file_type, + .list_dir = list_dir, + }, + .driverName = INITRD_DRIVER_NAME +}; + +// dev_desc is simply a pointer to a ustar_hdr + +OBOS_PAGEABLE_FUNCTION obos_status get_max_blk_count(dev_desc desc, size_t* count) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !count) + return OBOS_STATUS_INVALID_ARGUMENT; + if (hdr->type != AREGTYPE && hdr->type != REGTYPE) + return OBOS_STATUS_NOT_A_FILE; + *count = oct2bin(hdr->filesize, uacpi_strnlen(hdr->filesize, 12));; + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION obos_status read_sync(dev_desc desc, void* buf, size_t blkCount, size_t blkOffset, size_t* nBlkRead) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !buf) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!blkCount) + return OBOS_STATUS_SUCCESS; + if (hdr->type != AREGTYPE && hdr->type != REGTYPE) + return OBOS_STATUS_NOT_A_FILE; + size_t filesize = oct2bin(hdr->filesize, uacpi_strnlen(hdr->filesize, 12));; + if (blkOffset >= filesize) + { + *nBlkRead = 0; + return OBOS_STATUS_SUCCESS; + } + size_t nToRead = blkCount; + if ((blkOffset + blkCount) >= filesize) + nToRead = filesize - blkOffset; + const char* data = (char*)(hdr) + 0x200; + const char* iter = data+blkOffset; + memcpy(buf, iter, nToRead); + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION static uint64_t hash(const void *item, uint64_t seed0, uint64_t seed1) +{ + const char* pck = item; + return hashmap_sip(pck, uacpi_strnlen(pck, 100), seed0, seed1); +} +OBOS_PAGEABLE_FUNCTION static int cmp(const void *a, const void *b, void *udata) +{ + OBOS_UNUSED(udata); + const char* pck1 = a; + const char* pck2 = b; + return uacpi_strncmp(pck1, pck2, 100); +} +OBOS_PAGEABLE_FUNCTION obos_status query_path(dev_desc desc, const char** path) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !path) + return OBOS_STATUS_INVALID_ARGUMENT; + const char* filepath = nullptr; + if(uacpi_strnlen(hdr->filename, 100) == 100) + { + // Shit. + if (!names) + { + names = hashmap_new_with_allocator( + malloc, realloc, free, + sizeof(char*), + 64, 0, 0, + hash, cmp, + free, + nullptr); + } + filepath = malloc(101); + ((char*)filepath)[100] = 0; + memcpy(((char*)filepath), hdr->filename, 100); + hashmap_set(names, filepath); + } + else + { + filepath = (const char*)&hdr->filename; + } + *path = filepath; + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION obos_status get_linked_desc(dev_desc desc, dev_desc* found) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !found) + return OBOS_STATUS_INVALID_ARGUMENT; + if (hdr->type != LNKTYPE && hdr->type != SYMTYPE) + return OBOS_STATUS_INVALID_ARGUMENT; + obos_status status = OBOS_STATUS_SUCCESS; + *found = (dev_desc)GetFile(hdr->linked, &status); + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION obos_status path_search(dev_desc* found, const char* what) +{ + if (!found || !what) + return OBOS_STATUS_INVALID_ARGUMENT; + obos_status status = OBOS_STATUS_SUCCESS; + *found = (dev_desc)GetFile(what, &status); + return status; +} +OBOS_PAGEABLE_FUNCTION obos_status get_file_perms(dev_desc desc, driver_file_perm *perm) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !perm) + return OBOS_STATUS_INVALID_ARGUMENT; + uint16_t filemode = oct2bin(hdr->filemode, uacpi_strnlen(hdr->filemode, 8)); + perm->group_read = filemode & FILEMODE_GROUP_READ; + perm->owner_read = filemode & FILEMODE_OWNER_READ; + perm->other_read = filemode & FILEMODE_OTHER_READ; + perm->group_write = false; + perm->owner_write = false; + perm->other_write = false; + perm->group_exec = filemode & FILEMODE_GROUP_EXEC; + perm->owner_exec = filemode & FILEMODE_OWNER_EXEC; + perm->other_exec = filemode & FILEMODE_OTHER_EXEC; + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION obos_status get_file_type(dev_desc desc, file_type *type) +{ + const ustar_hdr* hdr = (ustar_hdr*)desc; + if (!hdr || !type) + return OBOS_STATUS_INVALID_ARGUMENT; + switch (hdr->type) { + case AREGTYPE: + case REGTYPE: + *type = FILE_TYPE_REGULAR_FILE; + break; + case DIRTYPE: + *type = FILE_TYPE_DIRECTORY; + break; + case LNKTYPE: + case SYMTYPE: + *type = FILE_TYPE_SYMBOLIC_LINK; + break; + default: + return OBOS_STATUS_INTERNAL_ERROR; + } + return OBOS_STATUS_SUCCESS; +} +OBOS_PAGEABLE_FUNCTION obos_status list_dir(dev_desc dir_, iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* userdata), void* userdata) +{ + const ustar_hdr* dir = (ustar_hdr*)dir_; + if (!dir || !cb) + return OBOS_STATUS_INVALID_ARGUMENT; + if (dir_ != UINTPTR_MAX && dir->type != DIRTYPE) + return OBOS_STATUS_INVALID_ARGUMENT; + if (dir_ != UINTPTR_MAX && uacpi_strnlen(dir->filename, 100) == 100) + return OBOS_STATUS_INTERNAL_ERROR; + const char* dir_filename = (const char*)&dir->filename; + if (dir_ == UINTPTR_MAX) + dir_filename = ""; + size_t dirnamelen = uacpi_strnlen(dir_filename, 100); + size_t real_dirnamelen = dirnamelen; + if (dir_filename[dirnamelen-1] == '/') + dirnamelen--; + // We don't get nice things like proper directory entries with the USTAR format. + const ustar_hdr* hdr = (ustar_hdr*)OBOS_InitrdBinary; + while (memcmp(hdr->magic, USTAR_MAGIC, 6)) + { + size_t filesize = oct2bin(hdr->filesize, uacpi_strnlen(hdr->filesize, 12)); + size_t filename_len = uacpi_strnlen(hdr->filename, 100); + if (!dirnamelen || (uacpi_strncmp(dir_filename, hdr->filename, dirnamelen) == 0 && real_dirnamelen != filename_len)) + { + if (strchr(hdr->filename+real_dirnamelen, '/') == filename_len-real_dirnamelen) + cb((dev_desc)hdr, 1, filesize, userdata); + } + size_t filesize_rounded = (filesize + 0x1ff) & ~0x1ff; + hdr = (ustar_hdr*)(((uintptr_t)hdr) + filesize_rounded + 512); + } + return OBOS_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/src/drivers/generic/initrd/name.h b/src/drivers/generic/initrd/name.h new file mode 100644 index 00000000..b16586fa --- /dev/null +++ b/src/drivers/generic/initrd/name.h @@ -0,0 +1,2 @@ +#pragma once +#define INITRD_DRIVER_NAME "Initial Ramdisk (InitRD) Driver" \ No newline at end of file diff --git a/src/drivers/generic/initrd/parse.c b/src/drivers/generic/initrd/parse.c new file mode 100644 index 00000000..d07e1fe5 --- /dev/null +++ b/src/drivers/generic/initrd/parse.c @@ -0,0 +1,109 @@ +/* + * drivers/generic/initrd/parse.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include +#include +#include +#include + +#include +#include + +#include "parse.h" +#include "ustar_hdr.h" + +#include + +#include + +static struct hashmap* cache; + +static uint64_t hash(const void *item, uint64_t seed0, uint64_t seed1) +{ + const ustar_hdr* pck = item; + return hashmap_sip(pck->filename, uacpi_strnlen(pck->filename, 100), seed0, seed1); +} +static int cmp(const void *a, const void *b, void *udata) +{ + OBOS_UNUSED(udata); + const ustar_hdr* pck1 = a; + const ustar_hdr* pck2 = b; + return uacpi_strncmp(pck1->filename, pck2->filename, 100); +} +void* malloc(size_t sz) +{ + return OBOS_KernelAllocator->Allocate(OBOS_KernelAllocator, sz, nullptr); +} +void* realloc(void* buf, size_t sz) +{ + return OBOS_KernelAllocator->Reallocate(OBOS_KernelAllocator, buf, sz, nullptr); +} +void free(void* buf) +{ + size_t blkSize = 0; + OBOS_KernelAllocator->QueryBlockSize(OBOS_KernelAllocator, buf, &blkSize); + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, buf, blkSize); +} +static void initialize_hashmap() +{ + cache = hashmap_new_with_allocator( + malloc, realloc, free, + sizeof(ustar_hdr), + 64, 0, 0, + hash, cmp, + nullptr, + nullptr); +} + +#define set_status(to) status ? (*status = to) : (void)0 +const ustar_hdr* GetFile(const char* path, obos_status* status) +{ + if (!OBOS_InitrdBinary) + { + set_status(OBOS_STATUS_NOT_FOUND); + return nullptr; + } + if (strlen(path) > 100) + { + set_status(OBOS_STATUS_INVALID_ARGUMENT); + return nullptr; + } + if (!cache) + initialize_hashmap(); + set_status(OBOS_STATUS_SUCCESS); + ustar_hdr what = {}; + memcpy(what.filename, path, uacpi_strnlen(path, 100)); + const ustar_hdr* hdr = (ustar_hdr*)hashmap_get(cache, &what); + if (hdr) + return hdr; + hdr = (ustar_hdr*)OBOS_InitrdBinary; + size_t pathlen = uacpi_strnlen(path, 100); + char path_slash[100]; + memcpy(path_slash, path, pathlen); + if (path_slash[pathlen-1] != '/') + { + path_slash[pathlen] = '/'; + path_slash[++pathlen] = '\0'; + } + while (memcmp(hdr->magic, USTAR_MAGIC, 6)) + { + if (uacpi_strncmp(path_slash, hdr->filename, pathlen) == 0 || + uacpi_strncmp(path, hdr->filename, pathlen) == 0 + ) + { + hashmap_set(cache, hdr); + return hdr; + } + size_t filesize = oct2bin(hdr->filesize, uacpi_strnlen(hdr->filesize, 12)); + size_t filesize_rounded = (filesize + 0x1ff) & ~0x1ff; + hdr = (ustar_hdr*)(((uintptr_t)hdr) + filesize_rounded + 512); + } + return nullptr; +} +void FreeCache() +{ + hashmap_free(cache); +} \ No newline at end of file diff --git a/src/drivers/generic/initrd/parse.h b/src/drivers/generic/initrd/parse.h new file mode 100644 index 00000000..9d51c98e --- /dev/null +++ b/src/drivers/generic/initrd/parse.h @@ -0,0 +1,28 @@ +/* + * drivers/generic/initrd/parse.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include +#include + +#include "ustar_hdr.h" + +// USTAR is strictly read only (on OBOS). + +const ustar_hdr* GetFile(const char* path, obos_status* status); +void FreeCache(); +inline static uint64_t oct2bin(const char* str, size_t size) +{ + uint64_t n = 0; + const char* c = str; + while (size-- > 0) + n = n * 8 + (uint64_t)(*c++ - '0'); + return n; +} +void* malloc(size_t sz); +void* realloc(void* buf, size_t sz); +void free(void* buf); \ No newline at end of file diff --git a/src/drivers/generic/initrd/ustar_hdr.h b/src/drivers/generic/initrd/ustar_hdr.h new file mode 100644 index 00000000..6594aa96 --- /dev/null +++ b/src/drivers/generic/initrd/ustar_hdr.h @@ -0,0 +1,57 @@ +/* + * drivers/generic/initrd/ustar_hdr.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include +#include + +typedef struct ustar_hdr +{ + char filename[100]; + char filemode[8]; + char owner_uid[8]; + char group_uid[8]; + char filesize[12]; // In octal! + char last_mod[12]; // In octal! + char chksum[8]; + char type; + char linked[100]; + char magic[6]; // should be ustar\0 + char version[2]; + char owner_uname[32]; + char group_uname[32]; + char unused[16]; + char prefix[155]; +} OBOS_PACK ustar_hdr; + +enum { + FILEMODE_EXEC = BIT(0), + FILEMODE_WRITE = BIT(1), + FILEMODE_READ = BIT(2), + FILEMODE_OTHER_EXEC = FILEMODE_EXEC << 0, + FILEMODE_OTHER_WRITE = FILEMODE_WRITE << 0, + FILEMODE_OTHER_READ = FILEMODE_READ << 0, + FILEMODE_GROUP_EXEC = FILEMODE_EXEC << 3, + FILEMODE_GROUP_WRITE = FILEMODE_WRITE << 3, + FILEMODE_GROUP_READ = FILEMODE_READ << 3, + FILEMODE_OWNER_EXEC = FILEMODE_EXEC << 6, + FILEMODE_OWNER_WRITE = FILEMODE_WRITE << 6, + FILEMODE_OWNER_READ = FILEMODE_READ << 6, +}; +enum +{ + AREGTYPE = '\0', + REGTYPE = '0', + LNKTYPE = '1', + SYMTYPE = '2', + CHRTYPE = '3', + BLKTYPE = '4', + DIRTYPE = '5', + FIFOTYPE = '6', + CONTYPE = '7', +}; +#define USTAR_MAGIC "ustar\0" \ No newline at end of file diff --git a/src/drivers/x86_64/uart/CMakeLists.txt b/src/drivers/x86_64/uart/CMakeLists.txt index 8a5b5c9b..59c4ccea 100644 --- a/src/drivers/x86_64/uart/CMakeLists.txt +++ b/src/drivers/x86_64/uart/CMakeLists.txt @@ -12,7 +12,7 @@ target_compile_options(uart PRIVATE $<$:-fstack-protector-all> PRIVATE $<$:-fno-builtin-memset> PRIVATE $<$:-fvisibility=hidden> - PRIVATE $<$:-fPIE> + PRIVATE $<$:-fPIC> ) set_property(TARGET uart PROPERTY link_depends ${DRIVER_LINKER_SCRIPT}) @@ -23,7 +23,7 @@ target_include_directories(uart target_link_options(uart PRIVATE "-nostdlib" - PRIVATE "-fPIE" + PRIVATE "-fPIC" PRIVATE "-Wl,-shared" # PRIVATE "-Wl,--allow-shlib-undefined" PRIVATE "-T" PRIVATE ${DRIVER_LINKER_SCRIPT} diff --git a/src/drivers/x86_64/uart/main.c b/src/drivers/x86_64/uart/main.c index 80b01937..3c87eb24 100644 --- a/src/drivers/x86_64/uart/main.c +++ b/src/drivers/x86_64/uart/main.c @@ -30,8 +30,13 @@ #include +#include +#include + #include "serial_port.h" +#include + driver_id* this_driver; serial_port* serialPorts = nullptr; size_t nSerialPorts = 0; @@ -80,11 +85,11 @@ obos_status query_user_readable_name(dev_desc what, const char** name) } return OBOS_STATUS_SUCCESS; } -obos_status foreach_device(iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount)) +obos_status foreach_device(iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* udata), void* udata) { for (size_t i = 0; i < nSerialPorts; i++) { - switch (cb((dev_desc)&serialPorts[i], 1, -1)) + switch (cb((dev_desc)&serialPorts[i], 1, -1, udata)) { case ITERATE_DECISION_CONTINUE: continue; @@ -101,9 +106,11 @@ obos_status read_sync(dev_desc desc, void* buf, size_t blkCount, size_t blkOffse if (!port || !buf || !blkCount) return OBOS_STATUS_INVALID_ARGUMENT; size_t i = 0; + while(port->in_buffer.szBuf < blkCount) + ; irql oldIrql = Core_SpinlockAcquireExplicit(&port->in_buffer.lock, IRQL_COM_IRQ, false); - // TODO: Make sync instead of async. - for (; i < blkCount && i < port->in_buffer.szBuf; i++) + const size_t initialBufSize = port->in_buffer.szBuf; + for (; i < blkCount && i < initialBufSize; i++) ((char*)buf)[i] = pop_from_buffer(&port->in_buffer); Core_SpinlockRelease(&port->in_buffer.lock, oldIrql); if (nBlkRead) @@ -137,6 +144,7 @@ obos_status write_sync(dev_desc desc, const void* buf, size_t blkCount, size_t b return OBOS_STATUS_SUCCESS; } obos_status ioctl(size_t nParameters, uint64_t request, ...); +obos_status ioctl_var(size_t nParameters, uint64_t request, va_list list); __attribute__((section(OBOS_DRIVER_HEADER_SECTION))) driver_header drv_hdr = { .magic = OBOS_DRIVER_MAGIC, .flags = DRIVER_HEADER_PIPE_STYLE_DEVICE|DRIVER_HEADER_HAS_STANDARD_INTERFACES|DRIVER_HEADER_FLAGS_DETECT_VIA_ACPI, @@ -148,6 +156,7 @@ __attribute__((section(OBOS_DRIVER_HEADER_SECTION))) driver_header drv_hdr = { .ftable = { .driver_cleanup_callback = cleanup, .ioctl = ioctl, + .ioctl_var = ioctl_var, .get_blk_size = get_blk_size, .get_max_blk_count = get_max_blk_count, .query_user_readable_name = query_user_readable_name, @@ -204,11 +213,9 @@ static uacpi_ns_iteration_decision match_uart(void *user, uacpi_namespace_node * return UACPI_NS_ITERATION_DECISION_CONTINUE; } -obos_status ioctl(size_t nParameters, uint64_t request, ...) +obos_status ioctl_var(size_t nParameters, uint64_t request, va_list list) { obos_status status = OBOS_STATUS_INVALID_IOCTL; - va_list list; - va_start(list, request); switch (request) { case IOCTL_OPEN_SERIAL_CONNECTION: @@ -245,6 +252,14 @@ obos_status ioctl(size_t nParameters, uint64_t request, ...) break; } } + return status; +} +obos_status ioctl(size_t nParameters, uint64_t request, ...) +{ + obos_status status = OBOS_STATUS_INVALID_IOCTL; + va_list list; + va_start(list, request); + status = ioctl_var(nParameters, request, list); va_end(list); return status; } @@ -286,6 +301,12 @@ OBOS_PAGEABLE_FUNCTION void OBOS_DriverEntry(driver_id* this) OBOS_Warning("Could not initialize GSI for COM%d. Status: %d.\n", port->com_port, status); continue; } + + vnode* vn = Drv_AllocateVNode(this_driver, (uintptr_t)port, 0, nullptr, VNODE_TYPE_CHR); + const char* dev_name = nullptr; + query_user_readable_name(vn->desc, &dev_name); + OBOS_Debug("%*s: Registering serial port at %s%s\n", uacpi_strnlen(this_driver->header.driverName, 64), this_driver->header.driverName, OBOS_DEV_PREFIX, dev_name); + Drv_RegisterVNode(vn, dev_name); } Core_ExitCurrentThread(); } \ No newline at end of file diff --git a/src/drivers/x86_64/uart/serial_port.c b/src/drivers/x86_64/uart/serial_port.c index be91b76a..a070ac8a 100644 --- a/src/drivers/x86_64/uart/serial_port.c +++ b/src/drivers/x86_64/uart/serial_port.c @@ -124,11 +124,11 @@ obos_status open_serial_connection(serial_port* port, uint32_t baudRate, data_bi outb(port->port_base + IRQ_ENABLE, 1); Arch_IOAPICMaskIRQ(port->gsi, false); Core_LowerIrql(oldIrql); - *connection = (dev_desc)port; + if (connection) + *connection = (dev_desc)port; return OBOS_STATUS_SUCCESS; } -static dpc com_dpc; // static dpc kdbg_int_dpc; static bool should_break; static void dpc_handler(dpc* obj, void* userdata) @@ -156,8 +156,9 @@ void com_irq_handler(struct irq* i, interrupt_frame* frame, void* userdata, irql OBOS_UNUSED(frame); OBOS_UNUSED(oldIrql_); serial_port *port = userdata; - com_dpc.userdata = userdata; - CoreH_InitializeDPC(&com_dpc, dpc_handler, Core_DefaultThreadAffinity & ~(CoreS_GetCPULocalPtr()->id)); + dpc* com_dpc = &port->com_dpc; + com_dpc->userdata = userdata; + CoreH_InitializeDPC(com_dpc, dpc_handler, Core_DefaultThreadAffinity & ~(CoreS_GetCPULocalPtr()->id)); // Receive all the avaliable data. irql oldIrql = Core_SpinlockAcquireExplicit(&port->in_buffer.lock, IRQL_COM_IRQ, false); while (inb(port->port_base + LINE_STATUS) & BIT(0)) diff --git a/src/drivers/x86_64/uart/serial_port.h b/src/drivers/x86_64/uart/serial_port.h index 8f3f70d1..11d56179 100644 --- a/src/drivers/x86_64/uart/serial_port.h +++ b/src/drivers/x86_64/uart/serial_port.h @@ -12,6 +12,7 @@ #include #include +#include #include @@ -43,6 +44,8 @@ typedef struct serial_port buffer out_buffer; bool isFaulty; + + dpc com_dpc; } serial_port; void flush_out_buffer(serial_port* port); diff --git a/src/inc/dev_prefix.h.in b/src/inc/dev_prefix.h.in new file mode 100644 index 00000000..1877a3b6 --- /dev/null +++ b/src/inc/dev_prefix.h.in @@ -0,0 +1 @@ +#cmakedefine OBOS_DEV_PREFIX "@OBOS_DEV_PREFIX@" \ No newline at end of file diff --git a/src/isogen/CMakeLists.txt b/src/isogen/CMakeLists.txt index c10481b7..60b22e69 100644 --- a/src/isogen/CMakeLists.txt +++ b/src/isogen/CMakeLists.txt @@ -29,7 +29,8 @@ add_custom_target(isogen ALL COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/config/hyper.cfg ${ISODIR}/ ${SUPPRESS_OUTPUT} COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/dependencies/hyper/hyper_iso_boot ${ISODIR} ${SUPPRESS_OUTPUT} COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/config/hyper_uefi_boot.bin ${ISODIR} ${SUPPRESS_OUTPUT} - COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/out/uart ${ISODIR} ${SUPPRESS_OUTPUT} + COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/out/initrd ${ISODIR}/obos ${SUPPRESS_OUTPUT} + COMMAND cmake -E copy ${CMAKE_SOURCE_DIR}/config/initrd.tar ${ISODIR}/obos ${SUPPRESS_OUTPUT} COMMAND xorriso -as mkisofs -b hyper_iso_boot -no-emul-boot -boot-load-size 4 -boot-info-table --efi-boot hyper_uefi_boot.bin -efi-boot-part --efi-boot-image --protective-msdos-label out/isodir -o ${OUTPUT_DIR}/obos.iso ${SUPPRESS_OUTPUT} COMMAND ${hyper_install} ${OUTPUT_DIR}/obos.iso ${SUPPRESS_OUTPUT} SOURCES ${CMAKE_SOURCE_DIR}/config/hyper.cfg ${CMAKE_SOURCE_DIR}/config/hyper_uefi_boot.bin ${CMAKE_SOURCE_DIR}/dependencies/hyper/hyper_iso_boot @@ -37,12 +38,13 @@ add_custom_target(isogen ALL BYPRODUCTS ${ISODIR}/EFI/BOOT/BOOTX64.efi BYPRODUCTS ${ISODIR}/hyper_iso_boot BYPRODUCTS ${ISODIR}/obos/oboskrnl - BYPRODUCTS ${ISODIR}/test_driver + BYPRODUCTS ${ISODIR}/obos/initrd + BYPRODUCTS ${ISODIR}/obos/initrd.tar BYPRODUCTS ${OUTPUT_DIR}/obos.iso WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Generating ${OUTPUT_DIR}/obos.iso" ) add_dependencies(isogen oboskrnl) -if (EXISTS uart) - add_dependencies(isogen uart) +if (EXISTS initrd) + add_dependencies(isogen initrd) endif() \ No newline at end of file diff --git a/src/oboskrnl/CMakeLists.txt b/src/oboskrnl/CMakeLists.txt index 791c0b59..62407546 100644 --- a/src/oboskrnl/CMakeLists.txt +++ b/src/oboskrnl/CMakeLists.txt @@ -9,13 +9,16 @@ list (APPEND oboskrnl_sources "irq/timer.c" "mm/context.c" "mm/init.c" "mm/swap.c" "mm/handler.c" "mm/alloc.c" "driver_interface/loader.c" "utils/hashmap.c" "driver_interface/pnp.c" "irq/dpc.c" "locks/mutex.c" "locks/semaphore.c" - "locks/event.c" "locks/wait.c" "cmdline.c" + "locks/event.c" "locks/wait.c" "cmdline.c" "vfs/init.c" + "vfs/alloc.c" "utils/string.c" "vfs/mount.c" "vfs/dirent.c" + "vfs/fd.c" "vfs/pagecache.c" "vfs/async.c" "mm/pmm.c" ) add_executable(oboskrnl) set_source_files_properties("sanitizers/ubsan.c" PROPERTIES COMPILE_OPTIONS "-fvisibility=default") set_source_files_properties("klog.c" PROPERTIES COMPILE_OPTIONS "-Wno-error") +set_source_files_properties("vfs/mount.c" PROPERTIES COMPILE_OPTIONS "-Wno-unused-function") target_compile_options(oboskrnl PRIVATE $<$:${TARGET_COMPILE_OPTIONS_C}> @@ -25,6 +28,7 @@ target_compile_options(oboskrnl PRIVATE $<$:-fstack-protector-all> PRIVATE $<$:-fno-builtin-memset> PRIVATE $<$:-fvisibility=hidden> + PRIVATE $<$:-Wno-error=unused-function> # https://discord.com/channels/440442961147199490/1141057599584878645/1269032825579901069 PRIVATE $<$:-Werror=incompatible-pointer-types> ) @@ -38,7 +42,9 @@ set_property(TARGET oboskrnl PROPERTY link_depends ${LINKER_SCRIPT}) target_include_directories(oboskrnl PRIVATE "${CMAKE_SOURCE_DIR}/src/oboskrnl" - PRIVATE ${OBOSKRNL_EXTERNAL_INCLUDES}) + PRIVATE "${CMAKE_SOURCE_DIR}/src/drivers" + PRIVATE ${OBOSKRNL_EXTERNAL_INCLUDES} +) target_link_options(oboskrnl PRIVATE "-nostdlib" @@ -59,5 +65,5 @@ target_sources(oboskrnl PRIVATE ${oboskrnl_sources}) set_source_files_properties( "arch/m68k/entry.c" PROPERTIES - OBJECT_DEPENDS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_driver" + OBJECT_DEPENDS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/initrd" ) \ No newline at end of file diff --git a/src/oboskrnl/allocators/basic_allocator.c b/src/oboskrnl/allocators/basic_allocator.c index 7d3e6b20..d00a7205 100644 --- a/src/oboskrnl/allocators/basic_allocator.c +++ b/src/oboskrnl/allocators/basic_allocator.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -68,7 +69,7 @@ static OBOS_NO_KASAN void* allocateBlock(basic_allocator* This, size_t size, int size_t nPages = size / OBOS_PAGE_SIZE; if (size % OBOS_PAGE_SIZE) nPages++; - uintptr_t phys = OBOSS_AllocatePhysicalPages(nPages, 1, status); + uintptr_t phys = Mm_AllocatePhysicalPages(nPages, 1, status); if (!phys) return nullptr; // Arch-specific: @@ -87,7 +88,7 @@ static OBOS_NO_KASAN void* allocateBlock(basic_allocator* This, size_t size, int memzero(ret, size); return ret; } - void* ret = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, size, 0, ((allocator_info*)This == OBOS_NonPagedPoolAllocator ? VMA_FLAGS_NON_PAGED : 0), status); + void* ret = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, size, 0, ((allocator_info*)This == OBOS_NonPagedPoolAllocator ? VMA_FLAGS_NON_PAGED : 0), nullptr, status); if (!ret) return nullptr; if ((allocator_info*)This == OBOS_NonPagedPoolAllocator) @@ -113,7 +114,7 @@ static OBOS_NO_KASAN basicalloc_region* allocateNewRegion(basic_allocator* This, if (!blk) return nullptr; blk->magic = PAGEBLOCK_MAGIC; - blk->size = initialSize + sizeof(basicalloc_node); + blk->size = initialSize+sizeof(basicalloc_region); basicalloc_node* n = (basicalloc_node*)(blk + 1); memzero(n, sizeof(*n)); n->magic = MEMBLOCK_MAGIC; @@ -163,11 +164,15 @@ static OBOS_NO_KASAN void freeRegion(basic_allocator* This, basicalloc_region* b uintptr_t phys = 0; #ifdef __x86_64__ phys = Arch_UnmapFromHHDM(block); +#elif defined(__m68k__) + phys = Arch_UnmapFromHHDM(block); +#else +# error Unknown architecture #endif size_t nPages = block->size / OBOS_PAGE_SIZE; if (block->size % OBOS_PAGE_SIZE) nPages++; - OBOSS_FreePhysicalPages(phys, nPages); + Mm_FreePhysicalPages(phys, nPages); break; } default: diff --git a/src/oboskrnl/arch/m68k/driver_loader.c b/src/oboskrnl/arch/m68k/driver_loader.c index e156b36d..309cdf40 100644 --- a/src/oboskrnl/arch/m68k/driver_loader.c +++ b/src/oboskrnl/arch/m68k/driver_loader.c @@ -387,7 +387,7 @@ void* DrvS_LoadRelocatableElf(driver_id* driver, const void* file, size_t szFile } end = (void*)(((uintptr_t)end + 0xfff) & ~0xfff); szProgram = (size_t)end; - void* base = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, szProgram, 0, VMA_FLAGS_GUARD_PAGE, status); + void* base = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, szProgram, 0, VMA_FLAGS_GUARD_PAGE, nullptr, status); if (!base) return nullptr; // The pages are mapped in. diff --git a/src/oboskrnl/arch/m68k/entry.c b/src/oboskrnl/arch/m68k/entry.c index 87d341b9..b52c8d89 100644 --- a/src/oboskrnl/arch/m68k/entry.c +++ b/src/oboskrnl/arch/m68k/entry.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,9 @@ #include #include #include +#include + +#include #include #include @@ -64,6 +68,10 @@ volatile struct limine_boot_info_request Arch_BootInfo = { .id = LIMINE_BOOT_INFO_REQUEST, .revision = 0, }; +volatile struct limine_module_request Arch_InitrdRequest = { + .id = LIMINE_MODULE_REQUEST, + .revision = 0, +}; cpu_local bsp_cpu; thread kmain_thread; thread idle_thread; @@ -161,7 +169,7 @@ asm ( ".global Arch_ModuleEnd;" ".align 8;" "Arch_ModuleStart:;" - ".incbin \"" OBOS_BINARY_DIRECTORY "/test_driver\";" + ".incbin \"" OBOS_BINARY_DIRECTORY "/initrd\";" "Arch_ModuleEnd:;" ".section text;" ); @@ -175,7 +183,7 @@ void Arch_KernelEntry() for (uint8_t vec = 25; vec < 32; vec++) Arch_RawRegisterInterrupt(vec, (uintptr_t)Arch_PICHandleIRQ); OBOS_Debug("%s: Initializing PMM.\n", __func__); - Arch_InitializePMM(); + Mm_InitializePMM(); OBOS_Debug("%s: Initializing page tables.\n", __func__); Arch_InitializePageTables(); Arch_TTYBase = Arch_BootInfo.response->uart_phys_base; @@ -195,6 +203,9 @@ void Arch_KernelEntry() if (obos_is_error(status = OBOSH_ConstructBasicAllocator(&kalloc))) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not initialize allocator. Status: %d.\n", status); OBOS_KernelAllocator = (allocator_info*)&kalloc; + OBOS_Debug("%s: Parsing command line.\n", __func__); + OBOS_KernelCmdLine = Arch_KernelFile.response->kernel_file->cmdline; + OBOS_ParseCMDLine(); OBOS_Debug("%s: Initialize kernel process.\n", __func__); OBOS_KernelProcess = Core_ProcessAllocate(&status); if (obos_is_error(status)) @@ -207,7 +218,7 @@ void Arch_KernelEntry() OBOS_Debug("%s: Initializing VMM.\n", __func__); static swap_dev swap; size_t swap_size = 16777216 /* 16 MiB */; - uintptr_t swap_buf = Arch_AllocatePhysicalPages(swap_size/OBOS_PAGE_SIZE, 1, nullptr); + uintptr_t swap_buf = Mm_AllocatePhysicalPages(swap_size/OBOS_PAGE_SIZE, 1, nullptr); Arch_InitializeInitialSwapDevice(&swap, Arch_MapToHHDM(swap_buf), swap_size); Mm_SwapProvider = &swap; Mm_Initialize(); @@ -282,7 +293,7 @@ void Arch_KernelEntry() } RB_INSERT(symbol_table, &OBOS_KernelSymbolTable, symbol); } - OBOS_Debug("%s: Loading test driver.\n", __func__); + OBOS_Debug("%s: Loading InitRD driver.\n", __func__); status = OBOS_STATUS_SUCCESS; const void* file = Arch_ModuleStart; size_t filesize = (uintptr_t)Arch_ModuleEnd-(uintptr_t)Arch_ModuleStart; @@ -291,6 +302,16 @@ void Arch_KernelEntry() if (obos_is_error(status)) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not load driver! Status: %d.\n", status); Drv_StartDriver(id, nullptr); + OBOS_Debug("%s: Initializing VFS.\n", __func__); + OBOS_InitrdBinary = Arch_InitrdRequest.response->modules[0]->address; + OBOS_InitrdSize = Arch_InitrdRequest.response->modules[0]->size; + Vfs_Initialize(); + OBOS_Log("%s: Done early boot.\n", __func__); + OBOS_Log("Currently at %ld KiB of committed memory (%ld KiB pageable), %ld KiB paged out, and %ld KiB non-paged.\n", + Mm_KernelContext.stat.committedMemory/0x400, + Mm_KernelContext.stat.pageable/0x400, + Mm_KernelContext.stat.paged/0x400, + Mm_KernelContext.stat.nonPaged/0x400); Core_ExitCurrentThread(); } void Arch_IdleTask() diff --git a/src/oboskrnl/arch/m68k/goldfish_pic.c b/src/oboskrnl/arch/m68k/goldfish_pic.c index 7ab1ee6d..5ea308e5 100644 --- a/src/oboskrnl/arch/m68k/goldfish_pic.c +++ b/src/oboskrnl/arch/m68k/goldfish_pic.c @@ -11,9 +11,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -43,13 +45,13 @@ static void initialize() nullptr, 0x1000, OBOS_PROTECTION_CACHE_DISABLE, - VMA_FLAGS_NON_PAGED, - nullptr); + VMA_FLAGS_NON_PAGED, + nullptr, nullptr); uintptr_t oldPhys = 0; OBOSS_GetPagePhysicalAddress((void*)virt_base, &oldPhys); // Map as RW, Cache Disabled, and Supervisor Arch_MapPage(MmS_GetCurrentPageTable(), virt_base, Arch_PICBases[i].phys_base, (0b11|(0b11<<5)|(1<<7))); - OBOSS_FreePhysicalPages(oldPhys, 1); + Mm_FreePhysicalPages(oldPhys, 1); Arch_PICBases[i].base = virt_base; } } diff --git a/src/oboskrnl/arch/m68k/goldfish_rtc.c b/src/oboskrnl/arch/m68k/goldfish_rtc.c index 566a1aad..4873f0c3 100644 --- a/src/oboskrnl/arch/m68k/goldfish_rtc.c +++ b/src/oboskrnl/arch/m68k/goldfish_rtc.c @@ -4,6 +4,7 @@ * Copyright (c) 2024 Omar Berrow */ +#include "mm/pmm.h" #include #include #include @@ -122,12 +123,12 @@ OBOS_PAGEABLE_FUNCTION obos_status CoreS_InitializeTimer(irq_handler handler) 0x1000, OBOS_PROTECTION_CACHE_DISABLE, VMA_FLAGS_NON_PAGED, - nullptr); + nullptr, nullptr); uintptr_t oldPhys = 0; OBOSS_GetPagePhysicalAddress((void*)virt_base, &oldPhys); // Map as RW, Cache Disabled, and Supervisor Arch_MapPage(MmS_GetCurrentPageTable(), virt_base, Arch_RTCBase.base, (0b11|(0b11<<5)|(1<<7))); - OBOSS_FreePhysicalPages(oldPhys, 1); + Mm_FreePhysicalPages(oldPhys, 1); Arch_RTCBase.base = virt_base; irql oldIrql = Core_RaiseIrql(IRQL_TIMER); volatile gf_rtc* rtc = (volatile gf_rtc*)(Arch_RTCBase.base); diff --git a/src/oboskrnl/arch/m68k/initial_swap.c b/src/oboskrnl/arch/m68k/initial_swap.c index 8354fb1e..43a69a8a 100644 --- a/src/oboskrnl/arch/m68k/initial_swap.c +++ b/src/oboskrnl/arch/m68k/initial_swap.c @@ -4,22 +4,19 @@ * Copyright (c) 2024 Omar Berrow */ -// Adapted from oboskrnl/arch/x86_64/initial_swap.c - #include #include #include #include #include - #include #include #include -#include +#include #define SWAP_HEADER_MAGIC 0x535741504844524D diff --git a/src/oboskrnl/arch/m68k/memmanip.c b/src/oboskrnl/arch/m68k/memmanip.c index a9dfd34d..99f12947 100644 --- a/src/oboskrnl/arch/m68k/memmanip.c +++ b/src/oboskrnl/arch/m68k/memmanip.c @@ -63,5 +63,5 @@ OBOS_NO_KASAN OBOS_NO_UBSAN size_t strchr(const char* str, char ch) size_t i = 0; for (; str[i] != ch && str[i]; i++) ; - return i; + return i + (str[i] == ch ? 1 : 0); } \ No newline at end of file diff --git a/src/oboskrnl/arch/m68k/mmu.c b/src/oboskrnl/arch/m68k/mmu.c index ffcf54b1..12dcde92 100644 --- a/src/oboskrnl/arch/m68k/mmu.c +++ b/src/oboskrnl/arch/m68k/mmu.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,7 @@ OBOS_NO_UBSAN OBOS_NO_KASAN obos_status Arch_MapPage(page_table pt_root, uintptr if (!(pte3[pte3Index] & PT_FLAGS_RESIDENT)) { obos_status status = OBOS_STATUS_SUCCESS; - uintptr_t phys = Arch_AllocatePhysicalPages(1, 1, &status); + uintptr_t phys = Mm_AllocatePhysicalPages(1, 1, &status); if (obos_is_error(status)) return status; memzero(Arch_MapToHHDM(phys), OBOS_PAGE_SIZE); @@ -67,7 +68,7 @@ OBOS_NO_UBSAN OBOS_NO_KASAN obos_status Arch_MapPage(page_table pt_root, uintptr if (!((uintptr_t)pte2[pte2Index] & PT_FLAGS_RESIDENT)) { obos_status status = OBOS_STATUS_SUCCESS; - uintptr_t phys = Arch_AllocatePhysicalPages(1, 1, &status); + uintptr_t phys = Mm_AllocatePhysicalPages(1, 1, &status); if (obos_is_error(status)) return status; memzero(Arch_MapToHHDM(phys), OBOS_PAGE_SIZE); @@ -106,14 +107,14 @@ OBOS_NO_UBSAN OBOS_NO_KASAN obos_status Arch_UnmapPage(page_table pt_root, uintp if (memcmp_b(pte1, 0, 256)) { pte2[pte2Index] = 0; - Arch_FreePhysicalPages((uintptr_t)Arch_UnmapFromHHDM(pte1), 1); + Mm_FreePhysicalPages((uintptr_t)Arch_UnmapFromHHDM(pte1), 1); if (memcmp_b(pte2, 0, 1024)) { pte3[pte3Index] = 0; - Arch_FreePhysicalPages((uintptr_t)Arch_UnmapFromHHDM(pte2), 1); + Mm_FreePhysicalPages((uintptr_t)Arch_UnmapFromHHDM(pte2), 1); // Don't do this since pte3=pt_root, and we shouldn't be freeing that, or bad stuff might happen. // if (memcmp_b(pte3, 0, 1024)) - // Arch_FreePhysicalPages((uintptr_t)pte3, 1); + // Mm_FreePhysicalPages((uintptr_t)pte3, 1); } } return OBOS_STATUS_SUCCESS; @@ -167,13 +168,13 @@ static basicmm_region kernel_region; static basicmm_region hhdm_region; OBOS_NO_UBSAN OBOS_NO_KASAN void Arch_InitializePageTables() { - page_table newPt = Arch_AllocatePhysicalPages(1, 1, nullptr); + page_table newPt = Mm_AllocatePhysicalPages(1, 1, nullptr); memzero(Arch_MapToHHDM(newPt), 4096); page_table oldPt = 0; asm("movec.l %%srp, %0" :"=a"(oldPt) :); // Map the HHDM. uintptr_t hhdm_base = Arch_HHDMRequest.response->offset; - for (uintptr_t addr = 0; addr < Arch_PhysicalMemoryBoundaries; addr += OBOS_PAGE_SIZE) + for (uintptr_t addr = 0; addr < Mm_PhysicalMemoryBoundaries; addr += OBOS_PAGE_SIZE) Arch_MapPage(newPt, hhdm_base+addr, addr, PT_FLAGS_RESIDENT|PT_FLAGS_CACHE_COPYBACK|PT_FLAGS_SUPERVISOR); // Map the kernel. Elf_Ehdr* ehdr = (Elf_Ehdr*)Arch_KernelFile.response->kernel_file->address; @@ -203,9 +204,9 @@ OBOS_NO_UBSAN OBOS_NO_KASAN void Arch_InitializePageTables() } kernelSize = top-Arch_KernelAddressRequest.response->virtual_base; asm volatile ("movec %0, %%srp" : :"r"(newPt)); - Arch_FreePhysicalPages(oldPt, 1); + Mm_FreePhysicalPages(oldPt, 1); OBOSH_BasicMMAddRegion(&kernel_region, (void*)(uintptr_t)Arch_KernelAddressRequest.response->virtual_base, kernelSize); - OBOSH_BasicMMAddRegion(&hhdm_region, (void*)Arch_MapToHHDM(0), Arch_PhysicalMemoryBoundaries); + OBOSH_BasicMMAddRegion(&hhdm_region, (void*)Arch_MapToHHDM(0), Mm_PhysicalMemoryBoundaries); } OBOS_NO_UBSAN OBOS_NO_KASAN obos_status MmS_QueryPageInfo(page_table pt, uintptr_t addr, page* ppage) { diff --git a/src/oboskrnl/arch/m68k/pmm.c b/src/oboskrnl/arch/m68k/pmm.c index ef5485f3..12598897 100644 --- a/src/oboskrnl/arch/m68k/pmm.c +++ b/src/oboskrnl/arch/m68k/pmm.c @@ -16,154 +16,27 @@ extern volatile struct limine_memmap_request Arch_MemmapRequest; extern volatile struct limine_hhdm_request Arch_HHDMRequest; -struct freelist_node +extern volatile struct limine_memmap_request Arch_MemmapRequest; +extern volatile struct limine_hhdm_request Arch_HHDMRequest; +obos_pmem_map_entry* MmS_GetFirstPMemMapEntry(uintptr_t* index) { - size_t nPages; - struct freelist_node *next, *prev; -}; -static struct freelist_node *s_head; -static struct freelist_node *s_tail; -static size_t s_nNodes; -size_t Arch_TotalPhysicalPages = 0; -size_t Arch_TotalPhysicalPagesUsed = 0; -size_t Arch_UsablePhysicalPages = 0; -uintptr_t Arch_PhysicalMemoryBoundaries; -obos_status Arch_InitializePMM() + *index = 0; + return Arch_MemmapRequest.response->entries[0]; +} +// returns nullptr at the end of the list. +obos_pmem_map_entry* MmS_GetNextPMemMapEntry(obos_pmem_map_entry* current, uintptr_t* index) { - size_t nEntries = Arch_MemmapRequest.response->entry_count; + OBOS_UNUSED(current); + size_t nEntries = (Arch_MemmapRequest.response->entry_count); if (!nEntries) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "No memory map entries.\n"); - for (size_t i = 0; i < nEntries; i++) - { - struct limine_memmap_entry* entry = Arch_MemmapRequest.response->entries[i]; - uintptr_t phys = entry->base; - size_t nPages = entry->length / 0x1000; - if (phys & 0xfff) - { - phys = (phys + 0xfff) & ~0xfff; - nPages--; - } - if (phys == 0x0) - { - phys = 0x1000; - nPages--; - } - Arch_TotalPhysicalPages += nPages; - if ((phys + nPages * 0x1000) > Arch_PhysicalMemoryBoundaries) - Arch_PhysicalMemoryBoundaries = (phys + nPages * 0x1000); - if (entry->type != LIMINE_MEMMAP_USABLE) - continue; - Arch_UsablePhysicalPages += nPages; - struct freelist_node* node = (struct freelist_node*)((uintptr_t)Arch_HHDMRequest.response->offset + phys); - memzero(node, sizeof(*node)); - node->nPages = nPages; - if (s_tail) - ((struct freelist_node*)((uintptr_t)Arch_HHDMRequest.response->offset + (uintptr_t)s_tail))->next = (struct freelist_node*)phys; - if (!s_head) - s_head = (struct freelist_node*)phys; - node->prev = s_tail; - s_tail = (struct freelist_node*)phys; - s_nNodes++; - } - if (Arch_PhysicalMemoryBoundaries & 0xfff) - Arch_PhysicalMemoryBoundaries = (Arch_PhysicalMemoryBoundaries + 0xfff) & ~0xfff; - return OBOS_STATUS_SUCCESS; -} -static bool IsRegionSufficient(struct freelist_node* node, size_t nPages, size_t alignmentMask, size_t *nPagesRequiredP) -{ - size_t nPagesRequired = nPages; - uintptr_t nodePhys = (uintptr_t)node - Arch_HHDMRequest.response->offset; - nPagesRequired += (node->nPages & ((alignmentMask + 1) / 0x1000 - 1)); - uintptr_t mod = nodePhys & alignmentMask; - nPagesRequired += mod / 4096; - if (nPagesRequiredP) - *nPagesRequiredP = nPagesRequired; - return node->nPages >= nPagesRequired; + if ((*index) > nEntries) + return nullptr; + return Arch_MemmapRequest.response->entries[++(*index)]; } #define MAP_TO_HHDM(addr, type) ((type*)((uintptr_t)Arch_HHDMRequest.response->offset + (uintptr_t)(addr))) #define UNMAP_FROM_HHDM(addr) ((uintptr_t)(addr) - (uintptr_t)Arch_HHDMRequest.response->offset) -OBOS_NO_UBSAN OBOS_NO_KASAN uintptr_t Arch_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status *status) -{ - if (!nPages) - { - *status = OBOS_STATUS_INVALID_ARGUMENT; - return 0; - } - if (!alignmentPages) - alignmentPages = 1; - if (__builtin_popcount(alignmentPages) > 1) - { - *status = OBOS_STATUS_INVALID_ARGUMENT; - return 0; - } - if (!s_head) - OBOS_Panic(OBOS_PANIC_NO_MEMORY, "No more avaliable physical memory!\n"); - uintptr_t phys = 0; - if (nPages % alignmentPages) - nPages += (alignmentPages - (nPages % alignmentPages)); - size_t alignmentMask = alignmentPages*0x1000-1; - size_t nPagesRequired = 0; - struct freelist_node* node = MAP_TO_HHDM(s_head, struct freelist_node); - while (UNMAP_FROM_HHDM(node) && !IsRegionSufficient(node, nPages, alignmentMask, &nPagesRequired)) - node = MAP_TO_HHDM(node->next, struct freelist_node); - if (!UNMAP_FROM_HHDM(node)) - { - if (status) - *status = OBOS_STATUS_NOT_ENOUGH_MEMORY; - return 0; - } - OBOS_ASSERT(node->nPages >= nPagesRequired); - node->nPages -= nPagesRequired; - Arch_TotalPhysicalPagesUsed += nPagesRequired; - if (!node->nPages) - { - if (node->next) - MAP_TO_HHDM(node->next, struct freelist_node)->prev = node->prev; - if (node->prev) - MAP_TO_HHDM(node->prev, struct freelist_node)->next = node->next; - if ((uintptr_t)s_head == UNMAP_FROM_HHDM(node)) - s_head = node->next; - if ((uintptr_t)s_tail == UNMAP_FROM_HHDM(node)) - s_tail = node->prev; - s_nNodes--; - node->next = nullptr; - node->prev = nullptr; - } - phys = (uintptr_t)node + node->nPages * 0x1000; - phys -= Arch_HHDMRequest.response->offset; - if (status) - *status = OBOS_STATUS_SUCCESS; - return phys; -} -OBOS_NO_UBSAN OBOS_NO_KASAN void Arch_FreePhysicalPages(uintptr_t addr, size_t nPages) -{ - OBOS_ASSERT(addr); - OBOS_ASSERT(!(addr & 0xfff)); - addr &= ~0xfff; - if (!addr) - return; - struct freelist_node* node = (struct freelist_node*)(uintptr_t)(Arch_HHDMRequest.response->offset+ addr); - node->nPages = nPages; - if (s_tail) - MAP_TO_HHDM(s_tail, struct freelist_node)->next = (struct freelist_node*)addr; - if (!s_head) - s_head = (struct freelist_node*)addr; - node->prev = s_tail; - Arch_TotalPhysicalPagesUsed -= nPages; - s_tail = (struct freelist_node*)addr; -} -OBOS_NO_UBSAN OBOS_NO_KASAN uintptr_t OBOSS_AllocatePhysicalPages(size_t nPages, size_t alignment, obos_status* status) -{ - return Arch_AllocatePhysicalPages(nPages, alignment, status); -} -OBOS_NO_UBSAN OBOS_NO_KASAN obos_status OBOSS_FreePhysicalPages(uintptr_t base, size_t nPages) -{ - base &= ~0xfff; - if (!base) - return OBOS_STATUS_INVALID_ARGUMENT; - Arch_FreePhysicalPages(base, nPages); - return OBOS_STATUS_SUCCESS; -} + OBOS_NO_UBSAN OBOS_NO_KASAN void* Arch_MapToHHDM(uintptr_t phys) { return MAP_TO_HHDM(phys, void); @@ -171,4 +44,12 @@ OBOS_NO_UBSAN OBOS_NO_KASAN void* Arch_MapToHHDM(uintptr_t phys) OBOS_NO_UBSAN OBOS_NO_KASAN uintptr_t Arch_UnmapFromHHDM(void* virt) { return UNMAP_FROM_HHDM(virt); +} +void* MmS_MapVirtFromPhys(uintptr_t addr) +{ + return Arch_MapToHHDM(addr); +} +uintptr_t MmS_UnmapVirtFromPhys(void* virt) +{ + return Arch_UnmapFromHHDM(virt); } \ No newline at end of file diff --git a/src/oboskrnl/arch/m68k/pmm.h b/src/oboskrnl/arch/m68k/pmm.h index 8e89a87c..5bea3a4f 100644 --- a/src/oboskrnl/arch/m68k/pmm.h +++ b/src/oboskrnl/arch/m68k/pmm.h @@ -8,32 +8,16 @@ #include #include -#include -extern size_t Arch_TotalPhysicalPages; -extern size_t Arch_TotalPhysicalPagesUsed; -extern size_t Arch_UsablePhysicalPages; -extern uintptr_t Arch_PhysicalMemoryBoundaries; +#include -/// -/// Initializes the PMM. -/// -/// The function status. -obos_status Arch_InitializePMM(); +typedef struct limine_memmap_entry obos_pmem_map_entry; +#define pmem_map_base base +#define pmem_map_size length +#define pmem_map_type type +#define PHYSICAL_MEMORY_TYPE_USABLE LIMINE_MEMMAP_USABLE +#define PHYSICAL_MEMORY_TYPE_RECLAIMABLE LIMINE_MEMMAP_ACPI_RECLAIMABLE +#define PHYSICAL_MEMORY_TYPE_LOADER_RECLAIMABLE LIMINE_MEMMAP_LOADER_RECLAIMABLE -/// -/// Allocates physical pages. -/// -/// The amount of physical pages to allocate. -/// The alignment of the address returned, in pages. -/// [optional] A pointer to a variable that will store the function's status. Can be nullptr. -/// The physical pages, or zero on failure. -uintptr_t Arch_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status* status); -/// -/// Frees physical pages. -/// -/// The address of the page. -/// The amount of pages to free. -void Arch_FreePhysicalPages(uintptr_t addr, size_t nPages); void* Arch_MapToHHDM(uintptr_t phys); uintptr_t Arch_UnmapFromHHDM(void* virt); \ No newline at end of file diff --git a/src/oboskrnl/arch/m68k/thread_ctx.c b/src/oboskrnl/arch/m68k/thread_ctx.c index 56594075..f94dba25 100644 --- a/src/oboskrnl/arch/m68k/thread_ctx.c +++ b/src/oboskrnl/arch/m68k/thread_ctx.c @@ -20,6 +20,14 @@ irql CoreS_GetThreadIRQL(const thread_ctx* ctx) { return ctx->irql; } +void* CoreS_GetThreadStack(const thread_ctx* ctx) +{ + return ctx->stackBase; +} +size_t CoreS_GetThreadStackSize(const thread_ctx* ctx) +{ + return ctx->stackSize; +} obos_status CoreS_SetupThreadContext(thread_ctx* ctx, uintptr_t entry, uintptr_t arg1, bool makeUserMode, void* stackBase, size_t stackSize) { if (!ctx) diff --git a/src/oboskrnl/arch/x86_64/boot_info.h b/src/oboskrnl/arch/x86_64/boot_info.h index c9bc6f89..ab03c447 100644 --- a/src/oboskrnl/arch/x86_64/boot_info.h +++ b/src/oboskrnl/arch/x86_64/boot_info.h @@ -13,4 +13,5 @@ extern struct ultra_platform_info_attribute* Arch_LdrPlatformInfo; extern struct ultra_kernel_info_attribute* Arch_KernelInfo; extern struct ultra_module_info_attribute* Arch_KernelBinary; extern struct ultra_module_info_attribute* Arch_InitialSwapBuffer; -extern struct ultra_framebuffer* Arch_Framebuffer; \ No newline at end of file +extern struct ultra_framebuffer* Arch_Framebuffer; +extern struct ultra_boot_context* Arch_BootContext; \ No newline at end of file diff --git a/src/oboskrnl/arch/x86_64/drv_loader.c b/src/oboskrnl/arch/x86_64/drv_loader.c index bbfe58cc..848fcb24 100644 --- a/src/oboskrnl/arch/x86_64/drv_loader.c +++ b/src/oboskrnl/arch/x86_64/drv_loader.c @@ -151,7 +151,7 @@ static void add_dependency(driver_id* depends, driver_id* dependency) } static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* symbolTable, Elf64_Off stringTable, const void* file, struct relocation i, void* base, size_t szProgram, Elf64_Addr* GOT, struct copy_reloc_array* copy_relocations, uintptr_t hashTableOffset) { - driver_symbol* Symbol; + driver_symbol* Symbol = nullptr; driver_symbol internal_symbol = {}; if (i.symbolTableOffset) { @@ -175,6 +175,8 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* (uint8_t*)file, base, symbolTable, hashTableOffset, stringTable, OffsetPtr(base, stringTable + Unresolved_Symbol->st_name, const char*) ); + if (!obos_expect((uintptr_t)sym != 0, 1)) + goto unresolved; if (sym->st_shndx == 0) goto unresolved; internal_symbol.address = OffsetPtr(base, sym->st_value, uintptr_t); @@ -245,10 +247,12 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* case R_AMD64_NONE: return true; case R_AMD64_64: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend; relocSize = 8; break; case R_AMD64_PC32: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend - relocAddr; relocSize = 4; break; @@ -273,6 +277,7 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* { // Save copy relocations for the end because if we don't, it might contain unresolved addresses. // copy_relocations.push_back({ (void*)relocAddr, (void*)Symbol->address, Symbol->size }); + OBOS_ASSERT(Symbol); struct copy_reloc reloc = { (void*)relocAddr, (void*)Symbol->address, Symbol->size }; append_copy_reloc(copy_relocations, &reloc); relocSize = 0; @@ -280,6 +285,7 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* } case R_AMD64_JUMP_SLOT: case R_AMD64_GLOB_DAT: + OBOS_ASSERT(Symbol); relocResult = Symbol->address; relocSize = 8; break; @@ -297,34 +303,42 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* relocSize = 8; break; case R_AMD64_32: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend; relocSize = 4; break; case R_AMD64_32S: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend; relocSize = 4; break; case R_AMD64_16: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend; relocSize = 2; break; case R_AMD64_PC16: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend - relocAddr; relocSize = 2; break; case R_AMD64_8: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend; relocSize = 1; break; case R_AMD64_PC8: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend - relocAddr; relocSize = 1; break; case R_AMD64_PC64: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend - relocAddr; relocSize = 8; break; case R_AMD64_GOTOFF64: + OBOS_ASSERT(Symbol); relocResult = Symbol->address + i.addend - ((uint64_t)GOT); relocSize = 8; break; @@ -334,10 +348,12 @@ static bool calculate_relocation(obos_status* status, driver_id* drv, Elf64_Sym* break; case R_AMD64_SIZE32: relocSize = 4; + OBOS_ASSERT(Symbol); relocResult = Symbol->size + i.addend; break; case R_AMD64_SIZE64: relocSize = 8; + OBOS_ASSERT(Symbol); relocResult = Symbol->size + i.addend; break; default: @@ -384,7 +400,7 @@ void* DrvS_LoadRelocatableElf(driver_id* driver, const void* file, size_t szFile end = (void*)(curr->p_vaddr+curr->p_memsz); } szProgram = (size_t)end; - void* base = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, szProgram, 0, VMA_FLAGS_GUARD_PAGE, status); + void* base = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, szProgram, 0, VMA_FLAGS_GUARD_PAGE, nullptr, status); if (!base) return nullptr; // The pages are mapped in. @@ -398,6 +414,7 @@ void* DrvS_LoadRelocatableElf(driver_id* driver, const void* file, size_t szFile // NOTE: Possible buffer overflow memzero(OffsetPtr(base, curr->p_vaddr + curr->p_offset, void*), curr->p_memsz - curr->p_filesz); } + OBOS_ASSERT(obos_expect((uintptr_t)dynamic != 0, 1)); // Apply relocations. struct relocation_array relocations = {.buf=nullptr,.nRelocations=0}; Elf64_Sym* symbolTable = 0; diff --git a/src/oboskrnl/arch/x86_64/entry.c b/src/oboskrnl/arch/x86_64/entry.c index 6f8502e5..6ce4c35e 100644 --- a/src/oboskrnl/arch/x86_64/entry.c +++ b/src/oboskrnl/arch/x86_64/entry.c @@ -32,8 +32,6 @@ #include -#include - #include #include #include @@ -49,6 +47,7 @@ #include +#include #include #include #include @@ -76,9 +75,14 @@ #include "gdbstub/general_query.h" #include "gdbstub/stop_reply.h" #include "gdbstub/bp.h" -#include "locks/wait.h" +#include "mm/pmm.h" + +#include + +#include #include +#include #include #include @@ -93,6 +97,7 @@ static swap_dev swap; static thread kernelMainThread; static thread_node kernelMainThreadNode; +struct ultra_boot_context* Arch_BootContext; static cpu_local bsp_cpu; extern void Arch_IdleTask(); @@ -111,6 +116,7 @@ OBOS_PAGEABLE_FUNCTION void Arch_KernelEntry(struct ultra_boot_context* bcontext // This call will ensure the IRQL is at the default IRQL (IRQL_MASKED). Core_GetIrql(); ParseBootContext(bcontext); + Arch_BootContext = bcontext; asm("sti"); if (!Arch_Framebuffer) OBOS_Warning("No framebuffer passed by the bootloader. All kernel logs will be on port 0xE9.\n"); @@ -197,9 +203,26 @@ struct ultra_kernel_info_attribute* Arch_KernelInfo; struct ultra_module_info_attribute* Arch_KernelBinary; struct ultra_module_info_attribute* Arch_InitialSwapBuffer; struct ultra_module_info_attribute* Arch_UARTDriver; +struct ultra_module_info_attribute* Arch_InitRDDriver; struct ultra_framebuffer* Arch_Framebuffer; extern obos_status Arch_InitializeInitialSwapDevice(swap_dev* dev, void* buf, size_t size); -static OBOS_PAGEABLE_FUNCTION OBOS_NO_KASAN void ParseBootContext(struct ultra_boot_context* bcontext) +static OBOS_PAGEABLE_FUNCTION OBOS_NO_UBSAN struct ultra_module_info_attribute* FindBootModule(struct ultra_boot_context* bcontext, const char* name, size_t nameLen) +{ + if (!nameLen) + nameLen = strlen(name); + struct ultra_attribute_header* header = bcontext->attributes; + for (size_t i = 0; i < bcontext->attribute_count; i++, header = ULTRA_NEXT_ATTRIBUTE(header)) + { + if (header->type == ULTRA_ATTRIBUTE_MODULE_INFO) + { + struct ultra_module_info_attribute* module = (struct ultra_module_info_attribute*)header; + if (uacpi_strncmp(module->name, name, nameLen) == 0) + return module; + } + } + return nullptr; +} +static OBOS_PAGEABLE_FUNCTION OBOS_NO_KASAN OBOS_NO_UBSAN void ParseBootContext(struct ultra_boot_context* bcontext) { struct ultra_attribute_header* header = bcontext->attributes; for (size_t i = 0; i < bcontext->attribute_count; i++, header = ULTRA_NEXT_ATTRIBUTE(header)) @@ -288,8 +311,7 @@ OBOS_NO_UBSAN void Arch_PageFaultHandler(interrupt_frame* frame) break; default: { - static const char format[] = "Handling page fault with error code 0x%x on address %p failed.\n"; - OBOS_Warning(format, mm_ec, getCR2()); + OBOS_Warning("Handling page fault with error code 0x%x on address %p failed with status %d.\n", mm_ec, getCR2(), status); break; } } @@ -549,7 +571,7 @@ void Arch_KernelMainBootstrap() //Core_Yield(); irql oldIrql = Core_RaiseIrql(IRQL_DISPATCH); OBOS_Debug("%s: Initializing PMM.\n", __func__); - Arch_InitializePMM(); + Mm_InitializePMM(); OBOS_Debug("%s: Initializing page tables.\n", __func__); obos_status status = Arch_InitializeKernelPageTable(); if (obos_is_error(status)) @@ -560,6 +582,28 @@ void Arch_KernelMainBootstrap() OBOS_KernelAllocator = (allocator_info*)&kalloc; OBOS_Debug("%s: Parsing command line.\n", __func__); OBOS_ParseCMDLine(); + { + char* initrd_module_name = OBOS_GetOPTS("initrd-module"); + char* initrd_driver_module_name = OBOS_GetOPTS("initrd-driver-module"); + if (initrd_module_name && initrd_driver_module_name) + { + OBOS_Debug("InitRD module name: %s, InitRD driver name: %s.\n", initrd_module_name, initrd_driver_module_name); + struct ultra_module_info_attribute* initrd = FindBootModule(Arch_BootContext, initrd_module_name, 0); + Arch_InitRDDriver = FindBootModule(Arch_BootContext, initrd_driver_module_name, 0); + if (!Arch_InitRDDriver) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not find module %s.\n", initrd_driver_module_name); + if (!initrd) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not find module %s.\n", initrd_module_name); + OBOS_InitrdBinary = (const char*)initrd->address; + OBOS_InitrdSize = initrd->size; + } + else + OBOS_Warning("Could not find either 'initrd-module' or 'initrd-driver-module'. Kernel will run without an initrd.\n"); + if (initrd_module_name) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, initrd_module_name, strlen(initrd_module_name)); + if (initrd_driver_module_name) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, initrd_driver_module_name, strlen(initrd_driver_module_name)); + } OBOS_Debug("%s: Initializing kernel process.\n", __func__); OBOS_KernelProcess = Core_ProcessAllocate(&status); if (obos_is_error(status)) @@ -619,7 +663,7 @@ void Arch_KernelMainBootstrap() // ; OBOS_Debug("Mapping framebuffer as Write-Combining.\n"); size_t size = (Arch_Framebuffer->height*Arch_Framebuffer->pitch + OBOS_HUGE_PAGE_SIZE - 1) & ~(OBOS_HUGE_PAGE_SIZE - 1); - void* base_ = Mm_VirtualMemoryAlloc(&Mm_KernelContext, (void*)0xffffa00000000000, size, 0, VMA_FLAGS_NON_PAGED | VMA_FLAGS_HINT | VMA_FLAGS_HUGE_PAGE, nullptr); + void* base_ = Mm_VirtualMemoryAlloc(&Mm_KernelContext, (void*)0xffffa00000000000, size, 0, VMA_FLAGS_NON_PAGED | VMA_FLAGS_HINT | VMA_FLAGS_HUGE_PAGE, nullptr, nullptr); uintptr_t base = (uintptr_t)base_; if (base) { @@ -648,14 +692,14 @@ void Arch_KernelMainBootstrap() // Present,Write,XD,Write-Combining (PAT: 0b110) Arch_MapHugePage(Mm_KernelContext.pt, (void*)addr, phys, BIT_TYPE(0, UL)|BIT_TYPE(1, UL)|BIT_TYPE(63, UL)|BIT_TYPE(4, UL)|BIT_TYPE(12, UL)); offset = curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; - OBOSS_FreePhysicalPages(oldPhys, offset); + Mm_FreePhysicalPages(oldPhys, offset/OBOS_PAGE_SIZE); } } OBOS_TextRendererState.fb.backbuffer_base = Mm_VirtualMemoryAlloc( &Mm_KernelContext, (void*)(0xffffa00000000000+size), size, 0, VMA_FLAGS_NON_PAGED | VMA_FLAGS_HINT | VMA_FLAGS_HUGE_PAGE | VMA_FLAGS_GUARD_PAGE, - nullptr); + nullptr, nullptr); memcpy(OBOS_TextRendererState.fb.backbuffer_base, OBOS_TextRendererState.fb.base, OBOS_TextRendererState.fb.height*OBOS_TextRendererState.fb.pitch); OBOS_TextRendererState.fb.base = base_; OBOS_TextRendererState.fb.modified_line_bitmap = OBOS_KernelAllocator->ZeroAllocate( @@ -696,8 +740,8 @@ if (st != UACPI_STATUS_OK)\ st = uacpi_finalize_gpe_initialization(); verify_status(st, uacpi_finalize_gpe_initialization); - Arch_IOAPICMaskIRQ(9, false); + OBOS_Debug("%s: Loading kernel symbol table.\n", __func__); Elf64_Ehdr* ehdr = (Elf64_Ehdr*)Arch_KernelBinary->address; Elf64_Shdr* sectionTable = (Elf64_Shdr*)(Arch_KernelBinary->address + ehdr->e_shoff); @@ -762,48 +806,91 @@ if (st != UACPI_STATUS_OK)\ } RB_INSERT(symbol_table, &OBOS_KernelSymbolTable, symbol); } - OBOS_Debug("Loading drivers through PnP.\n"); - void* driver_bin = (void*)Arch_UARTDriver->address; - size_t sizeof_driver_bin = Arch_UARTDriver->size; - driver_header header; - status = Drv_LoadDriverHeader(driver_bin, sizeof_driver_bin, &header); - if (obos_is_error(status)) - OBOS_Error("Could not load test driver #1. Status: %d.\n", status); - driver_header_node node = {.data = &header}; - driver_header_list list = {.head=&node,.tail=&node,.nNodes=1}; - driver_header_list toLoad; - memzero(&toLoad, sizeof(toLoad)); - status = Drv_PnpDetectDrivers(list, &toLoad); - if (obos_is_error(status)) - OBOS_Error("PnP failed. Status: %d.\n", status); - driver_id* drv1 = nullptr; - for (driver_header_node* node = toLoad.head; node; ) + if (Arch_InitRDDriver) { - OBOS_Debug("Loading driver '%s'.\n", node->data->driverName); - if (&header == node->data) - drv1 = Drv_LoadDriver(driver_bin, sizeof_driver_bin, nullptr); - - node = node->next; + OBOS_Log("Loading InitRD driver.\n"); + // Load the InitRD driver. + status = OBOS_STATUS_SUCCESS; + driver_id* drv = + Drv_LoadDriver((void*)Arch_InitRDDriver->address, Arch_InitRDDriver->size, &status); + if (obos_is_error(status)) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not load the InitRD driver passed in module %s.\nStatus: %d.\n", Arch_InitRDDriver->name, status); + status = Drv_StartDriver(drv, nullptr); + if (obos_is_error(status) && status != OBOS_STATUS_NO_ENTRY_POINT) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not start the InitRD driver passed in module %s.\nStatus: %d.\nNote: This is a bug, please report it.\n", Arch_InitRDDriver->name, status); + OBOS_Log("Loaded InitRD driver.\n"); } - thread* main = nullptr; - Drv_StartDriver(drv1, &main); - while (!(main->flags & THREAD_FLAGS_DIED)) - Core_Yield(); - dev_desc connection = 0; - obos_status com1_status = - status = - drv1->header.ftable.ioctl(6, /* IOCTL_OPEN_SERIAL_CONNECTION */0, - 1, - 115200, - /* EIGHT_DATABITS */ 3, - /* ONE_STOPBIT */ 0, - /* PARITYBIT_NONE */ 0, - &connection - ); - if (obos_is_error(status)) - OBOS_Error("Could not open COM1. Status: %d.\n", status); + else + { + OBOS_Debug("No InitRD driver!\n"); + OBOS_Debug("Scanning command line...\n"); + char* modules_to_load = OBOS_GetOPTS("load-modules"); + if (!modules_to_load) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "No initrd, and no drivers passed via the command line. Further boot is impossible.\n"); + size_t len = strlen(modules_to_load); + char* iter = modules_to_load; + while(iter < (modules_to_load + len)) + { + status = OBOS_STATUS_SUCCESS; + size_t namelen = strchr(modules_to_load, ','); + if (namelen != len) + namelen--; + OBOS_Debug("Loading driver %.*s.\n", namelen, iter); + if (uacpi_strncmp(iter, "__KERNEL__", namelen) == 0) + { + OBOS_Error("Cannot load the kernel (__KERNEL__) as a driver.\n"); + if (namelen != len) + namelen++; + iter += namelen; + continue; + } + struct ultra_module_info_attribute* module = FindBootModule(Arch_BootContext, iter, namelen); + if (!module) + { + OBOS_Warning("Could not load driver %s. Status: %d\n", module->name, OBOS_STATUS_NOT_FOUND); + if (namelen != len) + namelen++; + iter += namelen; + continue; + } + driver_id* drv = + Drv_LoadDriver((void*)module->address, module->size, &status); + if (obos_is_error(status)) + { + OBOS_Warning("Could not load driver %s. Status: %d\n", module->name, status); + if (namelen != len) + namelen++; + iter += namelen; + continue; + } + status = Drv_StartDriver(drv, nullptr); + if (obos_is_error(status) && status != OBOS_STATUS_NO_ENTRY_POINT) + { + OBOS_Warning("Could not start driver %s. Status: %d\n", module->name, status); + status = Drv_UnloadDriver(drv); + if (obos_is_error(status)) + OBOS_Warning("Could not unload driver %s. Status: %d\n", module->name, status); + if (namelen != len) + namelen++; + iter += namelen; + continue; + } + if (namelen != len) + namelen++; + iter += namelen; + } + } + OBOS_Debug("%s: Initializing VFS.\n", __func__); + Vfs_Initialize(); + OBOS_Debug("%s: Loading drivers through PnP.\n", __func__); + // TODO: + // Load PnP drivers. + OBOS_Debug("%s: Unimplemented.\n", __func__); + OBOS_Debug("%s: Finalizing VFS initialization...\n", __func__); + Vfs_FinalizeInitialization(); + // OBOS_Debug("%s: Loading init program...\n", __func__); static gdb_connection gdb_conn = {}; - Kdbg_ConnectionInitialize(&gdb_conn, &drv1->header.ftable, connection); + // Kdbg_ConnectionInitialize(&gdb_conn, &drv1->header.ftable, connection); Kdbg_AddPacketHandler("qC", Kdbg_GDB_qC, nullptr); Kdbg_AddPacketHandler("qfThreadInfo", Kdbg_GDB_q_ThreadInfo, nullptr); Kdbg_AddPacketHandler("qsThreadInfo", Kdbg_GDB_q_ThreadInfo, nullptr); @@ -828,199 +915,17 @@ if (st != UACPI_STATUS_OK)\ Arch_RawRegisterInterrupt(0x3, (uintptr_t)(void*)Kdbg_int3_handler); Arch_RawRegisterInterrupt(0x1, (uintptr_t)(void*)Kdbg_int1_handler); Kdbg_CurrentConnection = &gdb_conn; - if (obos_is_success(com1_status) && OBOS_GetOPTF("enable-kdbg")) + if (OBOS_GetOPTF("enable-kdbg") && Kdbg_CurrentConnection->pipe_interface->read_sync) { OBOS_Debug("%s: Enabling KDBG.\n", __func__); Kdbg_CurrentConnection->connection_active = true; asm("int3"); } OBOS_Log("%s: Done early boot.\n", __func__); - Core_ExitCurrentThread(); -} -void OBOS_TestLocks() -{ - OBOS_Debug("Testing semaphores.\n"); - const size_t nThreads = 3; - sem = (semaphore)SEMAPHORE_INITIALIZE(nThreads); - thread* threads = OBOS_NonPagedPoolAllocator->ZeroAllocate(OBOS_NonPagedPoolAllocator, nThreads, sizeof(thread), nullptr); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - thread_ctx ctx = {}; - CoreS_SetupThreadContext(&ctx, (uintptr_t)semaphore_test, (uintptr_t)&sem, false, Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr), 0x10000); - CoreH_ThreadInitialize(thr, THREAD_PRIORITY_NORMAL, Core_DefaultThreadAffinity, &ctx); - } - OBOS_Debug("Acquiring semaphore %d times.\n", nThreads); - for (size_t i = 0; i < nThreads; i++) - Core_SemaphoreAcquire(&sem); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - CoreH_ThreadReady(thr); - } - OBOS_Debug("Sleeping for 5 seconds...\n"); - timer_tick deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(1000000); - while(CoreS_GetTimerTick() < deadline) - ; - OBOS_Debug("Done.\n"); - OBOS_Debug("Releasing semaphore %d times.\n", nThreads); - for (size_t i = 0; i < nThreads; i++) - Core_SemaphoreRelease(&sem); - while(sem.count < nThreads) - asm("pause"); - bool allThreadsDead = false; - while(!allThreadsDead) - { - allThreadsDead = true; - for (size_t i = 0; i < nThreads && allThreadsDead; i++) - { - if (!(threads[i].flags & THREAD_FLAGS_DIED)) - allThreadsDead = false; - } - } - memzero(threads, sizeof(thread)*3); - OBOS_Debug("Testing mutexes.\n"); - mut = MUTEX_INITIALIZE(); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - thread_ctx ctx = {}; - CoreS_SetupThreadContext(&ctx, (uintptr_t)mutex_test, (uintptr_t)&mut, false, Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr), 0x10000); - CoreH_ThreadInitialize(thr, THREAD_PRIORITY_NORMAL, Core_DefaultThreadAffinity, &ctx); - } - Core_MutexAcquire(&mut); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - CoreH_ThreadReady(thr); - } - OBOS_Debug("Sleeping for 5 seconds...\n"); - deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(5000000); - while(CoreS_GetTimerTick() < deadline) - ; - OBOS_Debug("Releasing mutex.\n"); - Core_MutexRelease(&mut); - allThreadsDead = false; - while(!allThreadsDead) - { - allThreadsDead = true; - for (size_t i = 0; i < nThreads && allThreadsDead; i++) - { - if (!(threads[i].flags & THREAD_FLAGS_DIED)) - allThreadsDead = false; - } - } - memzero(threads, sizeof(thread)*3); - OBOS_Debug("Testing events.\n"); - e = EVENT_INITIALIZE(EVENT_SYNC); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - thread_ctx ctx = {}; - CoreS_SetupThreadContext(&ctx, (uintptr_t)event_test, (uintptr_t)&e, false, Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr), 0x10000); - CoreH_ThreadInitialize(thr, THREAD_PRIORITY_NORMAL, Core_DefaultThreadAffinity, &ctx); - CoreH_ThreadReady(thr); - } - OBOS_Debug("Sleeping for 5 seconds...\n"); - deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(5000000); - while(CoreS_GetTimerTick() < deadline) - ; - OBOS_Debug("Setting event.\n"); - Core_EventSet(&e, true); - allThreadsDead = false; - while(!allThreadsDead) - { - allThreadsDead = true; - for (size_t i = 0; i < nThreads && allThreadsDead; i++) - { - if (!(threads[i].flags & THREAD_FLAGS_DIED)) - allThreadsDead = false; - } - } - memzero(threads, sizeof(thread)*3); - OBOS_Debug("Testing notification events.\n"); - static event events[3] = {}; - events[0] = EVENT_INITIALIZE(EVENT_NOTIFICATION); - events[1] = EVENT_INITIALIZE(EVENT_NOTIFICATION); - events[2] = EVENT_INITIALIZE(EVENT_NOTIFICATION); - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - thread_ctx ctx = {}; - CoreS_SetupThreadContext(&ctx, (uintptr_t)not_event_test, (uintptr_t)&events, false, Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr), 0x10000); - CoreH_ThreadInitialize(thr, THREAD_PRIORITY_NORMAL, Core_DefaultThreadAffinity, &ctx); - CoreH_ThreadReady(thr); - } - for (size_t i = 0; i < nThreads; i++) - { - thread* thr = &threads[i]; - CoreH_ThreadReady(thr); - } - for (size_t i = 0; i < 3; i++) - { - deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(1000000); - while(CoreS_GetTimerTick() < deadline) - ; - Core_EventSet(&events[i], true); - } - allThreadsDead = false; - while(!allThreadsDead) - { - allThreadsDead = true; - for (size_t i = 0; i < nThreads && allThreadsDead; i++) - { - if (!(threads[i].flags & THREAD_FLAGS_DIED)) - allThreadsDead = false; - } - } - Core_EventClear(&events[0]); - Core_EventClear(&events[1]); - Core_EventClear(&events[2]); - OBOS_Debug("Done.\n"); - OBOS_NonPagedPoolAllocator->Free(OBOS_NonPagedPoolAllocator, threads, sizeof(thread)*3); -} -static void semaphore_test(void* userdata) -{ - semaphore* sem = (semaphore*)userdata; - OBOS_Debug("Thread %d attempted to acquire a semaphore.\n", Core_GetCurrentThread()->tid); - Core_SemaphoreAcquire(sem); - OBOS_Debug("Thread %d done.\n", Core_GetCurrentThread()->tid); - Core_SemaphoreRelease(sem); - Core_ExitCurrentThread(); -} -static void mutex_test(void* userdata) -{ - mutex* mut = (mutex*)userdata; - OBOS_Debug("Thread %d attempted to acquire a mutex.\n", Core_GetCurrentThread()->tid); - Core_MutexAcquire(mut); - OBOS_Debug("Thread %d acquired mutex.\n", Core_GetCurrentThread()->tid); - OBOS_Debug("Sleeping for one second before releasing...\n"); - timer_tick deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(1000000); - while(CoreS_GetTimerTick() < deadline) - ; - Core_MutexRelease(mut); - OBOS_Debug("Done\n"); - Core_ExitCurrentThread(); -} -static void event_test(void* userdata) -{ - event* e = (event*)userdata; - OBOS_Debug("Thread %d is waiting on an event.\n", Core_GetCurrentThread()->tid); - Core_WaitOnObject(WAITABLE_OBJECT(*e)); - Core_EventClear(e); - OBOS_Debug("Sleeping for one second before setting event...\n"); - timer_tick deadline = CoreS_GetTimerTick() + CoreH_TimeFrameToTick(1000000); - while(CoreS_GetTimerTick() < deadline) - ; - Core_EventSet(e, true); - OBOS_Debug("Thread %d done.\n", Core_GetCurrentThread()->tid); - Core_ExitCurrentThread(); -} -static void not_event_test(void* userdata) -{ - event* events = (event*)userdata; - OBOS_Debug("Thread %d waiting on events.\n", Core_GetCurrentThread()->tid); - Core_WaitOnObjects(3, WAITABLE_OBJECT(events[0]), WAITABLE_OBJECT(events[1]), WAITABLE_OBJECT(events[2])); - OBOS_Debug("Thread %d is done.\n", Core_GetCurrentThread()->tid); + OBOS_Log("Currently at %ld KiB of committed memory (%ld KiB pageable), %ld KiB paged out, and %ld KiB non-paged.\n", + Mm_KernelContext.stat.committedMemory/0x400, + Mm_KernelContext.stat.pageable/0x400, + Mm_KernelContext.stat.paged/0x400, + Mm_KernelContext.stat.nonPaged/0x400); Core_ExitCurrentThread(); } \ No newline at end of file diff --git a/src/oboskrnl/arch/x86_64/initial_swap.c b/src/oboskrnl/arch/x86_64/initial_swap.c index 17b1f636..41b4cf06 100644 --- a/src/oboskrnl/arch/x86_64/initial_swap.c +++ b/src/oboskrnl/arch/x86_64/initial_swap.c @@ -4,8 +4,8 @@ * Copyright (c) 2024 Omar Berrow */ -#include "arch/x86_64/pmm.h" -#include "irq/irql.h" +// Adapted from oboskrnl/arch/m68k/initial_swap.c + #include #include #include @@ -13,11 +13,14 @@ #include #include -#include #include +#include + #include +#include + #define SWAP_HEADER_MAGIC 0x535741504844524D typedef struct swap_page diff --git a/src/oboskrnl/arch/x86_64/map.c b/src/oboskrnl/arch/x86_64/map.c index 6b50d53f..561e4c84 100644 --- a/src/oboskrnl/arch/x86_64/map.c +++ b/src/oboskrnl/arch/x86_64/map.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -43,7 +43,7 @@ OBOS_NO_KASAN uintptr_t Arch_GetPML4Entry(uintptr_t pml4Base, uintptr_t addr) { if (!pml4Base) return 0; - uintptr_t* arr = (uintptr_t*)Arch_MapToHHDM(Arch_MaskPhysicalAddressFromEntry(pml4Base)); + uintptr_t* arr = (uintptr_t*)MmS_MapVirtFromPhys(Arch_MaskPhysicalAddressFromEntry(pml4Base)); return arr[AddressToIndex(addr, 3)]; } OBOS_NO_KASAN uintptr_t Arch_GetPML3Entry(uintptr_t pml4Base, uintptr_t addr) @@ -51,7 +51,7 @@ OBOS_NO_KASAN uintptr_t Arch_GetPML3Entry(uintptr_t pml4Base, uintptr_t addr) uintptr_t phys = Arch_MaskPhysicalAddressFromEntry(Arch_GetPML4Entry(pml4Base, addr)); if (!phys) return 0; - uintptr_t* arr = (uintptr_t*)Arch_MapToHHDM(phys); + uintptr_t* arr = (uintptr_t*)MmS_MapVirtFromPhys(phys); return arr[AddressToIndex(addr, 2)]; } OBOS_NO_KASAN uintptr_t Arch_GetPML2Entry(uintptr_t pml4Base, uintptr_t addr) @@ -59,7 +59,7 @@ OBOS_NO_KASAN uintptr_t Arch_GetPML2Entry(uintptr_t pml4Base, uintptr_t addr) uintptr_t phys = Arch_MaskPhysicalAddressFromEntry(Arch_GetPML3Entry(pml4Base, addr)); if (!phys) return 0; - uintptr_t* arr = (uintptr_t*)Arch_MapToHHDM(phys); + uintptr_t* arr = (uintptr_t*)MmS_MapVirtFromPhys(phys); return arr[AddressToIndex(addr, 1)]; } OBOS_NO_KASAN uintptr_t Arch_GetPML1Entry(uintptr_t pml4Base, uintptr_t addr) @@ -67,7 +67,7 @@ OBOS_NO_KASAN uintptr_t Arch_GetPML1Entry(uintptr_t pml4Base, uintptr_t addr) uintptr_t phys = Arch_MaskPhysicalAddressFromEntry(Arch_GetPML2Entry(pml4Base, addr)); if (!phys) return 0; - uintptr_t* arr = (uintptr_t*)Arch_MapToHHDM(phys); + uintptr_t* arr = (uintptr_t*)MmS_MapVirtFromPhys(phys); return arr[AddressToIndex(addr, 0)]; } @@ -99,11 +99,11 @@ uintptr_t* Arch_AllocatePageMapAt(uintptr_t pml4Base, uintptr_t at, uintptr_t cp cpuFlags &= ~0x07F0000000000E00; for (uint8_t i = 3; i > (3 - depth); i--) { - uintptr_t* pageMap = (uintptr_t*)Arch_MapToHHDM((i + 1) == 4 ? pml4Base : Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, i + 1))); + uintptr_t* pageMap = (uintptr_t*)MmS_MapVirtFromPhys((i + 1) == 4 ? pml4Base : Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, i + 1))); if (!pageMap[AddressToIndex(at, i)]) { - uintptr_t newTable = OBOSS_AllocatePhysicalPages(1,1, nullptr); - memzero(Arch_MapToHHDM(newTable), 4096); + uintptr_t newTable = Mm_AllocatePhysicalPages(1,1, nullptr); + memzero(MmS_MapVirtFromPhys(newTable), 4096); pageMap[AddressToIndex(at, i)] = newTable | cpuFlags; } else @@ -118,7 +118,7 @@ uintptr_t* Arch_AllocatePageMapAt(uintptr_t pml4Base, uintptr_t at, uintptr_t cp pageMap[AddressToIndex(at, i)] = entry; } } - return (uintptr_t*)Arch_MapToHHDM(Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, (4 - depth)))); + return (uintptr_t*)MmS_MapVirtFromPhys(Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, (4 - depth)))); } bool Arch_FreePageMapAt(uintptr_t pml4Base, uintptr_t at, uint8_t maxDepth) { @@ -128,13 +128,13 @@ bool Arch_FreePageMapAt(uintptr_t pml4Base, uintptr_t at, uint8_t maxDepth) { if (!(GetPageMapEntryForDepth(pml4Base, at, i + 1) & 1)) continue; - uintptr_t* pageMap = (uintptr_t*)Arch_MapToHHDM(Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, i + 1))); + uintptr_t* pageMap = (uintptr_t*)MmS_MapVirtFromPhys(Arch_MaskPhysicalAddressFromEntry(GetPageMapEntryForDepth(pml4Base, at, i + 1))); uintptr_t phys = Arch_MaskPhysicalAddressFromEntry(pageMap[AddressToIndex(at, i)]); - uintptr_t* subPageMap = (uintptr_t*)Arch_MapToHHDM(phys); + uintptr_t* subPageMap = (uintptr_t*)MmS_MapVirtFromPhys(phys); if (memcmp_b(subPageMap, (int)0, 4096)) { pageMap[AddressToIndex(at, i)] = 0; - OBOSS_FreePhysicalPages(phys, 1); + Mm_FreePhysicalPages(phys, 1); continue; } } @@ -219,7 +219,7 @@ obos_status Arch_UnmapPage(uintptr_t cr3, void* at_) if (!(entry & (1<<0))) return OBOS_STATUS_SUCCESS; uintptr_t phys = Arch_MaskPhysicalAddressFromEntry(entry); - uintptr_t* pt = (uintptr_t*)Arch_MapToHHDM(phys); + uintptr_t* pt = (uintptr_t*)MmS_MapVirtFromPhys(phys); pt[AddressToIndex(at, (uint8_t)isHugePage)] = 0; Arch_FreePageMapAt(cr3, at, 3 - (uint8_t)isHugePage); return invlpg_impl(at); @@ -239,10 +239,9 @@ static obos_status invlpg_impl(uintptr_t at) return OBOS_STATUS_SUCCESS; if (!invlpg_ipi_packet.irq && Core_IrqInterfaceInitialized()) { - OBOS_Debug("Initializing invlpg ipi IRQ.\n"); static irq irq; invlpg_ipi_packet.irq = &irq; - enum { IRQL_INVLPG_IPI=14 }; + enum { IRQL_INVLPG_IPI=15 }; Core_IrqObjectInitializeIRQL(&irq, IRQL_INVLPG_IPI, false, true); irq.handler = invlpg_ipi_bootstrap; irq.handlerUserdata = nullptr; @@ -305,7 +304,7 @@ obos_status OBOSS_GetPagePhysicalAddress(void* at_, uintptr_t* oPhys) entry = Arch_GetPML3Entry(getCR3(), at); if (!(entry & (1 << 0))) return OBOS_STATUS_SUCCESS; - *oPhys = Arch_MaskPhysicalAddressFromEntry(((uintptr_t*)Arch_MapToHHDM(Arch_MaskPhysicalAddressFromEntry(entry)))[AddressToIndex(at, (uint8_t)isHugePage)]); + *oPhys = Arch_MaskPhysicalAddressFromEntry(((uintptr_t*)MmS_MapVirtFromPhys(Arch_MaskPhysicalAddressFromEntry(entry)))[AddressToIndex(at, (uint8_t)isHugePage)]); return OBOS_STATUS_SUCCESS; } @@ -315,7 +314,7 @@ static void FreePageTables(uintptr_t* pm, uint8_t level, uint32_t beginIndex, ui { if (!pm) return; - pm = (uintptr_t*)Arch_MapToHHDM((uintptr_t)pm); + pm = (uintptr_t*)MmS_MapVirtFromPhys((uintptr_t)pm); for (indices[level] = beginIndex; indices[level] < 512; indices[level]++) { if (!pm[indices[level]]) @@ -323,7 +322,7 @@ static void FreePageTables(uintptr_t* pm, uint8_t level, uint32_t beginIndex, ui if (pm[indices[level]] & ((uintptr_t)1<<7) || level == 0) continue; FreePageTables((uintptr_t*)Arch_MaskPhysicalAddressFromEntry(pm[indices[level]]), level - 1, 0, indices); - Arch_FreePhysicalPages(Arch_MaskPhysicalAddressFromEntry(pm[indices[level]]), 1); + Mm_FreePhysicalPages(Arch_MaskPhysicalAddressFromEntry(pm[indices[level]]), 1); } } uintptr_t MmS_KernelBaseAddress; @@ -332,9 +331,9 @@ extern uintptr_t Arch_KernelCR3; obos_status Arch_InitializeKernelPageTable() { obos_status status = OBOS_STATUS_SUCCESS; - uintptr_t newCR3 = Arch_AllocatePhysicalPages(1,1, &status); + uintptr_t newCR3 = Mm_AllocatePhysicalPages(1,1, &status); uintptr_t oldCR3 = getCR3(); - memzero(Arch_MapToHHDM(newCR3), 4096); + memzero(MmS_MapVirtFromPhys(newCR3), 4096); if (status != OBOS_STATUS_SUCCESS) return status; OBOS_Debug("%s: Mapping kernel.\n", __func__); @@ -367,15 +366,15 @@ obos_status Arch_InitializeKernelPageTable() } } OBOS_Debug("%s: Mapping HHDM.\n", __func__); - for (uintptr_t off = 0; off < Arch_PhysicalMemoryBoundaries; off += 0x200000) - Arch_MapHugePage(newCR3, Arch_MapToHHDM(off), off, 0x8000000000000003 /* XD, Write, Present */); + for (uintptr_t off = 0; off < Mm_PhysicalMemoryBoundaries; off += 0x200000) + Arch_MapHugePage(newCR3, MmS_MapVirtFromPhys(off), off, 0x8000000000000003 /* XD, Write, Present */); asm volatile("mov %0, %%cr3;" : :"r"(newCR3)); // Reclaim old page tables. uint32_t indices[4] = { 0,0,0,0 }; FreePageTables((uintptr_t*)oldCR3, 3, AddressToIndex(0xffff800000000000, 3), indices); - Arch_FreePhysicalPages((uintptr_t)oldCR3, 1); + Mm_FreePhysicalPages((uintptr_t)oldCR3, 1); OBOSH_BasicMMAddRegion(&kernel_region, (void*)Arch_KernelInfo->virtual_base, Arch_KernelInfo->size); - OBOSH_BasicMMAddRegion(&hhdm_region, (void*)Arch_LdrPlatformInfo->higher_half_base, Arch_PhysicalMemoryBoundaries); + OBOSH_BasicMMAddRegion(&hhdm_region, (void*)Arch_LdrPlatformInfo->higher_half_base, Mm_PhysicalMemoryBoundaries); Arch_KernelCR3 = newCR3; return OBOS_STATUS_SUCCESS; } @@ -414,15 +413,16 @@ obos_status MmS_QueryPageInfo(page_table pt, uintptr_t addr, page* ppage) if (page.prot.huge_page) { uintptr_t pml3Entry = Arch_MaskPhysicalAddressFromEntry(Arch_GetPML3Entry(pt, addr)); - pml3Entry = (uintptr_t)Arch_MapToHHDM(pml3Entry); + pml3Entry = (uintptr_t)MmS_MapVirtFromPhys(pml3Entry); ((uintptr_t*)pml3Entry)[AddressToIndex(addr, 1)] &= ~(BIT_TYPE(5, UL) | BIT_TYPE(6, UL)); } else { pml2Entry = Arch_MaskPhysicalAddressFromEntry(pml2Entry); - pml2Entry = (uintptr_t)Arch_MapToHHDM(pml2Entry); + pml2Entry = (uintptr_t)MmS_MapVirtFromPhys(pml2Entry); ((uintptr_t*)pml2Entry)[AddressToIndex(addr, 0)] &= ~(BIT_TYPE(5, UL) | BIT_TYPE(6, UL)); } + ppage->addr = addr; memcpy(&ppage->prot, &page.prot, sizeof(page.prot)); return OBOS_STATUS_SUCCESS; } diff --git a/src/oboskrnl/arch/x86_64/pmm.c b/src/oboskrnl/arch/x86_64/pmm.c index 4fdd6e61..8eebd22d 100644 --- a/src/oboskrnl/arch/x86_64/pmm.c +++ b/src/oboskrnl/arch/x86_64/pmm.c @@ -15,156 +15,24 @@ extern struct ultra_memory_map_attribute* Arch_MemoryMap; extern struct ultra_platform_info_attribute* Arch_LdrPlatformInfo; -struct freelist_node +obos_pmem_map_entry* MmS_GetFirstPMemMapEntry(uintptr_t* index) { - size_t nPages; - struct freelist_node *next, *prev; -}; -static struct freelist_node *s_head; -static struct freelist_node *s_tail; -static size_t s_nNodes; -size_t Arch_TotalPhysicalPages = 0; -size_t Arch_TotalPhysicalPagesUsed = 0; -size_t Arch_UsablePhysicalPages = 0; -uintptr_t Arch_PhysicalMemoryBoundaries; -obos_status Arch_InitializePMM() + *index = 0; + return &Arch_MemoryMap->entries[0]; +} +// returns nullptr at the end of the list. +obos_pmem_map_entry* MmS_GetNextPMemMapEntry(obos_pmem_map_entry* current, uintptr_t* index) { - if (!Arch_MemoryMap) - return OBOS_STATUS_INVALID_INIT_PHASE; + OBOS_UNUSED(current); size_t nEntries = (Arch_MemoryMap->header.size - sizeof(Arch_MemoryMap->header)) / sizeof(struct ultra_memory_map_entry); if (!nEntries) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "No memory map entries.\n"); - for (size_t i = 0; i < nEntries; i++) - { - struct ultra_memory_map_entry* entry = &Arch_MemoryMap->entries[i]; - uintptr_t phys = entry->physical_address; - size_t nPages = entry->size / 0x1000; - if (phys & 0xfff) - { - phys = (phys + 0xfff) & ~0xfff; - nPages--; - } - if (phys == 0x0) - { - phys = 0x1000; - nPages--; - } - Arch_TotalPhysicalPages += nPages; - if ((phys + nPages * 0x1000) > Arch_PhysicalMemoryBoundaries) - Arch_PhysicalMemoryBoundaries = (phys + nPages * 0x1000); - if (entry->type != ULTRA_MEMORY_TYPE_FREE) - continue; - Arch_UsablePhysicalPages += nPages; - struct freelist_node* node = (struct freelist_node*)(Arch_LdrPlatformInfo->higher_half_base + phys); - memzero(node, sizeof(*node)); - node->nPages = nPages; - if (s_tail) - ((struct freelist_node*)(Arch_LdrPlatformInfo->higher_half_base + (uintptr_t)s_tail))->next = (struct freelist_node*)phys; - if (!s_head) - s_head = (struct freelist_node*)phys; - node->prev = s_tail; - s_tail = (struct freelist_node*)phys; - s_nNodes++; - } - if (Arch_PhysicalMemoryBoundaries & 4294967295) - Arch_PhysicalMemoryBoundaries = (Arch_PhysicalMemoryBoundaries + 4294967295) & ~4294967295; - return OBOS_STATUS_SUCCESS; -} -static bool IsRegionSufficient(struct freelist_node* node, size_t nPages, size_t alignmentMask, size_t *nPagesRequiredP) -{ - size_t nPagesRequired = nPages; - uintptr_t nodePhys = (uintptr_t)node - Arch_LdrPlatformInfo->higher_half_base; - nPagesRequired += (node->nPages & ((alignmentMask + 1) / 0x1000 - 1)); - uintptr_t mod = nodePhys & alignmentMask; - nPagesRequired += mod / 4096; - if (nPagesRequiredP) - *nPagesRequiredP = nPagesRequired; - return node->nPages >= nPagesRequired; + if ((*index) > nEntries) + return nullptr; + return &Arch_MemoryMap->entries[++(*index)]; } #define MAP_TO_HHDM(addr, type) ((type*)(Arch_LdrPlatformInfo->higher_half_base + (uintptr_t)(addr))) #define UNMAP_FROM_HHDM(addr) ((uintptr_t)(addr) - Arch_LdrPlatformInfo->higher_half_base) -OBOS_NO_KASAN uintptr_t Arch_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status *status) -{ - if (!nPages) - { - *status = OBOS_STATUS_INVALID_ARGUMENT; - return 0; - } - if (!alignmentPages) - alignmentPages = 1; - if (__builtin_popcount(alignmentPages) > 1) - { - *status = OBOS_STATUS_INVALID_ARGUMENT; - return 0; - } - if (!s_head) - OBOS_Panic(OBOS_PANIC_NO_MEMORY, "No more avaliable physical memory!\n"); - uintptr_t phys = 0; - if (nPages % alignmentPages) - nPages += (alignmentPages - (nPages % alignmentPages)); - size_t alignmentMask = alignmentPages*0x1000-1; - size_t nPagesRequired = 0; - struct freelist_node* node = MAP_TO_HHDM(s_head, struct freelist_node); - while (UNMAP_FROM_HHDM(node) && !IsRegionSufficient(node, nPages, alignmentMask, &nPagesRequired)) - node = MAP_TO_HHDM(node->next, struct freelist_node); - if (!UNMAP_FROM_HHDM(node)) - { - if (status) - *status = OBOS_STATUS_NOT_ENOUGH_MEMORY; - return 0; - } - OBOS_ASSERT(node->nPages >= nPagesRequired); - node->nPages -= nPagesRequired; - Arch_TotalPhysicalPagesUsed += nPagesRequired; - if (!node->nPages) - { - if (node->next) - MAP_TO_HHDM(node->next, struct freelist_node)->prev = node->prev; - if (node->prev) - MAP_TO_HHDM(node->prev, struct freelist_node)->next = node->next; - if ((uintptr_t)s_head == UNMAP_FROM_HHDM(node)) - s_head = node->next; - if ((uintptr_t)s_tail == UNMAP_FROM_HHDM(node)) - s_tail = node->prev; - s_nNodes--; - node->next = nullptr; - node->prev = nullptr; - } - phys = (uintptr_t)node + node->nPages * 0x1000; - phys -= Arch_LdrPlatformInfo->higher_half_base; - if (status) - *status = OBOS_STATUS_SUCCESS; - return phys; -} -OBOS_NO_KASAN void Arch_FreePhysicalPages(uintptr_t addr, size_t nPages) -{ - OBOS_ASSERT(addr); - OBOS_ASSERT(!(addr & 0xfff)); - addr &= ~0xfff; - if (!addr) - return; - struct freelist_node* node = (struct freelist_node*)(Arch_LdrPlatformInfo->higher_half_base + addr); - node->nPages = nPages; - if (s_tail) - MAP_TO_HHDM(s_tail, struct freelist_node)->next = (struct freelist_node*)addr; - if (!s_head) - s_head = (struct freelist_node*)addr; - node->prev = s_tail; - Arch_TotalPhysicalPagesUsed -= nPages; - s_tail = (struct freelist_node*)addr; -} -uintptr_t OBOSS_AllocatePhysicalPages(size_t nPages, size_t alignment, obos_status* status) -{ - return Arch_AllocatePhysicalPages(nPages, alignment, status); -} -obos_status OBOSS_FreePhysicalPages(uintptr_t base, size_t nPages) -{ - base &= ~0xfff; - if (!base) - return OBOS_STATUS_INVALID_ARGUMENT; - Arch_FreePhysicalPages(base, nPages); - return OBOS_STATUS_SUCCESS; -} OBOS_NO_KASAN OBOS_NO_UBSAN void* Arch_MapToHHDM(uintptr_t phys) { return MAP_TO_HHDM(phys, void); @@ -172,4 +40,12 @@ OBOS_NO_KASAN OBOS_NO_UBSAN void* Arch_MapToHHDM(uintptr_t phys) OBOS_NO_KASAN OBOS_NO_UBSAN uintptr_t Arch_UnmapFromHHDM(void* virt) { return UNMAP_FROM_HHDM(virt); +} +void* MmS_MapVirtFromPhys(uintptr_t addr) +{ + return Arch_MapToHHDM(addr); +} +uintptr_t MmS_UnmapVirtFromPhys(void* virt) +{ + return Arch_UnmapFromHHDM(virt); } \ No newline at end of file diff --git a/src/oboskrnl/arch/x86_64/pmm.h b/src/oboskrnl/arch/x86_64/pmm.h index 8e89a87c..cb767ca4 100644 --- a/src/oboskrnl/arch/x86_64/pmm.h +++ b/src/oboskrnl/arch/x86_64/pmm.h @@ -8,32 +8,16 @@ #include #include -#include -extern size_t Arch_TotalPhysicalPages; -extern size_t Arch_TotalPhysicalPagesUsed; -extern size_t Arch_UsablePhysicalPages; -extern uintptr_t Arch_PhysicalMemoryBoundaries; +#include -/// -/// Initializes the PMM. -/// -/// The function status. -obos_status Arch_InitializePMM(); +typedef struct ultra_memory_map_entry obos_pmem_map_entry; +#define pmem_map_base physical_address +#define pmem_map_size size +#define pmem_map_type type +#define PHYSICAL_MEMORY_TYPE_USABLE ULTRA_MEMORY_TYPE_FREE +#define PHYSICAL_MEMORY_TYPE_RECLAIMABLE ULTRA_MEMORY_TYPE_RECLAIMABLE +#define PHYSICAL_MEMORY_TYPE_LOADER_RECLAIMABLE ULTRA_MEMORY_TYPE_LOADER_RECLAIMABLE -/// -/// Allocates physical pages. -/// -/// The amount of physical pages to allocate. -/// The alignment of the address returned, in pages. -/// [optional] A pointer to a variable that will store the function's status. Can be nullptr. -/// The physical pages, or zero on failure. -uintptr_t Arch_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status* status); -/// -/// Frees physical pages. -/// -/// The address of the page. -/// The amount of pages to free. -void Arch_FreePhysicalPages(uintptr_t addr, size_t nPages); void* Arch_MapToHHDM(uintptr_t phys); uintptr_t Arch_UnmapFromHHDM(void* virt); \ No newline at end of file diff --git a/src/oboskrnl/arch/x86_64/thread_ctx.asm b/src/oboskrnl/arch/x86_64/thread_ctx.asm index a99dc0ed..852978be 100644 --- a/src/oboskrnl/arch/x86_64/thread_ctx.asm +++ b/src/oboskrnl/arch/x86_64/thread_ctx.asm @@ -242,6 +242,24 @@ CoreS_GetThreadIRQL: mov rax, [rdi+thread_ctx.irql] + leave + ret +global CoreS_GetThreadStack +global CoreS_GetThreadStackSize +CoreS_GetThreadStackSize: + push rbp + mov rbp, rsp + + mov rax, [rdi+thread_ctx.stackSize] + + leave + ret +CoreS_GetThreadStack: + push rbp + mov rbp, rsp + + mov rax, [rdi+thread_ctx.stackBase] + leave ret section .text \ No newline at end of file diff --git a/src/oboskrnl/cmdline.c b/src/oboskrnl/cmdline.c index 5f348d96..4375d446 100644 --- a/src/oboskrnl/cmdline.c +++ b/src/oboskrnl/cmdline.c @@ -14,6 +14,8 @@ #include const char* OBOS_KernelCmdLine; +const char* OBOS_InitrdBinary; +size_t OBOS_InitrdSize; char** OBOS_argv; size_t OBOS_argc; @@ -56,6 +58,16 @@ void OBOS_ParseCMDLine() "OBOSKRNL usage:\n" "NOTE: Any amount of dashes ('-') can be used at the beginning of the option or flag.\n" "--enable-kdbg: Enables the kernel debugger at boot. Not all architectures support this.\n" + "--initrd-module=name: The name or path of the initrd module.\n" + "--initrd-driver-module=name: The name or path of the initrd driver module.\n" + "--load-modules=name[,name]: If an initrd driver is specified, then 'name' is an absolute path\n" + " in the initrd, otherwise it is the name of a module to load as a driver.\n" + "--mount-initrd=pathspec: Mounts the InitRD at pathspec if specified, otherwise the initrd is left unmounted\n" + " when 'init' is called.\n" + "--root-fs-uuid=uuid: Specifies the partition to mount as root. If set to 'initrd', the initrd\n" + " is used as root.\n" + "--root-fs-partid=partid: Specifies the partition to mount as root. If set to 'initrd', the initrd\n" + " is used as root.\n" "--help: Displays this help message.\n"; printf("%s", help_message); } @@ -74,8 +86,9 @@ char* OBOS_GetOPTS(const char* opt) size_t optlen = strchr(arg, '='); if (arglen != optlen || arg[arglen - 1] == '=') optlen--; - if (arglen == optlen && arg[arglen - 1] != '=') - continue; + // Wut? + // if (arglen == optlen && arg[arglen - 1] != '=') + // continue; if (uacpi_strncmp(arg, opt, optlen) == 0) { if (i == (OBOS_argc - 1) && optlen == arglen) diff --git a/src/oboskrnl/cmdline.h b/src/oboskrnl/cmdline.h index 87ebaebb..a461598d 100644 --- a/src/oboskrnl/cmdline.h +++ b/src/oboskrnl/cmdline.h @@ -8,16 +8,18 @@ #include -extern const char* OBOS_KernelCmdLine; -extern char** OBOS_argv; -extern size_t OBOS_argc; +extern OBOS_EXPORT const char* OBOS_KernelCmdLine; +extern OBOS_EXPORT const char* OBOS_InitrdBinary; +extern OBOS_EXPORT size_t OBOS_InitrdSize; +extern OBOS_EXPORT char** OBOS_argv; +extern OBOS_EXPORT size_t OBOS_argc; // Parses the command line into OBOS_argv and OBOS_argc void OBOS_ParseCMDLine(); // Gets the value of a string command line option. -char* OBOS_GetOPTS(const char* opt); +OBOS_EXPORT char* OBOS_GetOPTS(const char* opt); // Gets the value of an integer command line option. -uint64_t OBOS_GetOPTD(const char* opt); +OBOS_EXPORT uint64_t OBOS_GetOPTD(const char* opt); // Gets the value of a flag command line option. // true if the flag was found on the command line, otherwise false. -bool OBOS_GetOPTF(const char* opt); \ No newline at end of file +OBOS_EXPORT bool OBOS_GetOPTF(const char* opt); \ No newline at end of file diff --git a/src/oboskrnl/driver_interface/header.h b/src/oboskrnl/driver_interface/header.h index d71c8f9a..a385954a 100644 --- a/src/oboskrnl/driver_interface/header.h +++ b/src/oboskrnl/driver_interface/header.h @@ -12,6 +12,8 @@ #include +#include + enum { OBOS_DRIVER_MAGIC = 0x00116d868ac84e59 }; // Not required, but can speed up loading times if the driver header is put in here. #define OBOS_DRIVER_HEADER_SECTION ".driverheader" @@ -60,7 +62,7 @@ typedef enum iterate_decision ITERATE_DECISION_CONTINUE, ITERATE_DECISION_STOP, } iterate_decision; -typedef struct file_perm +typedef struct driver_file_perm { bool other_exec : 1; bool other_write : 1; @@ -71,7 +73,7 @@ typedef struct file_perm bool owner_exec : 1; bool owner_write : 1; bool owner_read : 1; -} OBOS_PACK file_perm; +} OBOS_PACK driver_file_perm; typedef enum file_type { FILE_TYPE_DIRECTORY, @@ -84,7 +86,7 @@ typedef enum file_type typedef uintptr_t dev_desc; typedef struct driver_ftable { - // Note: If there is not an OBOS_STATUS for an error that a driver needs to return, rather choose the error closest to the error that you want to report, + // Note: If there is not an OBOS_STATUS for an error that a driver needs to return, choose the error closest to the error that you want to report, // or return OBOS_STATUS_INTERNAL_ERROR. // --------------------------------------- @@ -96,10 +98,11 @@ typedef struct driver_ftable obos_status(*get_max_blk_count)(dev_desc desc, size_t* count); obos_status(*read_sync)(dev_desc desc, void* buf, size_t blkCount, size_t blkOffset, size_t* nBlkRead); obos_status(*write_sync)(dev_desc desc, const void* buf, size_t blkCount, size_t blkOffset, size_t* nBlkWritten); - obos_status(*foreach_device)(iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount)); + obos_status(*foreach_device)(iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* userdata), void* userdata); // unrequired for fs drivers. obos_status(*query_user_readable_name)(dev_desc what, const char** name); // unrequired for fs drivers. // The driver dictates what the request means, and what its parameters are. obos_status(*ioctl)(size_t nParameters, uint64_t request, ...); + obos_status(*ioctl_var)(size_t nParameters, uint64_t request, va_list list); // Called on driver unload. // Frees all the driver's allocated resources, as the kernel // does not keep a track of allocated resources, and cannot free them on driver unload, causing a @@ -117,13 +120,15 @@ typedef struct driver_ftable // lifetime of *path is dicated by the driver. obos_status(*query_path)(dev_desc desc, const char** path); obos_status(*path_search)(dev_desc* found, const char* what); + obos_status(*get_linked_desc)(dev_desc desc, dev_desc* found); obos_status(*move_desc_to)(dev_desc desc, const char* where); obos_status(*mk_file)(dev_desc* newDesc, dev_desc parent, const char* name, file_type type); obos_status(*remove_file)(dev_desc desc); - obos_status(*get_file_perms)(dev_desc desc, file_perm *perm); - obos_status(*set_file_perms)(dev_desc desc, file_perm newperm); + obos_status(*get_file_perms)(dev_desc desc, driver_file_perm *perm); + obos_status(*set_file_perms)(dev_desc desc, driver_file_perm newperm); obos_status(*get_file_type)(dev_desc desc, file_type *type); - obos_status(*list_dir)(dev_desc dir, iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount)); + // If dir is UINTPTR_MAX, it refers to the root directory. + obos_status(*list_dir)(dev_desc dir, iterate_decision(*cb)(dev_desc desc, size_t blkSize, size_t blkCount, void* userdata), void* userdata); // ----------- END FS FUNCTIONS ---------- // --------------------------------------- } driver_ftable; diff --git a/src/oboskrnl/driver_interface/loader.c b/src/oboskrnl/driver_interface/loader.c index 1805ec2b..b8286de5 100644 --- a/src/oboskrnl/driver_interface/loader.c +++ b/src/oboskrnl/driver_interface/loader.c @@ -247,6 +247,10 @@ OBOS_NO_UBSAN driver_id *Drv_LoadDriver(const void* file_, size_t szFile, obos_s } driver->node.data = driver; APPEND_DRIVER_NODE(Drv_LoadedDrivers, &driver->node); + if (strlen(driver->header.driverName)) + OBOS_Debug("%s: Loaded driver '%s' at 0x%p.\n", __func__, driver->header.driverName, driver->base); + else + OBOS_Debug("%s: Loaded driver at 0x%p.\n", __func__, driver->header.driverName, driver->base); return driver; } obos_status Drv_StartDriver(driver_id* driver, thread** mainThread) @@ -268,7 +272,7 @@ obos_status Drv_StartDriver(driver_id* driver, thread** mainThread) stackSize = driver->header.stackSize; if (!stackSize) stackSize = 0x20000; - void* stack = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, stackSize, 0, VMA_FLAGS_KERNEL_STACK, &status); + void* stack = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, stackSize, 0, VMA_FLAGS_KERNEL_STACK, nullptr, &status); status = CoreS_SetupThreadContext(&ctx, driver->entryAddr, (uintptr_t)driver, @@ -286,6 +290,8 @@ obos_status Drv_StartDriver(driver_id* driver, thread** mainThread) Mm_VirtualMemoryFree(&Mm_KernelContext, stack, stackSize); return status; } + thr->stackFree = CoreH_VMAStackFree; + thr->stackFreeUserdata = &Mm_KernelContext; if (mainThread) { *mainThread = thr; diff --git a/src/oboskrnl/error.h b/src/oboskrnl/error.h index 044fd213..18c59243 100644 --- a/src/oboskrnl/error.h +++ b/src/oboskrnl/error.h @@ -117,4 +117,24 @@ typedef enum /// The lock was already locked by the current thread. /// OBOS_STATUS_RECURSIVE_LOCK, + /// + /// The driver cannot satisfy the write request, as the device/filesystem it's manipulating is read only. + /// + OBOS_STATUS_READ_ONLY, + /// + /// The filesystem driver cannot satisfy the request, as the descriptor passed was not a file. + /// + OBOS_STATUS_NOT_A_FILE, + /// + /// The vnode already had a node mounted there. + /// + OBOS_STATUS_ALREADY_MOUNTED, + /// + /// The fd object hit EOF. + /// + OBOS_STATUS_EOF, + /// + /// The operation was aborted. + /// + OBOS_STATUS_ABORTED, } obos_status; \ No newline at end of file diff --git a/src/oboskrnl/int.h b/src/oboskrnl/int.h index 23607177..f4c39bcc 100644 --- a/src/oboskrnl/int.h +++ b/src/oboskrnl/int.h @@ -9,6 +9,8 @@ #include #include +#define obos_expect(expr, eval) __builtin_expect((expr), (eval)) + #if OBOS_KERNEL # define OBOS_EXPORT __attribute__((visibility("default"))) // Usually redundant. @@ -39,6 +41,10 @@ #define PTR_BITS 16 #endif +typedef uint32_t uid, gid; +#define ROOT_UID 0 +#define ROOT_GID 0 + #if !defined(__cplusplus) && !defined(true) && !defined(false) # define true (1) # define false (0) @@ -46,7 +52,9 @@ typedef _Bool bool; #endif #ifndef __cplusplus # define nullptr ((void*)0) -# undef NULL +# ifndef IS_UACPI_BUILD +# undef NULL +# endif // Do all this to make sure intellisense is happy. # if __STDC_VERSION__ >= 201112L && __STDC_VERSION__ < 202311L # define OBOS_ALIGNAS(x) _Alignas(x) @@ -61,7 +69,9 @@ typedef _Bool bool; # endif #else # define OBOS_ALIGNAS(x) alignas(x) -# undef NULL +# ifndef IS_UACPI_BUILD +# undef NULL +# endif #endif #define OBOS_NORETURN [[noreturn]] @@ -94,6 +104,18 @@ typedef _Bool bool; #if __STDC_HOSTED__ # error Must be compiled as freestanding. #endif -#define OBOS_PAGEABLE_VARIABLE __attribute__((section(".pageable.data"))) -#define OBOS_PAGEABLE_RO_VARIABLE __attribute__((section(".pageable.rodata"))) -#define OBOS_PAGEABLE_FUNCTION __attribute__((section(".pageable.text"))) \ No newline at end of file +#if OBOS_KERNEL +# define OBOS_PAGEABLE_VARIABLE __attribute__((section(".pageable.data"))) +# define OBOS_PAGEABLE_RO_VARIABLE __attribute__((section(".pageable.rodata"))) +# define OBOS_PAGEABLE_FUNCTION __attribute__((section(".pageable.text"))) +#elif defined(OBOS_DRIVER) && !defined(__m68k__) +# define OBOS_PAGEABLE_VARIABLE __attribute__((section(".pageable.data"))) +# define OBOS_PAGEABLE_RO_VARIABLE __attribute__((section(".pageable.rodata"))) +# define OBOS_PAGEABLE_FUNCTION __attribute__((section(".pageable.text"))) +#else +# define OBOS_PAGEABLE_VARIABLE +# define OBOS_PAGEABLE_RO_VARIABLE +# define OBOS_PAGEABLE_FUNCTION +#endif + +#include \ No newline at end of file diff --git a/src/oboskrnl/irq/irq.c b/src/oboskrnl/irq/irq.c index a97db922..19c1673c 100644 --- a/src/oboskrnl/irq/irq.c +++ b/src/oboskrnl/irq/irq.c @@ -32,12 +32,19 @@ static bool s_irqInterfaceInitialized; void Core_IRQDispatcher(interrupt_frame* frame) { CoreS_SendEOI(frame); +#if !OBOS_ARCH_EMULATED_IRQL + irql irql_ = OBOS_IRQ_VECTOR_ID_TO_IRQL(frame->vector); + irql oldIrql2 = Core_RaiseIrqlNoThread(irql_); if (!CoreS_EnterIRQHandler(frame)) return; // some archs do IRQL emulation this way. +#else irql irql_ = OBOS_IRQ_VECTOR_ID_TO_IRQL(frame->vector); + if (!CoreS_EnterIRQHandler(frame)) + return; // some archs do IRQL emulation this way. if (irql_ <= Core_GetIrql()) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "IRQL on call of the dispatcher is less than the IRQL of the vector reported by the architecture (\"irql_ <= Core_GetIrql()\")."); irql oldIrql2 = Core_RaiseIrqlNoThread(irql_); +#endif context* oldCtx = CoreS_GetCPULocalPtr()->currentContext; CoreS_GetCPULocalPtr()->currentContext = &Mm_KernelContext; // irql oldIrql = Core_SpinlockAcquireExplicit(&s_lock, irql_, false); diff --git a/src/oboskrnl/klog.c b/src/oboskrnl/klog.c index a133cf50..919ec2ee 100644 --- a/src/oboskrnl/klog.c +++ b/src/oboskrnl/klog.c @@ -58,7 +58,7 @@ OBOS_EXPORT log_level OBOS_GetLogLevel() } static spinlock s_loggerLock; bool s_loggerLockInitialized = false; static spinlock s_printfLock; bool s_printfLockInitialized = false; -static OBOS_PAGEABLE_FUNCTION void common_log(log_level minimumLevel, const char* log_prefix, const char* format, va_list list) +static void common_log(log_level minimumLevel, const char* log_prefix, const char* format, va_list list) { if (!s_loggerLockInitialized) { @@ -72,28 +72,28 @@ static OBOS_PAGEABLE_FUNCTION void common_log(log_level minimumLevel, const char vprintf(format, list); Core_SpinlockRelease(&s_loggerLock, oldIrql); } -OBOS_PAGEABLE_FUNCTION OBOS_EXPORT void OBOS_Debug(const char* format, ...) +OBOS_EXPORT void OBOS_Debug(const char* format, ...) { va_list list; va_start(list, format); common_log(LOG_LEVEL_DEBUG, "DEBUG", format, list); va_end(list); } -OBOS_PAGEABLE_FUNCTION OBOS_EXPORT void OBOS_Log(const char* format, ...) +OBOS_EXPORT void OBOS_Log(const char* format, ...) { va_list list; va_start(list, format); common_log(LOG_LEVEL_LOG, " LOG ", format, list); va_end(list); } -OBOS_PAGEABLE_FUNCTION OBOS_EXPORT void OBOS_Warning(const char* format, ...) +OBOS_EXPORT void OBOS_Warning(const char* format, ...) { va_list list; va_start(list, format); common_log(LOG_LEVEL_WARNING, "WARN ", format, list); va_end(list); } -OBOS_PAGEABLE_FUNCTION OBOS_EXPORT void OBOS_Error(const char* format, ...) +OBOS_EXPORT void OBOS_Error(const char* format, ...) { va_list list; va_start(list, format); @@ -193,7 +193,7 @@ OBOS_EXPORT size_t vprintf(const char* format, va_list list) Core_SpinlockRelease(&s_printfLock, oldIrql); return ret; } -OBOS_EXPORT OBOS_PAGEABLE_FUNCTION size_t snprintf(char* buf, size_t bufSize, const char* format, ...) +OBOS_EXPORT size_t snprintf(char* buf, size_t bufSize, const char* format, ...) { va_list list; va_start(list, format); @@ -201,7 +201,7 @@ OBOS_EXPORT OBOS_PAGEABLE_FUNCTION size_t snprintf(char* buf, size_t bufSize, co va_end(list); return ret; } -OBOS_EXPORT OBOS_PAGEABLE_FUNCTION size_t vsnprintf(char* buf, size_t bufSize, const char* format, va_list list) +OBOS_EXPORT size_t vsnprintf(char* buf, size_t bufSize, const char* format, va_list list) { return stbsp_vsnprintf(buf, bufSize, format, list); } \ No newline at end of file diff --git a/src/oboskrnl/locks/mutex.c b/src/oboskrnl/locks/mutex.c index f083fe7d..cdb98057 100644 --- a/src/oboskrnl/locks/mutex.c +++ b/src/oboskrnl/locks/mutex.c @@ -31,16 +31,29 @@ obos_status Core_MutexAcquire(mutex* mut) if (mut->who == Core_GetCurrentThread()) return OBOS_STATUS_RECURSIVE_LOCK; // Spin for a bit. - irql oldIrql = Core_RaiseIrql(IRQL_DISPATCH); + irql oldIrql = Core_GetIrql() < IRQL_DISPATCH ? Core_RaiseIrql(IRQL_DISPATCH) : IRQL_INVALID; int spin = 100000; bool success = true; while (atomic_flag_test_and_set_explicit(&mut->lock, memory_order_seq_cst) && success) { spinlock_hint(); + if (mut->ignoreAllAndBlowUp) + { + if (oldIrql != IRQL_INVALID) + Core_LowerIrql(oldIrql); + return OBOS_STATUS_ABORTED; + } if (spin-- >= 0) success = false; } - Core_LowerIrql(oldIrql); + if (mut->ignoreAllAndBlowUp) + { + if (oldIrql != IRQL_INVALID) + Core_LowerIrql(oldIrql); + return OBOS_STATUS_ABORTED; + } + if (oldIrql != IRQL_INVALID) + Core_LowerIrql(oldIrql); if (success) { mut->who = Core_GetCurrentThread(); @@ -48,6 +61,8 @@ obos_status Core_MutexAcquire(mutex* mut) return OBOS_STATUS_SUCCESS; } Core_WaitOnObject(&mut->hdr); + if (mut->ignoreAllAndBlowUp) + return OBOS_STATUS_ABORTED; while (atomic_flag_test_and_set_explicit(&mut->lock, memory_order_seq_cst) && success) spinlock_hint(); mut->who = Core_GetCurrentThread(); @@ -69,6 +84,7 @@ obos_status Core_MutexRelease(mutex* mut) return OBOS_STATUS_SUCCESS; if (mut->who != Core_GetCurrentThread()) return OBOS_STATUS_ACCESS_DENIED; + mut->who = nullptr; atomic_flag_clear_explicit(&mut->lock, memory_order_seq_cst); obos_status status = CoreH_SignalWaitingThreads(&mut->hdr, false, false); if (obos_is_error(status)) diff --git a/src/oboskrnl/locks/mutex.h b/src/oboskrnl/locks/mutex.h index 8e3a2ecf..4becc1f6 100644 --- a/src/oboskrnl/locks/mutex.h +++ b/src/oboskrnl/locks/mutex.h @@ -20,6 +20,8 @@ typedef struct mutex { // Whether the mutex is locked or not. atomic_flag lock; bool locked; + // set this when freeing an object. + bool ignoreAllAndBlowUp; // The thread that took the mutex. thread* who; } mutex; diff --git a/src/oboskrnl/locks/spinlock.c b/src/oboskrnl/locks/spinlock.c index ed88c6bb..01518f30 100644 --- a/src/oboskrnl/locks/spinlock.c +++ b/src/oboskrnl/locks/spinlock.c @@ -23,7 +23,7 @@ spinlock Core_SpinlockCreate() { - spinlock tmp = ATOMIC_FLAG_INIT; + spinlock tmp = {.val=ATOMIC_FLAG_INIT, false, false, nullptr}; return tmp; } OBOS_NO_UBSAN irql Core_SpinlockAcquireExplicit(spinlock* const lock, irql minIrql, bool irqlNthrVariant) diff --git a/src/oboskrnl/locks/wait.c b/src/oboskrnl/locks/wait.c index 45c1c633..e3186cd9 100644 --- a/src/oboskrnl/locks/wait.c +++ b/src/oboskrnl/locks/wait.c @@ -111,10 +111,12 @@ obos_status CoreH_SignalWaitingThreads(struct waitable_header* obj, bool all, bo { thread_node* next = curr->next; CoreH_ThreadListRemove(&obj->waiting, curr); - if (boostPriority) - CoreH_ThreadBoostPriority(curr->data); if ((++curr->data->nSignaled) == curr->data->nWaiting) + { + if (boostPriority) + CoreH_ThreadBoostPriority(curr->data); CoreH_ThreadReadyNode(curr->data, curr->data->snode); + } if (curr->free) curr->free(curr); if (!all) diff --git a/src/oboskrnl/memmanip.h b/src/oboskrnl/memmanip.h index ac78af63..4e2fc89b 100644 --- a/src/oboskrnl/memmanip.h +++ b/src/oboskrnl/memmanip.h @@ -8,11 +8,11 @@ #include -void* memset(void* blk, uint32_t val, size_t count); -void* memzero(void* blk, size_t count); -void* memcpy(void* blk1, const void* blk2, size_t count); -bool memcmp(const void* blk1, const void* blk2, size_t count); -bool memcmp_b(const void* blk1, int against, size_t count); -bool strcmp(const char* str1, const char* str2); -size_t strlen(const char* str); -size_t strchr(const char* str, char ch); \ No newline at end of file +OBOS_EXPORT void* memset(void* blk, uint32_t val, size_t count); +OBOS_EXPORT void* memzero(void* blk, size_t count); +OBOS_EXPORT void* memcpy(void* blk1, const void* blk2, size_t count); +OBOS_EXPORT bool memcmp(const void* blk1, const void* blk2, size_t count); +OBOS_EXPORT bool memcmp_b(const void* blk1, int against, size_t count); +OBOS_EXPORT bool strcmp(const char* str1, const char* str2); +OBOS_EXPORT size_t strlen(const char* str); +OBOS_EXPORT size_t strchr(const char* str, char ch); \ No newline at end of file diff --git a/src/oboskrnl/mm/alloc.c b/src/oboskrnl/mm/alloc.c index 964d2f1a..f9244a93 100644 --- a/src/oboskrnl/mm/alloc.c +++ b/src/oboskrnl/mm/alloc.c @@ -4,6 +4,7 @@ * Copyright (c) 2024 Omar Berrow */ +#include "vfs/vnode.h" #include #include #include @@ -13,12 +14,16 @@ #include #include #include +#include #include #include #include +#include +#include + #include #include @@ -57,7 +62,7 @@ void* MmH_FindAvaliableAddress(context* ctx, size_t size, vma_flags flags, obos_ currentNode; currentNode = RB_NEXT(page_tree, &ctx->pages, currentNode)) { - uintptr_t currentNodeAddr = currentNode->addr - (currentNode->addr % pgSize); + uintptr_t currentNodeAddr = currentNode->addr; if (currentNodeAddr < base) continue; if (currentNodeAddr >= limit) @@ -65,6 +70,7 @@ void* MmH_FindAvaliableAddress(context* ctx, size_t size, vma_flags flags, obos_ if ((currentNodeAddr - lastAddress) >= (size + pgSize)) { found = lastAddress + (lastNode->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); + // found += (pgSize-(found%pgSize)); break; } lastAddress = currentNodeAddr + (currentNode->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); @@ -86,7 +92,7 @@ void* MmH_FindAvaliableAddress(context* ctx, size_t size, vma_flags flags, obos_ } return (void*)found; } -void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags prot, vma_flags flags, obos_status* ustatus) +void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags prot, vma_flags flags, fd* file, obos_status* ustatus) { obos_status status = OBOS_STATUS_SUCCESS; set_statusp(ustatus, status); @@ -95,6 +101,18 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p set_statusp(ustatus, OBOS_STATUS_INVALID_ARGUMENT); return nullptr; } + if (file && flags & VMA_FLAGS_NON_PAGED) + { + set_statusp(ustatus, OBOS_STATUS_INVALID_ARGUMENT); + return nullptr; + } + if (file && !file->vn) + { + set_statusp(ustatus, OBOS_STATUS_UNINITIALIZED); + return nullptr; + } + if (file) + flags &= ~VMA_FLAGS_HUGE_PAGE; // you see, page caches don't really use huge pages, so we have to force huge pages off. uintptr_t base = (uintptr_t)base_; const size_t pgSize = (flags & VMA_FLAGS_HUGE_PAGE) ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; if (base % pgSize) @@ -102,10 +120,39 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p set_statusp(ustatus, OBOS_STATUS_INVALID_ARGUMENT); return nullptr; } + if (OBOS_HUGE_PAGE_SIZE == OBOS_PAGE_SIZE) + flags &= ~VMA_FLAGS_HUGE_PAGE; + size_t filesize = 0; + if (file) + { + if (file->vn->vtype != VNODE_TYPE_REG) + { + set_statusp(ustatus, OBOS_STATUS_INVALID_ARGUMENT); + return nullptr; + } + if (file->vn->filesize < size) + size = file->vn->filesize; // Truncated. + if ((file->offset+size >= file->vn->filesize)) + size = (file->offset+size) - file->vn->filesize; + filesize = size; + if (size % pgSize) + size += (pgSize-(size%pgSize)); + if (!(file->flags & FD_FLAGS_READ)) + { + // No. + set_statusp(ustatus, OBOS_STATUS_ACCESS_DENIED); + return nullptr; + } + if (!(file->flags & FD_FLAGS_WRITE) && !(flags & VMA_FLAGS_PRIVATE)) + prot |= OBOS_PROTECTION_READ_ONLY; + } if (size % pgSize) size += (pgSize-(size%pgSize)); if (flags & VMA_FLAGS_GUARD_PAGE) size += pgSize; + if ((flags & VMA_FLAGS_PREFAULT || flags & VMA_FLAGS_PRIVATE) && file) + if (file->vn->pagecache.sz <= file->offset) + VfsH_PageCacheResize(&file->vn->pagecache, file->vn, file->offset+filesize); irql oldIrql = Core_SpinlockAcquireExplicit(&ctx->lock, IRQL_MASKED, true); top: if (!base) @@ -149,9 +196,23 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p // TODO: Optimize by splitting really big allocations (> OBOS_HUGE_PAGE_SIZE) into huge pages and normal pages. size_t nNodes = size / pgSize + (size % pgSize ? 1 : 0); page** nodes = Mm_Allocator->ZeroAllocate(Mm_Allocator, nNodes, sizeof(page*), &status); + off_t currFileOff = file ? file->offset : 0; + size_t currSize = filesize; + pagecache_mapped_region* reg = file ? + Mm_Allocator->ZeroAllocate(Mm_Allocator, 1, sizeof(pagecache_mapped_region), nullptr) : nullptr; + if (file) + { + reg->fileoff = file->offset; + reg->sz = filesize; + reg->addr = base; + reg->owner = &file->vn->pagecache; + reg->ctx = ctx; + LIST_APPEND(mapped_region_list, ®->owner->mapped_regions, reg); + } for (size_t i = 0; i < nNodes; i++) { - uintptr_t phys = OBOSS_AllocatePhysicalPages(pgSize/OBOS_PAGE_SIZE, pgSize/OBOS_PAGE_SIZE, &status); + uintptr_t phys = 0; + bool isPresent = true; page* node = Mm_Allocator->ZeroAllocate(Mm_Allocator, 1, sizeof(page), &status); nodes[i] = node; node->addr = base + i*pgSize; @@ -161,6 +222,40 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p node->pagedOut = false; node->prot.huge_page = flags & VMA_FLAGS_HUGE_PAGE; node->age = 0; + node->region = reg; + if (!file) + phys = Mm_AllocatePhysicalPages(pgSize/OBOS_PAGE_SIZE, pgSize/OBOS_PAGE_SIZE, &status); + else + { + // If this is a private mapping... + if (flags & VMA_FLAGS_PRIVATE) + { + void* pagecache_base = file->vn->pagecache.data + currFileOff; + page what = { .addr=(uintptr_t)pagecache_base }; + page* pc_page = RB_FIND(page_tree, &Mm_KernelContext.pages, &what); + OBOSS_GetPagePhysicalAddress(pagecache_base, &phys); + if (!(prot & OBOS_PROTECTION_READ_ONLY)) + { + if (pc_page->next_copied_page) + pc_page->next_copied_page->prev_copied_page = node; + node->next_copied_page = pc_page->next_copied_page; + node->prev_copied_page = pc_page; + pc_page->next_copied_page = node; + pc_page->prot.rw = false; + pc_page->prot.present = true; + MmS_SetPageMapping(Mm_KernelContext.pt, pc_page, phys); + } + node->prot.rw = false; + node->prot.present = true; + } + else + { + if (file->offset < file->vn->pagecache.sz) + OBOSS_GetPagePhysicalAddress((void*)(file->vn->pagecache.data + currFileOff), &phys); + else + isPresent = false; + } + } if (flags & VMA_FLAGS_GUARD_PAGE && i == 0) { node->prot.present = false; @@ -169,12 +264,18 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p } else { - node->prot.present = true; + node->prot.present = isPresent; node->prot.huge_page = flags & VMA_FLAGS_HUGE_PAGE; + if (!(flags & VMA_FLAGS_PRIVATE) || !file) + { + node->prot.rw = !(prot & OBOS_PROTECTION_READ_ONLY); + node->pageable = !(flags & VMA_FLAGS_NON_PAGED); + } + if (!(flags & VMA_FLAGS_PRIVATE) && file) + node->prot.rw = false; // force it off so that we can mark dirty pages. node->prot.executable = prot & OBOS_PROTECTION_EXECUTABLE; - node->prot.rw = !(prot & OBOS_PROTECTION_READ_ONLY); node->prot.user = prot & OBOS_PROTECTION_USER_PAGE; - node->pageable = !(flags & VMA_FLAGS_NON_PAGED); + node->prot.ro = prot & OBOS_PROTECTION_READ_ONLY; status = MmS_SetPageMapping(ctx->pt, node, phys); if (obos_is_error(status)) { @@ -186,20 +287,33 @@ void* Mm_VirtualMemoryAlloc(context* ctx, void* base_, size_t size, prot_flags p RB_REMOVE(page_tree, &ctx->pages, nodes[j]); Mm_Allocator->Free(Mm_Allocator, nodes[j], sizeof(page)); } + if (reg) + Mm_Allocator->Free(Mm_Allocator, reg, sizeof(*reg)); Core_SpinlockRelease(&ctx->lock, oldIrql); Mm_Allocator->Free(Mm_Allocator, node, sizeof(page)); Mm_Allocator->Free(Mm_Allocator, nodes, nNodes*sizeof(page*)); set_statusp(ustatus, status); return nullptr; } - memzero((void*)node->addr, pgSize); + if (isPresent && !(prot & OBOS_PROTECTION_READ_ONLY) && !file) + memzero((void*)node->addr, pgSize); } + currFileOff += pgSize; + currSize -= pgSize; RB_INSERT(page_tree, &ctx->pages, node); } // Page out each page so we don't explode. // TODO: Error handling? for (size_t i = 0; i < nNodes && !(flags & VMA_FLAGS_NON_PAGED); i++) Mm_SwapOut(nodes[i]); + if (!(flags & VMA_FLAGS_NON_PAGED)) + { + ctx->stat.paged += size; + ctx->stat.pageable += size; + } + else + ctx->stat.nonPaged += size; + ctx->stat.committedMemory += size; Mm_Allocator->Free(Mm_Allocator, nodes, nNodes*sizeof(page*)); Core_SpinlockRelease(&ctx->lock, oldIrql); if (flags & VMA_FLAGS_GUARD_PAGE) @@ -280,25 +394,51 @@ obos_status Mm_VirtualMemoryFree(context* ctx, void* base_, size_t size) Core_SpinlockRelease(&ctx->lock, oldIrql); return OBOS_STATUS_NOT_FOUND; } + + RB_REMOVE(page_tree, &ctx->pages, curr); + if (curr->ln_node.next || curr->ln_node.prev || &curr->ln_node == ctx->referenced.head || &curr->ln_node == ctx->workingSet.pages.head) + { + if (curr->workingSets > 0) + { + REMOVE_PAGE_NODE(ctx->workingSet.pages, &curr->ln_node); + ctx->workingSet.size -= curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + } + else + REMOVE_PAGE_NODE(ctx->referenced, &curr->ln_node); // it's in the referenced list + } + if (curr->pageable) + ctx->stat.pageable -= (curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); + else + ctx->stat.nonPaged -= (curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); if (curr->prot.present) { - curr->prot.present = false; - MmS_SetPageMapping(ctx->pt, curr, 0); // Unmap the page. + uintptr_t phys = 0; + OBOSS_GetPagePhysicalAddress((void*)curr->addr, &phys); + if (!curr->region || !curr->isPrivateMapping) + Mm_FreePhysicalPages(phys, (curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE) / OBOS_PAGE_SIZE); } - RB_REMOVE(page_tree, &ctx->pages, curr); - if (curr->age == 1 /* it's in the referenced list */) - REMOVE_PAGE_NODE(ctx->referenced, &curr->ln_node); - else if (curr->workingSets > 0) + else { - REMOVE_PAGE_NODE(ctx->workingSet.pages, &curr->ln_node); - ctx->workingSet.size -= curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + if (curr->pageable) + ctx->stat.paged -= (curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); } if (curr->allocated) Mm_Allocator->Free(Mm_Allocator, curr, sizeof(*curr)); offset = curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; } - + ctx->stat.committedMemory -= size; Core_SpinlockRelease(&ctx->lock, oldIrql); + struct page current = {}; + for (uintptr_t addr = base; addr < (base + size); addr += offset) + { + MmS_QueryPageInfo(ctx->pt, addr, ¤t); + if (curr->prot.present) + { + curr->prot.present = false; + MmS_SetPageMapping(ctx->pt, ¤t, 0); // Unmap the page. + } + offset = curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + } return OBOS_STATUS_SUCCESS; } @@ -363,11 +503,13 @@ obos_status Mm_VirtualMemoryProtect(context* ctx, void* base_, size_t size, prot curr->prot.executable = prot & OBOS_PROTECTION_EXECUTABLE; curr->prot.rw = !(prot & OBOS_PROTECTION_READ_ONLY); curr->prot.user = prot & OBOS_PROTECTION_USER_PAGE; + curr->prot.ro = prot & OBOS_PROTECTION_READ_ONLY; if (curr->pagedOut && !isPageable) { // Page in curr if: // The current page is paged out, and we need to change the pageable property from true to false. Mm_SwapIn(curr); + Mm_KernelContext.stat.paged -= (curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); } if (isPageable < 2) curr->pageable = isPageable; diff --git a/src/oboskrnl/mm/alloc.h b/src/oboskrnl/mm/alloc.h index b9ab26da..1282e317 100644 --- a/src/oboskrnl/mm/alloc.h +++ b/src/oboskrnl/mm/alloc.h @@ -13,8 +13,7 @@ #include -// TODO: Make interface. -// TODO: Implement. +#include typedef enum vma_flags { @@ -23,6 +22,8 @@ typedef enum vma_flags VMA_FLAGS_32BIT = BIT(3), VMA_FLAGS_HINT = BIT(4), VMA_FLAGS_NON_PAGED = BIT(5), + VMA_FLAGS_PRIVATE = BIT(6), // only applies when mapping a file. + VMA_FLAGS_PREFAULT = BIT(7), // only applies when mapping a file. VMA_FLAGS_KERNEL_STACK = VMA_FLAGS_NON_PAGED|VMA_FLAGS_NON_PAGED, } vma_flags; typedef enum prot_flags @@ -52,8 +53,9 @@ typedef enum prot_flags extern allocator_info* Mm_Allocator; -void* MmH_FindAvaliableAddress(context* ctx, size_t size, vma_flags flags, obos_status* status); -void* Mm_VirtualMemoryAlloc(context* ctx, void* base, size_t size, prot_flags prot, vma_flags flags, obos_status* status); +void* MmH_FindAvailableAddress(context* ctx, size_t size, vma_flags flags, obos_status* status); +// file can be nullptr for a anonymous mapping. +void* Mm_VirtualMemoryAlloc(context* ctx, void* base, size_t size, prot_flags prot, vma_flags flags, fd* file, obos_status* status); // Note: base must be the exact address as returned by AllocateVirtualMemory. obos_status Mm_VirtualMemoryFree(context* ctx, void* base, size_t size); // Note: base must be the exact address as returned by AllocateVirtualMemory. diff --git a/src/oboskrnl/mm/bare_map.c b/src/oboskrnl/mm/bare_map.c index 1835993f..6a5bc557 100644 --- a/src/oboskrnl/mm/bare_map.c +++ b/src/oboskrnl/mm/bare_map.c @@ -10,6 +10,7 @@ #include #include +#include #include @@ -90,7 +91,7 @@ OBOS_NO_KASAN OBOS_PAGEABLE_FUNCTION void* OBOS_BasicMMAllocatePages(size_t sz, for (uintptr_t addr = found; addr < (found + sz); addr += OBOS_PAGE_SIZE) { obos_status stat = OBOS_STATUS_SUCCESS; - uintptr_t mem = OBOSS_AllocatePhysicalPages(1, 1, &stat); + uintptr_t mem = Mm_AllocatePhysicalPages(1, 1, &stat); if (stat != OBOS_STATUS_SUCCESS) { if (status) @@ -137,7 +138,7 @@ OBOS_NO_KASAN OBOS_PAGEABLE_FUNCTION obos_status OBOS_BasicMMFreePages(void* bas OBOSS_GetPagePhysicalAddress((void*)addr, &phys); OBOSS_UnmapPage((void*)addr); if (phys) - OBOSS_FreePhysicalPages(phys, 1); + Mm_FreePhysicalPages(phys, 1); } return OBOS_STATUS_SUCCESS; } diff --git a/src/oboskrnl/mm/bare_map.h b/src/oboskrnl/mm/bare_map.h index fcd4dc69..5e0b080e 100644 --- a/src/oboskrnl/mm/bare_map.h +++ b/src/oboskrnl/mm/bare_map.h @@ -46,22 +46,6 @@ typedef struct basicmm_region struct basicmm_region* prev; } basicmm_region; -/// -/// Allocates physical pages. -/// -/// The amount of pages to allocate. -/// The alignment (in pages) of the address returned. Must be a power of two. -/// [out,optional] The function's status. Can be nullptr. -/// The address of the allocated region. -OBOS_WEAK uintptr_t OBOSS_AllocatePhysicalPages(size_t nPages, size_t alignment, obos_status* status); -/// -/// Frees physical pages. -/// -/// The base of said pages. -/// The amount of pages to free. -/// The status of the function. -OBOS_WEAK obos_status OBOSS_FreePhysicalPages(uintptr_t base, size_t nPages); - /// /// Allocates pages as RW XD. /// Only to be used in kernel-mode. diff --git a/src/oboskrnl/mm/context.h b/src/oboskrnl/mm/context.h index 5be75e87..25be01a5 100644 --- a/src/oboskrnl/mm/context.h +++ b/src/oboskrnl/mm/context.h @@ -13,6 +13,8 @@ #include +#include + #ifdef __x86_64__ typedef uintptr_t page_table; #elif __m68k__ @@ -50,6 +52,17 @@ typedef struct working_set size_t capacity; size_t size; } working_set; +typedef struct memstat +{ + // The size of all allocated (committed) memory. + size_t committedMemory; + // The size of all memory within this context which has been paged out. + size_t paged; + // The size of all pageable memory (memory that can be paged out). + size_t pageable; + // The size of all non-pageable memory (memory that cannot be paged out). + size_t nonPaged; +} memstat; typedef struct context { struct process* owner; @@ -59,6 +72,8 @@ typedef struct context page_list referenced; spinlock lock; page_table pt; + dpc file_mapping_dpc; + memstat stat; } context; extern context Mm_KernelContext; extern char MmS_MMPageableRangeStart[]; diff --git a/src/oboskrnl/mm/handler.c b/src/oboskrnl/mm/handler.c index e5c9babc..6b56671f 100644 --- a/src/oboskrnl/mm/handler.c +++ b/src/oboskrnl/mm/handler.c @@ -4,19 +4,115 @@ * Copyright (c) 2024 Omar Berrow */ +#include "mm/alloc.h" #include #include +#include #include #include #include #include #include +#include +#include + +#include +#include + +#include +#include #include #include +#include +static void handle_oom(context* ctx, size_t bytesNeeded, page* pg) +{ + page* chose = nullptr; + size_t szChose = 0; + bool lockAcquired = Core_SpinlockAcquired(&ctx->lock); + for (page_node* node = ctx->referenced.head; node; ) + { + page* const curr = node->data; + if (pg == curr) + { + node = node->next; + continue; + } + size_t bytesUsed = curr->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + if (bytesUsed >= bytesNeeded && bytesUsed <= szChose) + { + chose = curr; + szChose = bytesUsed; + } + node = node->next; + } + try_again: + if (!chose) + { + // Block until there is enough memory to satisfy the OOM. + if (lockAcquired) + Core_SpinlockForcedRelease(&ctx->lock); + CoreH_ThreadListAppend(&Mm_ThreadsAwaitingPhysicalMemory, &Core_GetCurrentThread()->phys_mem_node); + CoreH_ThreadBlock(Core_GetCurrentThread(), true); + if (lockAcquired) + Core_SpinlockAcquire(&ctx->lock); + return; + } + // Swap out the page. + if (obos_is_error(Mm_SwapOut(chose))) + { + chose = nullptr; + goto try_again; + } + ctx->stat.paged += (chose->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); + // We've freed enough memory. + // Return. + return; +} +static obos_status copy_cow_page(context* ctx, page* page, struct page* other) +{ + void* addr = (void*)page->addr; + if (other->next_copied_page != page && other->prev_copied_page != page) + { + // The other page already did the hard work. + // We just need to update our page mappings. + uintptr_t phys = 0; + OBOSS_GetPagePhysicalAddress((void*)addr, &phys); + other->prot.rw = true; + MmS_SetPageMapping(ctx->pt, page, phys); + return OBOS_STATUS_SUCCESS; + } + if (other->pagedOut) + { + // This is neat. + // We can just set prot.rw=true then finish. + other->prot.rw = true; + return OBOS_STATUS_SUCCESS; + } + // The other page gets the page, while we get a new one. + uintptr_t phys = page->addr; + OBOSS_GetPagePhysicalAddress((void*)addr, &phys); + const size_t pgSize = page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + obos_status status = OBOS_STATUS_SUCCESS; + try_again: + (void)0; + uintptr_t newPhys = Mm_AllocatePhysicalPages(pgSize/OBOS_PAGE_SIZE, pgSize/OBOS_PAGE_SIZE, &status); + if (obos_is_error(status) && status != OBOS_STATUS_NOT_ENOUGH_MEMORY) + return status; + if (status == OBOS_STATUS_NOT_ENOUGH_MEMORY) + { + handle_oom(ctx, pgSize, page); + goto try_again; + } + page->prot.rw = true; + MmS_SetPageMapping(ctx->pt, page, newPhys); + memcpy((void*)page->addr, MmS_MapVirtFromPhys(phys), pgSize); + other->prot.rw = true; + return OBOS_STATUS_SUCCESS; +} obos_status Mm_HandlePageFault(context* ctx, uintptr_t addr, uint32_t ec) { OBOS_ASSERT(ctx); @@ -24,6 +120,7 @@ obos_status Mm_HandlePageFault(context* ctx, uintptr_t addr, uint32_t ec) page* page = RB_FIND(page_tree, &ctx->pages, &what); if (!page) return OBOS_STATUS_UNHANDLED; + bool handled = false; bool pagedOut = page->pagedOut; irql oldIrql = Core_SpinlockAcquire(&ctx->lock); if (pagedOut && !page->pagedOut) @@ -31,33 +128,45 @@ obos_status Mm_HandlePageFault(context* ctx, uintptr_t addr, uint32_t ec) // The page was paged in by someone else... Core_SpinlockRelease(&ctx->lock, oldIrql); return OBOS_STATUS_SUCCESS; - } + } + bool requiresPageIn = false; + OBOS_UNUSED(requiresPageIn); + // To not waste time, check if the access is even allowed in the first place. + if (ec & PF_EC_EXEC && !page->prot.executable) + { + Core_SpinlockRelease(&ctx->lock, oldIrql); + return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. + } + // if (ec & PF_EC_RW && !page->prot.rw && !(page->prev_copied_page || page->next_copied_page)) + if (ec & PF_EC_RW && page->prot.ro) + { + Core_SpinlockRelease(&ctx->lock, oldIrql); + return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. + } + if (ec & PF_EC_UM && !page->prot.user) + { + Core_SpinlockRelease(&ctx->lock, oldIrql); + return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. + } if (page->pagedOut && !(ec & PF_EC_PRESENT)) { - // To not waste time, check if the access is even allowed in the first place. - if (ec & PF_EC_EXEC && !page->prot.executable) - { - Core_SpinlockRelease(&ctx->lock, oldIrql); - return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. - } - if (ec & PF_EC_RW && !page->prot.rw) - { - Core_SpinlockRelease(&ctx->lock, oldIrql); - return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. - } - if (ec & PF_EC_UM && !page->prot.user) - { - Core_SpinlockRelease(&ctx->lock, oldIrql); - return OBOS_STATUS_UNHANDLED; // TODO: Signal the thread. - } // If this assertion fails, something stupid has happened. OBOS_ASSERT(!page->workingSets); + handled = true; + try_again1: + requiresPageIn = true; obos_status status = Mm_SwapIn(page); - if (obos_is_error(status)) + if (obos_is_error(status) && status != OBOS_STATUS_NOT_ENOUGH_MEMORY) { Core_SpinlockRelease(&ctx->lock, oldIrql); return status; } + if (status == OBOS_STATUS_NOT_ENOUGH_MEMORY) + { + handle_oom(ctx, page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE, page); + goto try_again1; + } + Mm_KernelContext.stat.paged -= (page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); page->ln_node.data = page; page->age |= 1; page->prot.touched = false; @@ -66,12 +175,96 @@ obos_status Mm_HandlePageFault(context* ctx, uintptr_t addr, uint32_t ec) const size_t threshold = (ctx->workingSet.capacity / 4) / OBOS_PAGE_SIZE; if (ctx->referenced.nNodes >= threshold) Mm_RunPRA(ctx); - Core_SpinlockRelease(&ctx->lock, oldIrql); - return OBOS_STATUS_SUCCESS; + } + if ((page->next_copied_page || page->prev_copied_page) && ec & PF_EC_RW) + { + handled = true; + // CoW. + for (struct page* cur = page->next_copied_page; cur; ) + { + copy_cow_page(ctx, cur->prev_copied_page, cur); + struct page* next = cur->next_copied_page; + if (cur->prev_copied_page) + cur->prev_copied_page->next_copied_page = cur->next_copied_page; + if (cur->next_copied_page) + cur->next_copied_page->prev_copied_page = cur->prev_copied_page; + cur = next; + } + for (struct page* cur = page->prev_copied_page; cur; ) + { + copy_cow_page(ctx, cur->next_copied_page, cur); + struct page* next = cur->prev_copied_page; + if (cur->prev_copied_page) + cur->prev_copied_page->next_copied_page = cur->next_copied_page; + if (cur->next_copied_page) + cur->next_copied_page->prev_copied_page = cur->prev_copied_page; + cur = next; + } } Core_SpinlockRelease(&ctx->lock, oldIrql); - // TODO: Signal the thread. - return OBOS_STATUS_UNHANDLED; + if (page->region && !(ec & PF_EC_PRESENT)) + { + // We need to map part of the file in. + handled = true; + // TODO: Get the vnode in a more sane manner. + vnode* vn = (vnode*)((uintptr_t)page->region->owner - offsetof(vnode, pagecache)); + OBOS_ASSERT(vn->filesize); + OBOS_ASSERT(vn->filesize > (page->region->fileoff + page->region->sz)); + // const size_t pgSize = page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + if (page->region->owner->sz >= page->region->fileoff) + VfsH_PageCacheResize(page->region->owner, vn, page->region->fileoff + page->region->sz); + void* pagecache_region = page->region->owner->data + (page->region->fileoff+(addr - page->region->addr)); + Core_MutexAcquire(&page->region->lock); + what.addr = (uintptr_t)pagecache_region; + struct page* pc_page = RB_FIND(page_tree, &Mm_KernelContext.pages, &what); + if (page->isPrivateMapping) + { + // Simply make both this page, and the page cache's page mapped as CoW + uintptr_t pagePhys = 0; + if (pc_page->next_copied_page) + pc_page->next_copied_page->prev_copied_page = page; + page->next_copied_page = pc_page->next_copied_page; + page->prev_copied_page = pc_page; + pc_page->next_copied_page = page; + page->prot.rw = page->prot.ro; + pc_page->prot.rw = pc_page->prot.ro; + page->prot.present = true; + pc_page->prot.present = true; + OBOSS_GetPagePhysicalAddress((void*)page->addr, &pagePhys); + MmS_SetPageMapping(ctx->pt, page, pagePhys); + OBOSS_GetPagePhysicalAddress(pagecache_region, &pagePhys); + MmS_SetPageMapping(Mm_KernelContext.pt, pc_page, pagePhys); + } + else + { + // We can just set this page's current physical address to that of the page cache. + uintptr_t pagePhys = 0; + page->prot.present = true; + OBOSS_GetPagePhysicalAddress(pagecache_region, &pagePhys); + MmS_SetPageMapping(ctx->pt, page, pagePhys); + } + Core_MutexRelease(&page->region->lock); + } + if (page->region && !page->prot.ro && (ec & PF_EC_RW) && (ec && PF_EC_PRESENT)) + { + pagecache_dirty_region* dirty_reg = Mm_Allocator->ZeroAllocate(Mm_Allocator, 1, sizeof(pagecache_dirty_region), nullptr); + dirty_reg->fileoff = page->region->fileoff+(addr - page->region->addr); + dirty_reg->sz = page->region->sz-page->region->fileoff+(addr - page->region->addr); + dirty_reg->sz -= dirty_reg->sz % OBOS_PAGE_SIZE; + dirty_reg->owner = page->region->owner; + // TODO: Get the vnode in a more sane manner. + vnode* vn = (vnode*)((uintptr_t)page->region->owner - offsetof(vnode, pagecache)); + OBOS_ASSERT(vn->filesize); + OBOS_ASSERT(vn->filesize > (page->region->fileoff + page->region->sz)); + LIST_APPEND(dirty_pc_list, &vn->pagecache.dirty_regions, dirty_reg); + uintptr_t pagePhys = 0; + OBOSS_GetPagePhysicalAddress((void*)page->addr, &pagePhys); + page->prot.rw = true; + MmS_SetPageMapping(ctx->pt, page, pagePhys); + } + + // TODO: Signal the thread if handled == false. + return !handled ? OBOS_STATUS_UNHANDLED : OBOS_STATUS_SUCCESS; } obos_status Mm_RunPRA(context* ctx) { @@ -96,6 +289,7 @@ obos_status Mm_RunPRA(context* ctx) // NOTE(oberrow, 07:11 2024-07-15): // Now that I'm awake I can start on this. + int64_t workingSetDifference = 0; for (page_node* node = ctx->workingSet.pages.head; node;) { page* page = node->data; @@ -118,25 +312,23 @@ obos_status Mm_RunPRA(context* ctx) if (!(--page->workingSets)) { obos_status status = Mm_SwapOut(page); - if (obos_is_error(status)) - { - static const char format1[] = - "Could not swap out page. Status: %d. Ignoring...\n"; - OBOS_Warning(format1, status); - } + if (obos_is_success(status)) + ctx->stat.paged += (page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); } + workingSetDifference -= (page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); // Pop the last referenced page and put it into the working-set - if (ctx->workingSet.size < ctx->workingSet.capacity) + if ((ctx->workingSet.size + workingSetDifference) < ctx->workingSet.capacity && ctx->referenced.nNodes) { struct page_node* replacement = ctx->referenced.tail; REMOVE_PAGE_NODE(ctx->referenced, replacement); replacement->next = replacement->prev = nullptr; - ctx->workingSet.size += replacement->data->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; + workingSetDifference += replacement->data->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE; APPEND_PAGE_NODE(ctx->workingSet.pages, replacement); replacement->data->workingSets++; } node = next; } + ctx->workingSet.size += workingSetDifference; if (ctx->workingSet.size > ctx->workingSet.capacity) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Pages in working-set exceeded its size. Size of pages: %lu, size of working set: %lu.\n", ctx->workingSet.size, ctx->workingSet.capacity); @@ -162,6 +354,7 @@ obos_status Mm_RunPRA(context* ctx) { // This page needs to be swapped out as well as removed from the referenced list. Mm_SwapOut(page); + ctx->stat.paged += (page->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); } node = next; } diff --git a/src/oboskrnl/mm/init.c b/src/oboskrnl/mm/init.c index 2bc3ed6e..441041cf 100644 --- a/src/oboskrnl/mm/init.c +++ b/src/oboskrnl/mm/init.c @@ -85,11 +85,14 @@ static bool register_pages(basicmm_region* region, void* udatablk) region->mmioRange); if (pg->pageable) udata->szPageablePages += (pg->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); + else + Mm_KernelContext.stat.nonPaged += (pg->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); pg->workingSets = 0; pg->pagedOut = false; pg->age = 0; pg->owner = &Mm_KernelContext; RB_INSERT(page_tree, &Mm_KernelContext.pages, pg); + Mm_KernelContext.stat.committedMemory += (pg->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); if (pg->prot.huge_page) addr += OBOS_HUGE_PAGE_SIZE; else @@ -125,17 +128,28 @@ void Mm_Initialize() if (obos_is_error(status)) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Could not allocate node buffer. Status: %d.\n", status); OBOSH_BasicMMIterateRegions(register_pages, &udata); + Mm_KernelContext.stat.pageable = udata.szPageablePages; // Do any architecture-specific rounding here. #if defined(__x86_64__) || defined(__m68k__) udata.szPageablePages = (udata.szPageablePages + 0x3fff) & ~0x3fff; #endif // Mm_KernelContext.workingSet.capacity = udata.szPageablePages; - Mm_KernelContext.workingSet.capacity = 0x20000 /* 128 KiB */; + Mm_KernelContext.workingSet.capacity = 0x80000 /* 512 KiB */; initialized = true; page* i = nullptr; + // size_t committedMemory; + // size_t paged; + // size_t pageable; + // size_t nonPaged; + RB_FOREACH(i, page_tree, &Mm_KernelContext.pages) + { if (i->pageable) + { Mm_SwapOut(i); + Mm_KernelContext.stat.paged += (i->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE); + } + } Core_SpinlockRelease(&Mm_KernelContext.lock, oldIrql); } bool Mm_IsInitialized() diff --git a/src/oboskrnl/mm/page.h b/src/oboskrnl/mm/page.h index 18fd4b72..b7d5431d 100644 --- a/src/oboskrnl/mm/page.h +++ b/src/oboskrnl/mm/page.h @@ -17,26 +17,31 @@ typedef struct page_node } page_node; typedef struct page { - struct context* owner; // The context that this page is in. - RB_ENTRY(page) rb_node; // The rb-tree node. - struct page_node ln_node; // The linked-list node. - struct // - { // - bool present : 1; // If set, the page is present. - bool rw : 1; // If set, the page can be written - bool user : 1; // If set, the page can be accessed in user mode. - bool touched : 1; // If set, the page has been read or written. - bool huge_page : 1; // If set, the page is a huge page. - bool executable : 1; // If set, the page can be executed. - } prot; // The protection of the page. - bool pageable : 1; // If set, the page is pageable. - bool pagedOut : 1; // If set, the page is paged out. - size_t workingSets : 16; // The amount of working sets the page is in. - bool isGuardPage : 1; // If set, the page is a guard page. - bool allocated : 1; // If set, this object was allocated by Mm_Allocator. - uint8_t age : 8; // The page's age - uintptr_t addr : PTR_BITS; // The page's address. - uintptr_t swapId : PTR_BITS; // The page's swap allocation id. Only valid if pagedOut == true. + struct context* owner; // The context that this page is in. + RB_ENTRY(page) rb_node; // The rb-tree node. + page_node ln_node; // The linked-list node. + struct pagecache_mapped_region* region; // A region of a vnode that is supposed to be mapped here. + bool isPrivateMapping; // Only valid if region != nullptr. Set to true if the file mapping is a private mapping. + struct // + { // + bool present : 1; // If set, the page is present. + bool rw : 1; // If set, the page can be written + bool user : 1; // If set, the page can be accessed in user mode. + bool touched : 1; // If set, the page has been read or written. + bool huge_page : 1; // If set, the page is a huge page. + bool executable : 1; // If set, the page can be executed. + bool ro : 1; // If set, this page was originally allocated as read-only. This is only used in CoW pages as of now. + } prot; // The protection of the page. + bool pageable : 1; // If set, the page is pageable. + bool pagedOut : 1; // If set, the page is paged out. + size_t workingSets : 16; // The amount of working sets the page is in. + bool isGuardPage : 1; // If set, the page is a guard page. + bool allocated : 1; // If set, this object was allocated by Mm_Allocator. + uint8_t age : 8; // The page's age + uintptr_t addr : PTR_BITS; // The page's address. + uintptr_t swapId : PTR_BITS; // The page's swap allocation id. Only valid if pagedOut == true. + struct page* next_copied_page; // If CoW is enabled on this page, this contains the pointer to the next page we're sharing data with. + struct page* prev_copied_page; // If CoW is enabled on this page, this contains the pointer to the previous page we're sharing data with. } page; typedef struct page_list { diff --git a/src/oboskrnl/mm/pmm.c b/src/oboskrnl/mm/pmm.c new file mode 100644 index 00000000..d9f0c6ce --- /dev/null +++ b/src/oboskrnl/mm/pmm.c @@ -0,0 +1,196 @@ +/* + oboskrnl/mm/pmm.c + + Copyright (c) 2024 Omar Berrow +*/ + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +struct freelist_node +{ +#if OBOS_KASAN_ENABLED + uint8_t poison[64]; +#endif + size_t nPages; + struct freelist_node *next, *prev; +}; +static struct freelist_node *s_head; +static struct freelist_node *s_tail; +static size_t s_nNodes; +size_t Mm_TotalPhysicalPages; +size_t Mm_TotalPhysicalPagesUsed; +size_t Mm_UsablePhysicalPages; +uintptr_t Mm_PhysicalMemoryBoundaries; +thread_list Mm_ThreadsAwaitingPhysicalMemory; +static spinlock lock; + +obos_status Mm_InitializePMM() +{ + uintptr_t i = 0; + if (!MmS_GetFirstPMemMapEntry(&i)) + return OBOS_STATUS_INVALID_INIT_PHASE; + for (obos_pmem_map_entry* entry = MmS_GetFirstPMemMapEntry(&i); entry; ) + { + uintptr_t phys = entry->pmem_map_base; + size_t nPages = entry->pmem_map_size / OBOS_PAGE_SIZE; + if (phys % OBOS_PAGE_SIZE) + { + phys = phys + (OBOS_PAGE_SIZE-(phys%OBOS_PAGE_SIZE)); + nPages--; + } + if (phys == 0x0) + { + phys = OBOS_PAGE_SIZE; + nPages--; + } + Mm_TotalPhysicalPages += nPages; + if ((phys + nPages * OBOS_PAGE_SIZE) > Mm_PhysicalMemoryBoundaries) + Mm_PhysicalMemoryBoundaries = (phys + nPages * OBOS_PAGE_SIZE); + if (entry->pmem_map_type != PHYSICAL_MEMORY_TYPE_USABLE || !nPages) + { + Mm_TotalPhysicalPagesUsed += nPages; + entry = MmS_GetNextPMemMapEntry(entry, &i); + continue; + } + Mm_UsablePhysicalPages += nPages; + // struct freelist_node* node = (struct freelist_node*)(MmS_MapVirtFromPhys(phys)); + // memzero(node, sizeof(*node)); + // node->nPages = nPages; + // if (s_tail) + // ((struct freelist_node*)(MmS_MapVirtFromPhys((uintptr_t)s_tail)))->next = (struct freelist_node*)phys; + // if (!s_head) + // s_head = (struct freelist_node*)phys; + // node->prev = s_tail; + // s_tail = (struct freelist_node*)phys; + // s_nNodes++; + OBOS_Debug("%s: Free physical memory region at 0x%p-0x%p.\n", __func__, phys, phys+nPages*OBOS_PAGE_SIZE); + Mm_FreePhysicalPages(phys, nPages); + entry = MmS_GetNextPMemMapEntry(entry, &i); + } +#if OBOS_ARCHITECTURE_BITS == 64 + if (Mm_PhysicalMemoryBoundaries & 4294967295) + Mm_PhysicalMemoryBoundaries = (Mm_PhysicalMemoryBoundaries + 4294967295) & ~4294967295; +#endif + return OBOS_STATUS_SUCCESS; +} +static bool IsRegionSufficient(struct freelist_node* node, size_t nPages, size_t alignmentMask, size_t *nPagesRequiredP) +{ + size_t nPagesRequired = nPages; + uintptr_t nodePhys = MmS_UnmapVirtFromPhys(node); + nPagesRequired += (node->nPages & ((alignmentMask + 1) / OBOS_PAGE_SIZE - 1)); + uintptr_t mod = nodePhys & alignmentMask; + nPagesRequired += mod / OBOS_PAGE_SIZE; + if (nPagesRequiredP) + *nPagesRequiredP = nPagesRequired; + return node->nPages >= nPagesRequired; +} +#define MAP_TO_HHDM(addr, type) ((type*)(MmS_MapVirtFromPhys((uintptr_t)(addr)))) +#define UNMAP_FROM_HHDM(addr) (MmS_UnmapVirtFromPhys((void*)(addr))) +OBOS_NO_KASAN uintptr_t Mm_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status *status) +{ + if (!nPages) + { + *status = OBOS_STATUS_INVALID_ARGUMENT; + return 0; + } + if (!alignmentPages) + alignmentPages = 1; + if (__builtin_popcount(alignmentPages) > 1) + { + *status = OBOS_STATUS_INVALID_ARGUMENT; + return 0; + } + if (!s_head) + OBOS_Panic(OBOS_PANIC_NO_MEMORY, "No more avaliable physical memory!\n"); + uintptr_t phys = 0; + if (nPages % alignmentPages) + nPages += (alignmentPages - (nPages % alignmentPages)); + size_t alignmentMask = alignmentPages*OBOS_PAGE_SIZE-1; + size_t nPagesRequired = 0; + irql oldIrql = Core_SpinlockAcquireExplicit(&lock, IRQL_DISPATCH, true); + struct freelist_node* node = MAP_TO_HHDM(s_head, struct freelist_node); + while (UNMAP_FROM_HHDM(node) && !IsRegionSufficient(node, nPages, alignmentMask, &nPagesRequired)) + node = MAP_TO_HHDM(node->next, struct freelist_node); + if (!UNMAP_FROM_HHDM(node)) + { + if (status) + *status = OBOS_STATUS_NOT_ENOUGH_MEMORY; + Core_SpinlockRelease(&lock, oldIrql); + return 0; + } + OBOS_ASSERT(node->nPages >= nPagesRequired); + node->nPages -= nPagesRequired; + Mm_TotalPhysicalPagesUsed += nPagesRequired; + if (!node->nPages) + { + if (node->next) + MAP_TO_HHDM(node->next, struct freelist_node)->prev = node->prev; + if (node->prev) + MAP_TO_HHDM(node->prev, struct freelist_node)->next = node->next; + if ((uintptr_t)s_head == UNMAP_FROM_HHDM(node)) + s_head = node->next; + if ((uintptr_t)s_tail == UNMAP_FROM_HHDM(node)) + s_tail = node->prev; + s_nNodes--; + node->next = nullptr; + node->prev = nullptr; + } + phys = ((uintptr_t)node) + node->nPages * OBOS_PAGE_SIZE; + phys = UNMAP_FROM_HHDM(phys); + if (status) + *status = OBOS_STATUS_SUCCESS; + Core_SpinlockRelease(&lock, oldIrql); + OBOS_ASSERT(phys); + OBOS_ASSERT(phys < Mm_PhysicalMemoryBoundaries); + return phys; +} +OBOS_NO_KASAN obos_status Mm_FreePhysicalPages(uintptr_t addr, size_t nPages) +{ + OBOS_ASSERT(addr); + OBOS_ASSERT(!(addr % OBOS_PAGE_SIZE)); + OBOS_ASSERT(addr < Mm_PhysicalMemoryBoundaries); + addr -= (addr%OBOS_PAGE_SIZE); + if (!addr) + return OBOS_STATUS_INVALID_ARGUMENT; + irql oldIrql = Core_SpinlockAcquireExplicit(&lock, IRQL_DISPATCH, true); + struct freelist_node* node = MAP_TO_HHDM(addr, struct freelist_node); +#if OBOS_KASAN_ENABLED + memset(node->poison, OBOS_ASANPoisonValues[ASAN_POISON_ALLOCATED], sizeof(node->poison)); +#endif + node->nPages = nPages; + if (s_tail) + MAP_TO_HHDM(s_tail, struct freelist_node)->next = (struct freelist_node*)addr; + if (!s_head) + s_head = (struct freelist_node*)addr; + node->prev = s_tail; + Mm_TotalPhysicalPagesUsed -= nPages; + s_tail = (struct freelist_node*)addr; + Core_SpinlockRelease(&lock, oldIrql); + // OBOS_Debug("%s: Marking physical memory region at 0x%p-0x%p as free.\n", __func__, addr, addr+nPages*OBOS_PAGE_SIZE); + size_t nMemoryLeft = nPages * OBOS_PAGE_SIZE; + for (thread_node* node = Mm_ThreadsAwaitingPhysicalMemory.head; node; ) + { + thread_node* next = node->next; + thread* const curr = node->data; + if (nMemoryLeft >= curr->nBytesWaitingFor) + { + nMemoryLeft -= curr->nBytesWaitingFor; + CoreH_ThreadListRemove(&Mm_ThreadsAwaitingPhysicalMemory, node); + CoreH_ThreadReadyNode(curr, curr->snode); + } + + node = next; + } + return OBOS_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/src/oboskrnl/mm/pmm.h b/src/oboskrnl/mm/pmm.h new file mode 100644 index 00000000..ba14d945 --- /dev/null +++ b/src/oboskrnl/mm/pmm.h @@ -0,0 +1,56 @@ +/* + oboskrnl/mm/pmm.h + + Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include +#include + +#include + +extern size_t Mm_TotalPhysicalPages; +extern size_t Mm_TotalPhysicalPagesUsed; +extern size_t Mm_UsablePhysicalPages; +extern uintptr_t Mm_PhysicalMemoryBoundaries; +extern thread_list Mm_ThreadsAwaitingPhysicalMemory; + +/// +/// Initializes the PMM. +/// +/// The function status. +obos_status Mm_InitializePMM(); + +/// +/// Allocates physical pages. +/// +/// The amount of physical pages to allocate. +/// The alignment of the address returned, in pages. +/// [optional] A pointer to a variable that will store the function's status. Can be nullptr. +/// The physical pages, or zero on failure. +uintptr_t Mm_AllocatePhysicalPages(size_t nPages, size_t alignmentPages, obos_status* status); +/// +/// Frees physical pages. +/// +/// The address of the page. +/// The amount of pages to free. +obos_status Mm_FreePhysicalPages(uintptr_t addr, size_t nPages); + +// This returns a virtual address given a physical address. +// For example, on x86-64, this can offset the physical address by the hhdm. +void* MmS_MapVirtFromPhys(uintptr_t addr); +uintptr_t MmS_UnmapVirtFromPhys(void* virt); + +#ifdef __x86_64__ +# include +#elif defined(__m68k__) +# include +#else +# error Unknown architecture! +#endif + +obos_pmem_map_entry* MmS_GetFirstPMemMapEntry(uintptr_t* index); +// returns nullptr at the end of the list. +obos_pmem_map_entry* MmS_GetNextPMemMapEntry(obos_pmem_map_entry* current, uintptr_t* index); \ No newline at end of file diff --git a/src/oboskrnl/mm/swap.c b/src/oboskrnl/mm/swap.c index 432ef987..0dca5fdf 100644 --- a/src/oboskrnl/mm/swap.c +++ b/src/oboskrnl/mm/swap.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include swap_dev* Mm_SwapProvider; @@ -53,15 +53,19 @@ obos_status Mm_SwapOut(page* page) return OBOS_STATUS_INTERNAL_ERROR; return status; } - status = OBOSS_FreePhysicalPages(phys, nPages); - if (obos_is_error(status)) + if (phys) { - if (obos_is_error(Mm_SwapProvider->swap_free(Mm_SwapProvider, id, nPages))) - return OBOS_STATUS_INTERNAL_ERROR; - page->prot.present = true; - if (obos_is_error(MmS_SetPageMapping(page->owner->pt, page, phys))) - return OBOS_STATUS_INTERNAL_ERROR; - return status; + // FIXME: Why is phys sometimes set to zero? + status = Mm_FreePhysicalPages(phys, nPages); + if (obos_is_error(status)) + { + if (obos_is_error(Mm_SwapProvider->swap_free(Mm_SwapProvider, id, nPages))) + return OBOS_STATUS_INTERNAL_ERROR; + page->prot.present = true; + if (obos_is_error(MmS_SetPageMapping(page->owner->pt, page, phys))) + return OBOS_STATUS_INTERNAL_ERROR; + return status; + } } page->pagedOut = true; return OBOS_STATUS_SUCCESS; @@ -78,14 +82,14 @@ obos_status Mm_SwapIn(page* page) uintptr_t id = page->swapId; // Allocate a physical page. obos_status status = OBOS_STATUS_SUCCESS; - uintptr_t phys = OBOSS_AllocatePhysicalPages(nPages, nPages, &status); + uintptr_t phys = Mm_AllocatePhysicalPages(nPages, nPages, &status); if (obos_is_error(status)) return status; // Read the page from the swap. status = Mm_SwapProvider->swap_read(Mm_SwapProvider, id, phys, nPages, 0); if (obos_is_error(status)) { - if (obos_is_error(OBOSS_FreePhysicalPages(phys, nPages))) + if (obos_is_error(Mm_FreePhysicalPages(phys, nPages))) return OBOS_STATUS_INTERNAL_ERROR; return status; } @@ -93,7 +97,7 @@ obos_status Mm_SwapIn(page* page) status = Mm_SwapProvider->swap_free(Mm_SwapProvider, id, nPages); if (obos_is_error(status)) { - if (obos_is_error(OBOSS_FreePhysicalPages(phys, nPages))) + if (obos_is_error(Mm_FreePhysicalPages(phys, nPages))) return OBOS_STATUS_INTERNAL_ERROR; return status; } diff --git a/src/oboskrnl/scheduler/process.h b/src/oboskrnl/scheduler/process.h index 89249907..e9a6eb54 100644 --- a/src/oboskrnl/scheduler/process.h +++ b/src/oboskrnl/scheduler/process.h @@ -16,6 +16,8 @@ typedef struct process // If pid==1, this is the kernel process. uint64_t pid; thread_list threads; + uid currentUID; + gid currentGID; struct context* ctx; } process; extern uint64_t Core_NextPID; diff --git a/src/oboskrnl/scheduler/thread.c b/src/oboskrnl/scheduler/thread.c index b0323cc0..5d68935a 100644 --- a/src/oboskrnl/scheduler/thread.c +++ b/src/oboskrnl/scheduler/thread.c @@ -19,6 +19,10 @@ #include +#include +#include +#include + static uint64_t s_nextTID = 1; cpu_local* Core_CpuInfo; thread_affinity Core_DefaultThreadAffinity = 1; @@ -224,6 +228,12 @@ OBOS_NORETURN OBOS_PAGEABLE_FUNCTION static uintptr_t ExitCurrentThread(uintptr_ CoreS_FreeThreadContext(¤tThread->context); if (node->free) node->free(node); + if (currentThread->stackFree) + { + currentThread->stackFree(CoreS_GetThreadStack(¤tThread->context), + CoreS_GetThreadStackSize(¤tThread->context), + currentThread->stackFreeUserdata); + } if (!currentThread->references && currentThread->free) currentThread->free(currentThread); CoreS_GetCPULocalPtr()->currentThread = nullptr; @@ -236,3 +246,13 @@ OBOS_NORETURN OBOS_PAGEABLE_FUNCTION void Core_ExitCurrentThread() CoreS_CallFunctionOnStack(ExitCurrentThread, 0); OBOS_UNREACHABLE; } + +void CoreH_VMAStackFree(void* base, size_t sz, void* userdata) +{ + Mm_VirtualMemoryFree((context*)userdata, base, sz); +} +void CoreH_BasicMMStackFree(void* base, size_t sz, void* userdata) +{ + OBOS_UNUSED(userdata); + OBOS_BasicMMFreePages(base, sz); +} \ No newline at end of file diff --git a/src/oboskrnl/scheduler/thread.h b/src/oboskrnl/scheduler/thread.h index 319fcac8..8510f347 100644 --- a/src/oboskrnl/scheduler/thread.h +++ b/src/oboskrnl/scheduler/thread.h @@ -84,11 +84,17 @@ typedef struct thread struct process* proc; thread_ctx context; + // Frees the thread's stack. + void* stackFreeUserdata; + void(*stackFree)(void* base, size_t sz, void* userdata); // The node used by waitable_header (locks/wait.h) thread_node lock_node; size_t nWaiting; // the count of objects the thread is waiting on. size_t nSignaled; // the count of objects that have signaled the thread. + + thread_node phys_mem_node; + size_t nBytesWaitingFor; } thread; typedef struct thread_list { diff --git a/src/oboskrnl/scheduler/thread_context_info.h b/src/oboskrnl/scheduler/thread_context_info.h index a06b28dc..951855c1 100644 --- a/src/oboskrnl/scheduler/thread_context_info.h +++ b/src/oboskrnl/scheduler/thread_context_info.h @@ -71,9 +71,28 @@ OBOS_WEAK void CoreS_SetThreadIRQL(thread_ctx* ctx, irql newIRQL); /// The thread's context. /// The thread's IRQL. OBOS_WEAK irql CoreS_GetThreadIRQL(const thread_ctx* ctx); +/// +/// Gets the base of the stack of a thread. +/// +/// The thread's context. +/// The thread's stack. +OBOS_WEAK void* CoreS_GetThreadStack(const thread_ctx* ctx); +/// +/// Gets the size of the stack of a thread. +/// +/// The thread's context. +/// The thread's stack size. +OBOS_WEAK size_t CoreS_GetThreadStackSize(const thread_ctx* ctx); #ifdef __x86_64__ # include #elif defined(__m68k__) # include -#endif \ No newline at end of file +#endif + +// Some helpers to free stacks allocated using the VMA and the basic MM. + +// userdata should be the context* used to allocate the stack. +void CoreH_VMAStackFree(void* base, size_t sz, void* userdata); +// userdata is unused. +void CoreH_BasicMMStackFree(void* base, size_t sz, void* userdata); \ No newline at end of file diff --git a/src/oboskrnl/utils/list.h b/src/oboskrnl/utils/list.h index b9dc67b2..114aa762 100644 --- a/src/oboskrnl/utils/list.h +++ b/src/oboskrnl/utils/list.h @@ -39,7 +39,7 @@ attrib type* name##_LIST_GET_PREV(name* list, type* what) #define LIST_GENERATE(name, type, field) \ LIST_GENERATE_INTERNAL(name, type, field, ) #define LIST_GENERATE_STATIC(name, type, field) \ -LIST_GENERATE_INTERNAL(name, type, field, static) +LIST_GENERATE_INTERNAL(name, type, field, __attribute__((__unused__)) static) #define LIST_GENERATE_INTERNAL(name, type, field, attrib) \ attrib void name##_LIST_APPEND(name* list, type* what)\ {\ diff --git a/src/oboskrnl/utils/string.c b/src/oboskrnl/utils/string.c new file mode 100644 index 00000000..ed9fcf43 --- /dev/null +++ b/src/oboskrnl/utils/string.c @@ -0,0 +1,104 @@ +/* + * oboskrnl/utils/string.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include +#include + +#include + +#include + +#include + +void OBOS_StringSetAllocator(string* obj, allocator_info* allocator) +{ + obj->allocator = allocator; +} +void OBOS_InitString(string* obj, const char* str) +{ + OBOS_InitStringLen(obj, str, strlen(str)); +} +void OBOS_InitStringLen(string* obj, const char* str, size_t len) +{ + if (!obj->allocator) + obj->allocator = OBOS_KernelAllocator; + if (len <= 32) + { + memcpy(obj->sso, str, len); + obj->cap = 32; + obj->len = len; + } + else + { + OBOS_SetCapacityString(obj, len); + memcpy(obj->ls, str, len); + } +} +void OBOS_AppendStringC(string* obj, const char* str) +{ + size_t str_len = strlen(str); + size_t newlen = obj->len + str_len; + OBOS_ResizeString(obj, newlen); + memcpy(OBOS_GetStringPtr(obj) + (newlen-str_len), OBOS_GetStringPtr(obj), str_len); +} +void OBOS_AppendStringS(string* obj, string* str) +{ + size_t newlen = obj->len + str->len; + OBOS_ResizeString(obj, newlen); + memcpy(OBOS_GetStringPtr(obj) + (newlen-str->len), OBOS_GetStringPtr(obj), str->len); +} +void OBOS_ResizeString(string* obj, size_t len) +{ + if (((len + 0x1f) & ~0x1f) != obj->cap) + OBOS_SetCapacityString(obj, len); + if (len < obj->len) + memzero(obj->ls + obj->len, obj->len - len); + obj->len = len; +} +void OBOS_SetCapacityString(string* obj, size_t cap) +{ + if (cap <= 32) + return; + cap = (cap + 0x1f) & ~0x1f; + obj->cap = cap; + obj->ls = obj->allocator->Reallocate(obj->allocator, obj->ls, obj->cap, nullptr); +} +size_t OBOS_GetStringCapacity(const string* obj) +{ + return obj ? obj->cap : 0; +} +size_t OBOS_GetStringSize(const string* obj) +{ + return obj ? obj->len : 0; +} +char* OBOS_GetStringPtr(string* obj) +{ + return obj->cap <= 32 ? obj->sso : obj->ls; +} +const char* OBOS_GetStringCPtr(const string* obj) +{ + return obj->cap <= 32 ? obj->sso : obj->ls; +} +bool OBOS_CompareStringS(const string* str1, const string* str2) +{ + return OBOS_CompareStringNC(str1, OBOS_GetStringCPtr(str2), str2->len); +} +bool OBOS_CompareStringC(const string* str1, const char* str2) +{ + return OBOS_CompareStringNC(str1, str2, strlen(str2)); +} +bool OBOS_CompareStringNC(const string* str1, const char* str2, size_t str2len) +{ + if (str2len != str1->len) + return false; + return memcmp(OBOS_GetStringCPtr(str1), str2, str2len); +} +void OBOS_FreeString(string* obj) +{ + if (obj->cap <= 32) + return; + obj->allocator->Free(obj->allocator, obj->ls, obj->cap); +} \ No newline at end of file diff --git a/src/oboskrnl/utils/string.h b/src/oboskrnl/utils/string.h new file mode 100644 index 00000000..dd8c1e53 --- /dev/null +++ b/src/oboskrnl/utils/string.h @@ -0,0 +1,39 @@ +/* + * oboskrnl/utils/string.h + * + * Copyright (c) 2024 Omar Berrow + * + * String manipulation routines. +*/ + +#pragma once + +#include + +#include + +typedef struct string +{ + // small string optimization. + // this buffer is used when cap < 32 + char sso[33]; + char *ls; + size_t len, cap; + allocator_info* allocator; +} string; + +void OBOS_StringSetAllocator(string* obj, allocator_info* allocator); +void OBOS_InitString(string* obj, const char* str); +void OBOS_InitStringLen(string* obj, const char* str, size_t len); +void OBOS_AppendStringC(string* obj, const char* str); +void OBOS_AppendStringS(string* obj, string* str); +void OBOS_ResizeString(string* obj, size_t len); +void OBOS_SetCapacityString(string* obj, size_t cap); +size_t OBOS_GetStringCapacity(const string* obj); +size_t OBOS_GetStringSize(const string* obj); +char* OBOS_GetStringPtr(string* obj); +const char* OBOS_GetStringCPtr(const string* obj); +void OBOS_FreeString(string* obj); +bool OBOS_CompareStringS(const string* str1, const string* str2); +bool OBOS_CompareStringC(const string* str1, const char* str2); +bool OBOS_CompareStringNC(const string* str1, const char* str2, size_t str2len); \ No newline at end of file diff --git a/src/oboskrnl/vfs/alloc.c b/src/oboskrnl/vfs/alloc.c new file mode 100644 index 00000000..210d43ed --- /dev/null +++ b/src/oboskrnl/vfs/alloc.c @@ -0,0 +1,47 @@ +/* + * oboskrnl/vfs/alloc.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include "utils/tree.h" +#include + +#include +#include + +allocator_info* Vfs_Allocator; + +static basic_allocator alloc; +void* Vfs_Malloc(size_t cnt) +{ + if (!Vfs_Allocator) + { + OBOSH_ConstructBasicAllocator(&alloc); + Vfs_Allocator = (allocator_info*)&alloc; + } + return Vfs_Allocator->Allocate(Vfs_Allocator, cnt, nullptr); +} +void* Vfs_Calloc(size_t nObjs, size_t szObj) +{ + if (!Vfs_Allocator) + { + OBOSH_ConstructBasicAllocator(&alloc); + Vfs_Allocator = (allocator_info*)&alloc; + } + return Vfs_Allocator->ZeroAllocate(Vfs_Allocator, nObjs, szObj, nullptr); +} +void* Vfs_Realloc(void* what, size_t cnt) +{ + if (!Vfs_Allocator) + return nullptr; + return Vfs_Allocator->Reallocate(Vfs_Allocator, what, cnt, nullptr); +} +void Vfs_Free(void* what) +{ + if (!Vfs_Allocator) + return; + size_t szBlk = 0; + Vfs_Allocator->QueryBlockSize(Vfs_Allocator, what, &szBlk); + Vfs_Allocator->Free(Vfs_Allocator, what, szBlk); +} \ No newline at end of file diff --git a/src/oboskrnl/vfs/alloc.h b/src/oboskrnl/vfs/alloc.h new file mode 100644 index 00000000..ab21b365 --- /dev/null +++ b/src/oboskrnl/vfs/alloc.h @@ -0,0 +1,18 @@ +/* + * oboskrnl/vfs/alloc.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +#include + +extern allocator_info* Vfs_Allocator; + +void* Vfs_Malloc(size_t cnt); +void* Vfs_Calloc(size_t nObjs, size_t szObj); +void* Vfs_Realloc(void* what, size_t cnt); +void Vfs_Free(void* what); \ No newline at end of file diff --git a/src/oboskrnl/vfs/async.c b/src/oboskrnl/vfs/async.c new file mode 100644 index 00000000..a2d63328 --- /dev/null +++ b/src/oboskrnl/vfs/async.c @@ -0,0 +1,265 @@ +/* + * oboskrnl/vfs/alloc.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +// if depth == zero, it will search all nodes starting at 'from' +// otherwise, it will only search 'depth' nodes + +static bool is_eof(vnode* vn, size_t off) +{ + return off >= vn->filesize; +} + +static void async_read(struct async_irp* irp) +{ + mount* const point = irp->vn->mount_point ? irp->vn->mount_point : irp->vn->un.mounted; + const driver_header* driver = irp->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (irp->vn->vtype == VNODE_TYPE_CHR || irp->vn->vtype == VNODE_TYPE_BLK) + driver = &irp->vn->un.device->driver->header; + if (!VfsH_LockMountpoint(point)) + goto abort; + driver->ftable.read_sync( + irp->vn->desc, + irp->un.buf, + irp->requestSize, + irp->fileoff, + nullptr + ); + VfsH_UnlockMountpoint(point); + + abort: + Core_EventSet(irp->e, true); + irp->vn->nPendingAsyncIO--; + Vfs_Free(irp); + Core_ExitCurrentThread(); +} +static void async_write(struct async_irp* irp) +{ + mount* const point = irp->vn->mount_point ? irp->vn->mount_point : irp->vn->un.mounted; + const driver_header* driver = irp->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (irp->vn->vtype == VNODE_TYPE_CHR || irp->vn->vtype == VNODE_TYPE_BLK) + driver = &irp->vn->un.device->driver->header; + if (!VfsH_LockMountpoint(point)) + goto abort; + driver->ftable.write_sync( + irp->vn->desc, + irp->un.cbuf, + irp->requestSize, + irp->fileoff, + nullptr + ); + VfsH_UnlockMountpoint(point); + + abort: + Core_EventSet(irp->e, true); + irp->vn->nPendingAsyncIO--; + Vfs_Free(irp); + Core_GetCurrentThread(); +} + +obos_status Vfs_FdAWrite(fd* desc, const void* buf, size_t nBytes, event* evnt) +{ + if (!desc || !buf || !evnt) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + if (!nBytes) + return OBOS_STATUS_SUCCESS; + if (is_eof(desc->vn, desc->offset)) + return OBOS_STATUS_EOF; + if (!(desc->flags & FD_FLAGS_WRITE)) + return OBOS_STATUS_ACCESS_DENIED; + if (desc->flags & FD_FLAGS_UNCACHED) + { + // If an asynchronous, uncached read is made, then first check if nBytes < sector size. + // If it is not, or the vnode is not backed by a drive, then an IRP is made. + // Otherwise, the read is made, and the operation completes. + size_t sector_size = 0; + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + if (!point->device) + goto irp; + driver->ftable.get_blk_size(point->device->desc, §or_size); + if (nBytes >= sector_size) + goto irp; + + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + obos_status status = driver->ftable.write_sync( + desc->vn->desc, + buf, + nBytes, + desc->offset, + nullptr + ); + Core_EventSet(evnt, true); + desc->offset += nBytes; + VfsH_UnlockMountpoint(point); + return status; + } + else + { + if ((desc->offset) > desc->vn->pagecache.sz) + goto irp; + size_t nBytesToRead = nBytes; + if ((nBytesToRead + desc->offset) > desc->vn->pagecache.sz) + nBytesToRead -= ((nBytesToRead + desc->offset) - desc->vn->pagecache.sz); + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + memcpy(desc->vn->pagecache.data + desc->offset, buf, nBytesToRead); + VfsH_UnlockMountpoint(point); + if (!(nBytesToRead-nBytes)) + { + desc->offset += nBytes; + return OBOS_STATUS_SUCCESS; + } + buf += nBytesToRead; + nBytes -= nBytesToRead; + } + irp: + (void)0; + struct async_irp* irp = Vfs_Calloc(1, sizeof(struct async_irp)); + irp->e = evnt; + irp->rw = true; + irp->fileoff = desc->offset; + irp->cached = !(desc->flags & FD_FLAGS_UNCACHED); + irp->requestSize = nBytes; + irp->un.cbuf = buf; + irp->vn = desc->vn; + irp->worker = CoreH_ThreadAllocate(nullptr); + thread_ctx ctx = {}; + CoreS_SetupThreadContext( + &ctx, + (uintptr_t)async_write, (uintptr_t)irp, + false, + Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr, nullptr), + 0x10000); + CoreH_ThreadInitialize(irp->worker, THREAD_PRIORITY_HIGH, Core_DefaultThreadAffinity, &ctx); + irp->worker->stackFree = CoreH_VMAStackFree; + irp->worker->stackFreeUserdata = &Mm_KernelContext; + Core_ProcessAppendThread(OBOS_KernelProcess, irp->worker); + desc->vn->nPendingAsyncIO++; + CoreH_ThreadReady(irp->worker); + desc->offset += nBytes; + return OBOS_STATUS_SUCCESS; +} +obos_status Vfs_FdARead(fd* desc, void* buf, size_t nBytes, event* evnt) +{ + if (!desc || !buf || !evnt) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + if (!nBytes) + return OBOS_STATUS_SUCCESS; + if (is_eof(desc->vn, desc->offset)) + return OBOS_STATUS_EOF; + if (!(desc->flags & FD_FLAGS_READ)) + return OBOS_STATUS_ACCESS_DENIED; + if (desc->flags & FD_FLAGS_UNCACHED) + { + // If an asynchronous, uncached read is made, then first check if nBytes < sector size. + // If it is not, or the vnode is not backed by a drive, then an IRP is made. + // Otherwise, the read is made, and the operation completes. + size_t sector_size = 0; + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + if (!point->device) + goto irp; + point->device->driver->header.ftable.get_blk_size(point->device->desc, §or_size); + if (nBytes >= sector_size) + goto irp; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + obos_status status = driver->ftable.read_sync( + desc->vn->desc, + buf, + nBytes, + desc->offset, + nullptr + ); + Core_EventSet(evnt, true); + desc->offset += nBytes; + VfsH_UnlockMountpoint(point); + return status; + } + else + { + if ((desc->offset) > desc->vn->pagecache.sz) + goto irp; + size_t nBytesToRead = nBytes; + if ((nBytesToRead + desc->offset) > desc->vn->pagecache.sz) + nBytesToRead -= ((nBytesToRead + desc->offset) - desc->vn->pagecache.sz); + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + memcpy(buf, desc->vn->pagecache.data + desc->offset, nBytesToRead); + VfsH_UnlockMountpoint(point); + if (!(nBytesToRead-nBytes)) + { + desc->offset += nBytes; + return OBOS_STATUS_SUCCESS; + } + buf += nBytesToRead; + nBytes -= nBytesToRead; + } + irp: + (void)0; + struct async_irp* irp = Vfs_Calloc(1, sizeof(struct async_irp)); + irp->e = evnt; + irp->rw = false; + irp->fileoff = desc->offset; + irp->cached = !(desc->flags & FD_FLAGS_UNCACHED); + irp->requestSize = nBytes; + irp->un.buf = buf; + irp->vn = desc->vn; + irp->worker = CoreH_ThreadAllocate(nullptr); + thread_ctx ctx = {}; + CoreS_SetupThreadContext( + &ctx, + (uintptr_t)async_read, (uintptr_t)irp, + false, + Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, 0x10000, 0, VMA_FLAGS_KERNEL_STACK, nullptr, nullptr), + 0x10000); + CoreH_ThreadInitialize(irp->worker, THREAD_PRIORITY_HIGH, Core_DefaultThreadAffinity, &ctx); + irp->worker->stackFree = CoreH_VMAStackFree; + irp->worker->stackFreeUserdata = &Mm_KernelContext; + desc->vn->nPendingAsyncIO++; + CoreH_ThreadReady(irp->worker); + desc->offset += nBytes; + return OBOS_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/src/oboskrnl/vfs/dirent.c b/src/oboskrnl/vfs/dirent.c new file mode 100644 index 00000000..f3731fef --- /dev/null +++ b/src/oboskrnl/vfs/dirent.c @@ -0,0 +1,266 @@ +/* + * oboskrnl/vfs/dirent.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include "vfs/vnode.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +static size_t str_search(const char* str, char ch) +{ + size_t ret = strchr(str, ch); + for (; str[ret] == ch && str[ret] != 0; ret++) + ; + return ret; +} +static namecache_ent* namecache_lookup_internal(namecache* nc, const char* path) +{ + namecache_ent what = { }; + OBOS_StringSetAllocator(&what.path, Vfs_Allocator); + OBOS_InitString(&what.path, path); + namecache_ent* hit = RB_FIND(namecache, nc, &what); + OBOS_FreeString(&what.path); + return hit; +} +static dirent* namecache_lookup(namecache* nc, const char* path) +{ + namecache_ent* nc_ent = namecache_lookup_internal(nc, path); + if (!nc_ent) + return nullptr; + return nc_ent->ent; +} +static void namecache_insert(namecache* nc, dirent* what, const char* path, size_t pathlen) +{ + namecache_ent* ent = Vfs_Calloc(1, sizeof(namecache_ent)); + ent->ent = what; + ent->ref = what->vnode; + ent->ref->refs++; + OBOS_StringSetAllocator(&ent->path, Vfs_Allocator); + OBOS_InitStringLen(&ent->path, path, pathlen); + if (!namecache_lookup_internal(nc, OBOS_GetStringCPtr(&ent->path))) + RB_INSERT(namecache, nc, ent); + else + { + OBOS_FreeString(&ent->path); + Vfs_Free(ent); + } +} +static dirent* on_match(dirent** const curr_, dirent** const root, const char** const tok, size_t* const tok_len, const char** const path, + size_t* const path_len, size_t* const lastMountPoint, mount** const lastMount) +{ + dirent *curr = *curr_; + *root = curr; + const char *newtok = (*tok) + str_search(*tok, '/'); + if (newtok >= (*path + *path_len)) + { + if (*lastMount) + { + size_t currentPathLen = strlen(*path+(*lastMountPoint))-1; + if ((*path+(*lastMountPoint))[currentPathLen] != '/') + currentPathLen++; + while ((*path+(*lastMountPoint))[currentPathLen] == '/') + currentPathLen--; + namecache_insert(&(*lastMount)->nc, curr, *path+(*lastMountPoint), currentPathLen); + } + return curr; + } + if (!curr->d_children.nChildren) + return nullptr; // could not find node. + *tok = newtok; + size_t currentPathLen = strlen(*tok)-1; + if ((*tok)[currentPathLen] != '/') + currentPathLen++; + while ((*tok)[currentPathLen] == '/') + currentPathLen--; + *tok_len = strchr(*tok, '/'); + if (*tok_len != currentPathLen) + (*tok_len)--; + while ((*tok)[(*tok_len) - 1] == '/') + (*tok_len)--; + if (curr->vnode && curr->vnode->flags & VFLAGS_MOUNTPOINT) + { + (*lastMountPoint) = ((*tok)-(*path)); + (*lastMount) = curr->vnode->un.mounted; + dirent* hit = namecache_lookup(&curr->vnode->un.mounted->nc, *tok); + if (!hit) + *root = curr->vnode->un.mounted->root; + return hit; + } + return nullptr; +} +dirent* VfsH_DirentLookupFrom(const char* path, dirent* root) +{ + if (!path) + return nullptr; + size_t path_len = strlen(path); + if (!path_len) + return nullptr; + for (; *path == '/'; path++, path_len--) + ; + const char* tok = path; + size_t tok_len = strchr(tok, '/'); + if (tok_len != path_len) + tok_len--; + while (tok[tok_len - 1] == '/') + tok_len--; + if (!tok_len) + return nullptr; + // Offset of the last mount point in the path. + size_t lastMountPoint = 0; + mount* lastMount = root->vnode->flags & VFLAGS_MOUNTPOINT ? root->vnode->un.mounted : root->vnode->mount_point; + if (root->vnode && root->vnode->flags & VFLAGS_MOUNTPOINT) + { + // If 'root' is at the root of it's mount point, consult the name cache. + dirent* hit = namecache_lookup(&root->vnode->un.mounted->nc, path); + if (hit) + return hit; + } + while(root) + { + dirent* curr = root; + if (OBOS_CompareStringNC(&root->name, tok, tok_len)) + { + // Match! + root = curr->d_children.head ? curr->d_children.head : root; + dirent* what = + on_match(&curr, &root, &tok, &tok_len, &path, &path_len, &lastMountPoint, &lastMount); + if (what) + return what; + continue; + } + for (curr = root->d_children.head; curr;) + { + if (OBOS_CompareStringNC(&curr->name, tok, tok_len)) + { + // Match! + dirent* what = + on_match(&curr, &root, &tok, &tok_len, &path, &path_len, &lastMountPoint, &lastMount); + if (what) + return what; + // else + // return nullptr; + curr = curr->d_children.head ? curr->d_children.head : curr; + break; + } + + // root = curr->d_children.head ? curr->d_children.head : root; + curr = curr->d_next_child; + } + if (!curr) + root = root->d_parent; + } + return nullptr; +} +dirent* VfsH_DirentLookup(const char* path) +{ + dirent* root = Vfs_Root; + if (strcmp(path, "/")) + return root; + return VfsH_DirentLookupFrom(path, root); +} +void VfsH_DirentAppendChild(dirent* parent, dirent* child) +{ + if(!parent->d_children.head) + parent->d_children.head = child; + if (parent->d_children.tail) + parent->d_children.tail->d_next_child = child; + child->d_prev_child = parent->d_children.tail; + parent->d_children.tail = child; + parent->d_children.nChildren++; + child->d_parent = parent; + mount* const point = parent->vnode->mount_point ? parent->vnode->mount_point : parent->vnode->un.mounted; + LIST_APPEND(dirent_list, &point->dirent_list, child); + if (child->vnode) + child->vnode->refs++; +} +void VfsH_DirentRemoveChild(dirent* parent, dirent* what) +{ + if (what->d_prev_child) + what->d_prev_child->d_next_child = what->d_next_child; + if (what->d_next_child) + what->d_next_child->d_prev_child = what->d_prev_child; + if (parent->d_children.head == what) + parent->d_children.head = what->d_next_child; + if (parent->d_children.tail == what) + parent->d_children.tail = what->d_prev_child; + parent->d_children.nChildren--; + what->d_parent = nullptr; // we're now an orphan :( + mount* const point = parent->vnode->mount_point ? parent->vnode->mount_point : parent->vnode->un.mounted; + LIST_REMOVE(dirent_list, &point->dirent_list, what); +} + +vnode* Drv_AllocateVNode(driver_id* drv, dev_desc desc, size_t filesize, vdev** dev_p, uint32_t type) +{ + static file_perm default_fileperm = { + .owner_read = true, + .group_read = true, + .other_read = false, + .owner_write = true, + .group_write = true, + .other_write = false, + .owner_exec = false, + .group_exec = false, + .other_exec = false, + }; + if (!drv) + return nullptr; + vdev* dev = Vfs_Calloc(1, sizeof(vdev)); + dev->desc = desc; + dev->driver = drv; + dev->refs++; + vnode *vn = Vfs_Calloc(1, sizeof(vnode)); + vn->desc = desc; + vn->filesize = filesize; + vn->un.device = dev; + vn->perm = default_fileperm; + vn->vtype = type; + vn->group_uid = ROOT_GID; + vn->owner_uid = ROOT_UID; + if (dev_p) + *dev_p = dev; + return vn; +} +dirent* Drv_RegisterVNode(struct vnode* vn, const char* const dev_name) +{ + if (!vn || !dev_name) + return nullptr; + dirent* parent = VfsH_DirentLookup(OBOS_DEV_PREFIX); + if (!parent) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "%s: Could not find directory at OBOS_DEV_PREFIX (%s) specified at build time.\n", __func__, OBOS_DEV_PREFIX); + dirent* ent = VfsH_DirentLookupFrom(dev_name, parent); + if (ent && ent->vnode == vn) + return ent; + mount* const point = parent->vnode->mount_point ? parent->vnode->mount_point : parent->vnode->un.mounted; + if (!ent) + ent = Vfs_Calloc(1, sizeof(dirent)); + else + { + ent->vnode = vn; + + ent->vnode->mount_point = point; + return ent; + } + if (!VfsH_LockMountpoint(point)) + return nullptr; + ent->vnode = vn; + ent->vnode->mount_point = point; + OBOS_StringSetAllocator(&ent->name, Vfs_Allocator); + OBOS_InitString(&ent->name, dev_name); + VfsH_DirentAppendChild(parent, ent); + VfsH_UnlockMountpoint(point); + return ent; +} +LIST_GENERATE(dirent_list, dirent, node); diff --git a/src/oboskrnl/vfs/dirent.h b/src/oboskrnl/vfs/dirent.h new file mode 100644 index 00000000..0fa60463 --- /dev/null +++ b/src/oboskrnl/vfs/dirent.h @@ -0,0 +1,47 @@ +/* + * oboskrnl/vfs/dirent.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +#include + +#include +#include + +typedef LIST_HEAD(dirent_list, struct dirent) dirent_list; +typedef struct dirent +{ + struct + { + struct dirent* parent; + struct + { + struct dirent* head; + struct dirent* tail; + size_t nChildren; + } children; + struct dirent* next_child; + struct dirent* prev_child; + } tree_info; + struct vnode* vnode; + string name; + LIST_NODE(dirent_list, struct dirent) node; +} dirent; +LIST_PROTOTYPE(dirent_list, dirent, node); +#define d_children tree_info.children +#define d_next_child tree_info.next_child +#define d_prev_child tree_info.prev_child +#define d_parent tree_info.parent +void VfsH_DirentAppendChild(dirent* parent, dirent* child); +void VfsH_DirentRemoveChild(dirent* parent, dirent* what); +// path shouldn't have unneeded slashes, this way, there is a higher chance of a name cache hit, thus speeding up +// the lookup +dirent* VfsH_DirentLookup(const char* path); +dirent* VfsH_DirentLookupFrom(const char* path, dirent* root); + +OBOS_EXPORT dirent* Drv_RegisterVNode(struct vnode* vn, const char* const dev_name); \ No newline at end of file diff --git a/src/oboskrnl/vfs/fd.c b/src/oboskrnl/vfs/fd.c new file mode 100644 index 00000000..5215420f --- /dev/null +++ b/src/oboskrnl/vfs/fd.c @@ -0,0 +1,325 @@ +/* + * oboskrnl/vfs/fd.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +// if depth == zero, it will search all nodes starting at 'from' +// otherwise, it will only search 'depth' nodes + +static bool is_eof(vnode* vn, size_t off) +{ + if (vn->vtype != VNODE_TYPE_REG) + return false; + return off >= vn->filesize; +} +obos_status Vfs_FdOpen(fd* const desc, const char* path, uint32_t oflags) +{ + dirent* ent = VfsH_DirentLookup(path); + if (!ent) + return OBOS_STATUS_NOT_FOUND; + return Vfs_FdOpenDirent(desc, ent, oflags); +} +obos_status Vfs_FdOpenDirent(fd* const desc, dirent* ent, uint32_t oflags) +{ + if (!desc || !ent) + return OBOS_STATUS_INVALID_ARGUMENT; + if (desc->flags & FD_FLAGS_OPEN) + return OBOS_STATUS_ALREADY_INITIALIZED; + OBOS_ASSERT(ent->vnode); + if (ent->vnode->vtype == VNODE_TYPE_DIR) + return OBOS_STATUS_NOT_A_FILE; + if (ent->vnode->vtype == VNODE_TYPE_CHR) + oflags |= FD_FLAGS_UNCACHED; + desc->vn = ent->vnode; + desc->flags |= FD_FLAGS_OPEN; + desc->flags |= FD_FLAGS_READ; + desc->flags |= FD_FLAGS_WRITE; + if (desc->vn->owner_uid == Core_GetCurrentThread()->proc->currentUID) + { + // We have owner perms. + vnode* const vn = desc->vn; + if (!vn->perm.owner_read) + desc->flags &= FD_FLAGS_READ; + if (!vn->perm.owner_write) + desc->flags &= FD_FLAGS_READ; + } + else if (desc->vn->group_uid == Core_GetCurrentThread()->proc->currentGID) + { + // We have group perms. + vnode* const vn = desc->vn; + if (!vn->perm.group_read) + desc->flags &= FD_FLAGS_READ; + if (!vn->perm.group_write) + desc->flags &= FD_FLAGS_READ; + } + else + { + // We have other perms. + vnode* const vn = desc->vn; + if (!vn->perm.other_read) + desc->flags &= FD_FLAGS_READ; + if (!vn->perm.other_write) + desc->flags &= FD_FLAGS_READ; + } + if (oflags & FD_OFLAGS_READ_ONLY) + desc->flags &= ~FD_FLAGS_WRITE; + if (oflags & FD_OFLAGS_UNCACHED) + desc->flags |= FD_FLAGS_UNCACHED; + desc->vn->refs++; + LIST_APPEND(fd_list, &desc->vn->opened, desc); + desc->flags |= FD_FLAGS_OPEN; + return OBOS_STATUS_SUCCESS; +} +static obos_status do_uncached_write(fd* desc, const void* from, size_t nBytes, size_t* nWritten_) +{ + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + size_t blkSize = 0; + driver->ftable.get_blk_size(desc->vn->desc, &blkSize); + if (nBytes % blkSize) + return OBOS_STATUS_INVALID_ARGUMENT; + nBytes /= blkSize; + const uintptr_t offset = desc->offset / blkSize; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + obos_status status = driver->ftable.write_sync(desc->vn->desc, from, nBytes, offset, nWritten_); + VfsH_UnlockMountpoint(point); + if (obos_expect(obos_is_error(status) == true, 0)) + return status; + return OBOS_STATUS_SUCCESS; +} +obos_status Vfs_FdWrite(fd* desc, const void* buf, size_t nBytes, size_t* nWritten) +{ + if (!desc || !buf) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + if (!nBytes) + return OBOS_STATUS_SUCCESS; + if (is_eof(desc->vn, desc->offset)) + return OBOS_STATUS_EOF; + if (!(desc->flags & FD_FLAGS_WRITE)) + return OBOS_STATUS_ACCESS_DENIED; + if (nBytes > (desc->vn->filesize - desc->offset) && desc->vn->vtype == VNODE_TYPE_REG) + desc->vn->filesize += (nBytes-(desc->vn->filesize - desc->offset)); // add the difference to the file size + obos_status status = OBOS_STATUS_SUCCESS; + if (desc->flags & FD_FLAGS_UNCACHED) + { + // Keep it nice and simple, and just do an uncached write on the file. + + status = do_uncached_write(desc, buf, nBytes, nWritten); + } + else + { + pagecache_dirty_region* dirty = VfsH_PCDirtyRegionCreate(&desc->vn->pagecache, desc->offset, nBytes); + OBOS_ASSERT(obos_expect(dirty != nullptr, 0)); + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + Core_MutexAcquire(&dirty->lock); + if (desc->vn->pagecache.sz <= desc->offset) + VfsH_PageCacheResize(&desc->vn->pagecache, desc->vn, desc->offset + nBytes); + memcpy(desc->vn->pagecache.data + desc->offset, buf, nBytes); + VfsH_UnlockMountpoint(point); + Core_MutexRelease(&dirty->lock); + + if (nWritten) + *nWritten = nBytes; + } + if (obos_expect(obos_is_success(status), 1)) + Vfs_FdSeek(desc, nBytes, SEEK_CUR); + return status; +} +static obos_status do_uncached_read(fd* desc, void* into, size_t nBytes, size_t* nRead_) +{ + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + size_t blkSize = 0; + driver->ftable.get_blk_size(desc->vn->desc, &blkSize); + if (nBytes % blkSize) + return OBOS_STATUS_INVALID_ARGUMENT; + nBytes /= blkSize; + const uintptr_t offset = desc->offset / blkSize; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + obos_status status = driver->ftable.read_sync(desc->vn->desc, into, nBytes, offset, nRead_); + VfsH_UnlockMountpoint(point); + if (obos_expect(obos_is_error(status) == true, 0)) + return status; + return OBOS_STATUS_SUCCESS; +} +obos_status Vfs_FdRead(fd* desc, void* buf, size_t nBytes, size_t* nRead) +{ + if (!desc || !buf) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + if (!nBytes) + return OBOS_STATUS_SUCCESS; + if (is_eof(desc->vn, desc->offset)) + return OBOS_STATUS_EOF; + if (!(desc->flags & FD_FLAGS_READ)) + return OBOS_STATUS_ACCESS_DENIED; + if (nBytes > (desc->vn->filesize - desc->offset) && desc->vn->vtype == VNODE_TYPE_REG) + nBytes = desc->vn->filesize - desc->offset; // truncate size to the space we have left in the file. + obos_status status = OBOS_STATUS_SUCCESS; + if (desc->flags & FD_FLAGS_UNCACHED) + { + // Keep it nice and simple, and just do an uncached read on the file. + + status = do_uncached_read(desc, buf, nBytes, nRead); + } + else + { + pagecache_dirty_region* dirty = VfsH_PCDirtyRegionLookup(&desc->vn->pagecache, desc->offset); + if (dirty) + Core_MutexAcquire(&dirty->lock); + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + if ((desc->offset+nBytes) > desc->vn->pagecache.sz) + VfsH_PageCacheResize(&desc->vn->pagecache, desc->vn, desc->offset + nBytes); + memcpy(buf, desc->vn->pagecache.data + desc->offset, nBytes); + if (dirty) + Core_MutexRelease(&dirty->lock); + VfsH_UnlockMountpoint(point); + + if (nRead) + *nRead = nBytes; + } + if (obos_expect(obos_is_success(status), 1)) + Vfs_FdSeek(desc, nBytes, SEEK_CUR); + return status; +} +obos_status Vfs_FdSeek(fd* desc, off_t off, whence_t whence) +{ + if (!desc) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + size_t finalOff = 0; + switch (whence) + { + case SEEK_SET: + finalOff = off; + break; + case SEEK_END: + finalOff = (off_t)desc->vn->filesize - 1 + off; + break; + case SEEK_CUR: + finalOff = (off_t)desc->offset + off; + break; + } + if (is_eof(desc->vn, finalOff)) + return OBOS_STATUS_EOF; + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + size_t blkSize = 0; + driver->ftable.get_blk_size(desc->vn->desc, &blkSize); + if (finalOff % blkSize) + return OBOS_STATUS_INVALID_ARGUMENT; + desc->offset = finalOff; + return OBOS_STATUS_SUCCESS; +} +uoff_t Vfs_FdTellOff(const fd* desc) +{ + if (desc) + return desc->offset; + return (uoff_t)(-1); +} +size_t Vfs_FdGetBlkSz(const fd* desc) +{ + if (!desc) + return (size_t)-1; + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + const driver_header* driver = desc->vn->vtype == VNODE_TYPE_REG ? &point->fs_driver->driver->header : nullptr; + if (desc->vn->vtype == VNODE_TYPE_CHR || desc->vn->vtype == VNODE_TYPE_BLK) + driver = &desc->vn->un.device->driver->header; + size_t blkSize = 0; + driver->ftable.get_blk_size(desc->vn->desc, &blkSize); + return blkSize; +} +obos_status Vfs_FdEOF(const fd* desc) +{ + if (!desc) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + return is_eof(desc->vn, desc->offset) ? OBOS_STATUS_EOF : OBOS_STATUS_SUCCESS; +} +vnode* Vfs_FdGetVnode(fd* desc) +{ + return desc ? desc->vn : nullptr; +} +obos_status Vfs_FdIoctl(fd* desc, size_t nParameters, uint64_t request, ...) +{ + if (!desc) + return OBOS_STATUS_INVALID_ARGUMENT; + if (!(desc->flags & FD_FLAGS_OPEN)) + return OBOS_STATUS_UNINITIALIZED; + if (desc->vn->vtype != VNODE_TYPE_BLK && desc->vn->vtype != VNODE_TYPE_CHR) + return OBOS_STATUS_INVALID_IOCTL; + va_list list; + va_start(list, request); + obos_status status = desc->vn->un.device->driver->header.ftable.ioctl_var(nParameters, request, list); + va_end(list); + return status; +} +obos_status Vfs_FdFlush(fd* desc) +{ + if (!desc) + return OBOS_STATUS_SUCCESS; + if (desc->flags & FD_FLAGS_UNCACHED) + return OBOS_STATUS_INVALID_OPERATION; + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + VfsH_PageCacheFlush(&desc->vn->pagecache, desc->vn); + VfsH_UnlockMountpoint(point); + return OBOS_STATUS_SUCCESS; +} +obos_status Vfs_FdClose(fd* desc) +{ + Vfs_FdFlush(desc); + mount* const point = desc->vn->mount_point ? desc->vn->mount_point : desc->vn->un.mounted; + if (!VfsH_LockMountpoint(point)) + return OBOS_STATUS_ABORTED; + vnode* vn = desc->vn; + LIST_REMOVE(fd_list, &desc->vn->opened, desc); + vn->refs--; + desc->flags &= ~FD_FLAGS_OPEN; + VfsH_UnlockMountpoint(point); + return OBOS_STATUS_SUCCESS; +} +LIST_GENERATE(fd_list, struct fd, node); \ No newline at end of file diff --git a/src/oboskrnl/vfs/fd.h b/src/oboskrnl/vfs/fd.h new file mode 100644 index 00000000..c15ca1c0 --- /dev/null +++ b/src/oboskrnl/vfs/fd.h @@ -0,0 +1,53 @@ +/* + * oboskrnl/vfs/fd.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include +#include + +#include +#include + +#include + +#include + +typedef LIST_HEAD(fd_list, struct fd) fd_list; +LIST_PROTOTYPE(fd_list, struct fd, node); +enum +{ + FD_FLAGS_OPEN = 1, + FD_FLAGS_READ = 2, + FD_FLAGS_WRITE = 4, + FD_FLAGS_UNCACHED = 8, +}; +enum +{ + FD_OFLAGS_READ_ONLY = 1, + FD_OFLAGS_UNCACHED = 2, +}; +typedef struct fd +{ + struct vnode* vn; + uint32_t flags; + uoff_t offset; + LIST_NODE(fd_list, struct fd) node; +} fd; +obos_status Vfs_FdOpen(fd* const desc, const char* path, uint32_t oflags); +obos_status Vfs_FdOpenDirent(fd* const desc, dirent* ent, uint32_t oflags); +obos_status Vfs_FdWrite(fd* desc, const void* buf, size_t nBytes, size_t* nWritten); +obos_status Vfs_FdRead(fd* desc, void* buf, size_t nBytes, size_t* nRead); +obos_status Vfs_FdAWrite(fd* desc, const void* buf, size_t nBytes, event* evnt); +obos_status Vfs_FdARead(fd* desc, void* buf, size_t nBytes, event* evnt); +obos_status Vfs_FdSeek(fd* desc, off_t off, whence_t whence); +uoff_t Vfs_FdTellOff(const fd* desc); +size_t Vfs_FdGetBlkSz(const fd* desc); +obos_status Vfs_FdEOF(const fd* desc); +struct vnode* Vfs_FdGetVnode(fd* desc); +obos_status Vfs_FdIoctl(fd* desc, size_t nParameters, uint64_t request, ...); +obos_status Vfs_FdFlush(fd* desc); +obos_status Vfs_FdClose(fd* desc); \ No newline at end of file diff --git a/src/oboskrnl/vfs/init.c b/src/oboskrnl/vfs/init.c new file mode 100644 index 00000000..a029b585 --- /dev/null +++ b/src/oboskrnl/vfs/init.c @@ -0,0 +1,102 @@ +/* + * oboskrnl/vfs/init.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include "driver_interface/header.h" +#include "driver_interface/loader.h" +#include "mm/alloc.h" +#include "mm/context.h" +#include "scheduler/thread.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +// InitRD driver name. +#include + +#include +#include + +/* + "--mount-initrd=pathspec: Mounts the InitRD at pathspec if specified, otherwise the initrd is left unmounted." + "--root-fs-uuid=uuid: Specifies the partition to mount as root. If set to 'initrd', the initrd" + " is used as root." + "--root-fs-partid=partid: Specifies the partition to mount as root. If set to 'initrd', the initrd" + " is used as root." +*/ + +void Vfs_Initialize() +{ + char* root_uuid = OBOS_GetOPTS("root-fs-uuid"); + char* root_partid = OBOS_GetOPTS("root-fs-partid"); + if (!root_uuid && !root_partid) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Neither a root UUID, nor a root PARTID was specified.\n"); + if (root_uuid && root_partid) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "Options, 'root-fs-uuid' and 'root-fs-partid', are mutually exclusive.\n"); + Vfs_Root = Vfs_Calloc(1, sizeof(dirent)); + OBOS_StringSetAllocator(&Vfs_Root->name, Vfs_Allocator); + OBOS_InitString(&Vfs_Root->name, "/"); + Vfs_Root->vnode = Vfs_Calloc(1, sizeof(vnode)); + Vfs_Root->vnode->vtype = VNODE_TYPE_DIR; + Vfs_Root->vnode->perm.group_exec = false; + Vfs_Root->vnode->perm.group_write = true; + Vfs_Root->vnode->perm.group_read = true; + Vfs_Root->vnode->perm.owner_exec = false; + Vfs_Root->vnode->perm.owner_write = true; + Vfs_Root->vnode->perm.owner_read = true; + Vfs_Root->vnode->perm.other_exec = false; + Vfs_Root->vnode->perm.other_write = false; + Vfs_Root->vnode->perm.other_read = true; + vdev initrd_dev = { }; + for (driver_node* cur = Drv_LoadedDrivers.head; cur; ) + { + if (uacpi_strncmp(cur->data->header.driverName, INITRD_DRIVER_NAME, 32) == 0) + { + initrd_dev.driver = cur->data; + break; + } + cur = cur->next; + } + if (!initrd_dev.driver) + return; + mount* root = nullptr; + Vfs_Mount("/", nullptr, &initrd_dev, &root); + Vfs_Root->vnode->mount_point = root; + if (root_partid) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, root_partid, strlen(root_partid)); + if (root_uuid) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, root_uuid, strlen(root_uuid)); +} +void Vfs_FinalizeInitialization() +{ + char* root_uuid = OBOS_GetOPTS("root-fs-uuid"); + char* root_partid = OBOS_GetOPTS("root-fs-partid"); + if (strcmp(root_partid ? root_partid : root_uuid, "initrd")) + goto end; // We needn't do anything. + OBOS_Debug("%s: Unimplemented.\n", __func__); + end: + if (root_partid) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, root_partid, strlen(root_partid)); + if (root_uuid) + OBOS_KernelAllocator->Free(OBOS_KernelAllocator, root_uuid, strlen(root_uuid)); +} \ No newline at end of file diff --git a/src/oboskrnl/vfs/init.h b/src/oboskrnl/vfs/init.h new file mode 100644 index 00000000..d1838e1c --- /dev/null +++ b/src/oboskrnl/vfs/init.h @@ -0,0 +1,17 @@ +/* + * oboskrnl/vfs/init.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +// Initializes the VFS. +// After this is called, the root is set to the InitRD. +void Vfs_Initialize(); +// Finalizes VFS initialization. +// To be called after fs drivers and disk drivers are done being loaded. +// This mainly mounts the root fs as was specified in the kernel cmd line. +void Vfs_FinalizeInitialization(); \ No newline at end of file diff --git a/src/oboskrnl/vfs/limits.h b/src/oboskrnl/vfs/limits.h new file mode 100644 index 00000000..ab2eb072 --- /dev/null +++ b/src/oboskrnl/vfs/limits.h @@ -0,0 +1,26 @@ +/* + * oboskrnl/vfs/limits.h + * + * Copyright (c) 2024 Omar Berrow + * + * Defines limits for the VFS. +*/ + +#pragma once + +#include + +#define MAX_FILENAME_LEN 256 /* In bytes */ + +typedef int64_t off_t; +typedef uint64_t uoff_t; +#define OFF_T_MIN INT64_MIN +#define OFF_T_MAX INT64_MAX +#define UOFF_T_MIN UINT64_MIN +#define UOFF_T_MAX UINT64_MAX +typedef enum whence_t +{ + SEEK_CUR, + SEEK_SET, + SEEK_END, +} whence_t; \ No newline at end of file diff --git a/src/oboskrnl/vfs/mount.c b/src/oboskrnl/vfs/mount.c new file mode 100644 index 00000000..cef50323 --- /dev/null +++ b/src/oboskrnl/vfs/mount.c @@ -0,0 +1,372 @@ +/* + * oboskrnl/vfs/mount.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include "locks/wait.h" +#include "scheduler/schedule.h" +#include "vfs/fd.h" +#include "vfs/pagecache.h" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +struct dirent* Vfs_Root; +mount_list Vfs_Mounted; + +typedef LIST_HEAD(symbolic_link_list, struct symbolic_link) symbolic_link_list; +typedef struct symbolic_link +{ + dirent* ent; + dev_desc desc; + LIST_NODE(symbolic_link_list, struct symbolic_link) node; +} symbolic_link; +LIST_GENERATE_STATIC(symbolic_link_list, struct symbolic_link, node); + +static size_t str_search(const char* str, char ch) +{ + size_t ret = strchr(str, ch); + for (; str[ret] == ch && str[ret] != 0; ret++) + ; + return ret; +} +OBOS_STATIC_ASSERT(sizeof(driver_file_perm) == sizeof(file_perm), "Invalid sizes!"); +static vnode* create_vnode(mount* mountpoint, dev_desc desc, file_type* t) +{ + file_type type = 0; + driver_file_perm perm = {}; + mountpoint->fs_driver->driver->header.ftable.get_file_perms(desc, &perm); + mountpoint->fs_driver->driver->header.ftable.get_file_type(desc, &type); + vnode* vn = Vfs_Calloc(1, sizeof(vnode)); + switch (type) + { + case FILE_TYPE_REGULAR_FILE: + vn->vtype = VNODE_TYPE_REG; + mountpoint->fs_driver->driver->header.ftable.get_max_blk_count(desc, &vn->filesize); + break; + case FILE_TYPE_DIRECTORY: + vn->vtype = VNODE_TYPE_DIR; + break; + case FILE_TYPE_SYMBOLIC_LINK: + { + // Defer the initialization of the vnode. + Vfs_Free(vn); + vn = nullptr; + break; + } + default: + OBOS_ASSERT(type); + } + vn->mount_point = mountpoint; + vn->desc = desc; + memcpy(&vn->perm, &perm, sizeof(file_perm)); + VfsH_PageCacheRef(&vn->pagecache); + if (t) + *t = type; + return vn; +} +static iterate_decision callback(dev_desc desc, size_t blkSize, size_t blkCount, void* userdata) +{ + uintptr_t *udata = (uintptr_t*)userdata; + mount* mountpoint = (mount*)udata[0]; + vdev* fs_driver = (vdev*)udata[1]; + vdev* device = (vdev*)udata[2]; + symbolic_link_list* symlinks = (symbolic_link_list*)udata[3]; + OBOS_UNUSED(blkSize); + OBOS_UNUSED(device); + file_type type = 0; + const char* path = nullptr; + OBOS_UNUSED(mountpoint); + OBOS_UNUSED(desc); + OBOS_UNUSED(blkCount); + OBOS_UNUSED(device); + fs_driver->driver->header.ftable.query_path(desc, &path); + fs_driver->driver->header.ftable.get_file_type(desc, &type); + size_t pathlen = strlen(path); + const char* tok = path; + bool is_base = false; + size_t tok_len = 0; + { + size_t currentPathLen = strlen(tok)-1; + if (tok[currentPathLen] != '/') + currentPathLen++; + while (tok[currentPathLen] == '/') + currentPathLen--; + tok_len = strchr(tok, '/'); + if (tok_len != currentPathLen) + tok_len--; + else + is_base = true; + } + dirent* last = mountpoint->root; + char* currentPath = Vfs_Calloc(pathlen + 1, sizeof(char)); + size_t currentPathLen = 0; + while (tok < (path+pathlen)) + { + char* token = Vfs_Calloc(tok_len + 1, sizeof(char)); + memcpy(token, tok, tok_len); + token[tok_len] = 0; + memcpy(currentPath + currentPathLen, token, tok_len); + currentPathLen += tok_len; + if (!is_base) + currentPath[currentPathLen++] = '/'; + dirent* new = VfsH_DirentLookupFrom(token, last ? last : mountpoint->root); + if (!new) + { + // Allocate a new dirent. + new = Vfs_Calloc(1, sizeof(dirent)); + OBOS_StringSetAllocator(&new->name, Vfs_Allocator); + OBOS_InitStringLen(&new->name, token, tok_len); + dev_desc curdesc = 0; + file_type curtype = 0; + if (is_base) + curdesc = desc; + else + fs_driver->driver->header.ftable.path_search(&curdesc, currentPath); + mountpoint->fs_driver->driver->header.ftable.get_file_type(desc, &type); + if (curtype == FILE_TYPE_SYMBOLIC_LINK) + { + symbolic_link* lnk = Vfs_Calloc(1, sizeof(symbolic_link)); + lnk->ent = new; + lnk->desc = desc; + LIST_APPEND(symbolic_link_list, symlinks, lnk); + } + else + { + vnode* new_vn = create_vnode(mountpoint, curdesc, &curtype); + new->vnode = new_vn; + new->vnode->refs++; + } + } + if (!new->d_prev_child && !new->d_next_child && last->d_children.head != new) + VfsH_DirentAppendChild(last ? last : mountpoint->root, new); + last = new; + Vfs_Free(token); + + tok += str_search(tok, '/'); + size_t currentPathLen = strlen(tok)-1; + if (tok[currentPathLen] != '/') + currentPathLen++; + while (tok[currentPathLen] == '/') + currentPathLen--; + tok_len = strchr(tok, '/'); + if (tok_len != currentPathLen) + tok_len--; + else + is_base = true; + } + if (type == FILE_TYPE_DIRECTORY) + fs_driver->driver->header.ftable.list_dir(desc, callback, udata); + return ITERATE_DECISION_CONTINUE; +} +obos_status Vfs_Mount(const char* at_, vdev* device, vdev* fs_driver, mount** pMountpoint) +{ + OBOS_UNUSED(device); + if (!Vfs_Root) + return OBOS_STATUS_INVALID_INIT_PHASE; + if (!at_ || !fs_driver) + return OBOS_STATUS_INVALID_ARGUMENT; + dirent* at = VfsH_DirentLookup(at_); + if (!at) + return OBOS_STATUS_NOT_FOUND; + if (at->vnode->vtype != VNODE_TYPE_DIR) + return OBOS_STATUS_INVALID_OPERATION; + if (at->vnode->flags & VFLAGS_MOUNTPOINT) + return OBOS_STATUS_ALREADY_MOUNTED; + if (at->d_children.nChildren) + return OBOS_STATUS_INVALID_ARGUMENT; + mount* mountpoint = Vfs_Calloc(1, sizeof(mount)); + if (pMountpoint) + *pMountpoint = mountpoint; + mountpoint->mounted_on = at->vnode; + at->vnode->un.mounted = mountpoint; + // at->vnode->mount_point = mountpoint; + at->vnode->flags |= VFLAGS_MOUNTPOINT; + mountpoint->root = at; + symbolic_link_list symlinks = {}; + uintptr_t udata[4] = { + (uintptr_t)mountpoint, + (uintptr_t)fs_driver, + (uintptr_t)device, + (uintptr_t)&symlinks, + }; + mountpoint->fs_driver = memcpy(Vfs_Calloc(1, sizeof(vdev)), fs_driver, sizeof(*fs_driver)); + if (device) + mountpoint->device = memcpy(Vfs_Calloc(1, sizeof(vdev)), device, sizeof(*device)); + fs_driver->driver->header.ftable.list_dir(UINTPTR_MAX, callback, udata); + for (symbolic_link* lnk = LIST_GET_HEAD(symbolic_link_list, &symlinks); lnk; ) + { + symbolic_link* next = LIST_GET_NEXT(symbolic_link_list, &symlinks, lnk); + if (lnk->ent->vnode) + { + // We have already been resolved. + lnk = next; + continue; + } + const char *points_at = nullptr; + dev_desc desc_points_at = 0; + fs_driver->driver->header.ftable.get_linked_desc(lnk->desc, &desc_points_at); + fs_driver->driver->header.ftable.query_path(desc_points_at, &points_at); + dirent* resolved = VfsH_DirentLookupFrom(points_at, mountpoint->root); + OBOS_ASSERT(resolved); // TODO: Proper error handling. + dev_desc desc = 0; + while (!resolved->vnode) + { + desc = desc_points_at; + // *resolved is probably also an unresolved symlink. + // resolve it. + points_at = nullptr; + desc_points_at = 0; + fs_driver->driver->header.ftable.get_linked_desc(desc, &desc_points_at); + fs_driver->driver->header.ftable.query_path(desc_points_at, &points_at); + resolved = VfsH_DirentLookupFrom(points_at, mountpoint->root); + OBOS_ASSERT(resolved); // TODO: Proper error handling. + } + lnk->ent->vnode = resolved->vnode; + lnk->ent->vnode->refs++; + VfsH_PageCacheRef(&lnk->ent->vnode->pagecache); + Vfs_Free(lnk); // free the temporary structure. + lnk = next; + } + LIST_APPEND(mount_list, &Vfs_Mounted, mountpoint); + return OBOS_STATUS_SUCCESS; +} +bool VfsH_LockMountpoint(mount* point) +{ + point->nWaiting++; + obos_status status = Core_MutexAcquire(&point->lock); + if (obos_is_error(status) && status != OBOS_STATUS_ABORTED) + return false; + if (status == OBOS_STATUS_ABORTED) + { + if (point->awaitingFree && !(--point->nWaiting)) + Vfs_Free(point); + return false; + } + point->nWaiting--; + return true; +} +bool VfsH_UnlockMountpoint(mount* point) +{ + return obos_is_success(Core_MutexRelease(&point->lock)); +} +static void deref_vnode(vnode* vn) +{ + if (!(--vn->refs)) + { + if (vn->pagecache.dirty_regions.nNodes) + OBOS_Warning("Freeing a vnode before dirty regions are freed. All cached writes will be dropped.\n"); + VfsH_PageCacheResize(&vn->pagecache, vn, 0); + if (vn->vtype == VNODE_TYPE_CHR || vn->vtype == VNODE_TYPE_BLK) + if (!(vn->un.device->refs--)) + Vfs_Free(vn->un.device); + Vfs_Free(vn); + } +} +static void close_fd(fd* desc) +{ + desc->flags &= ~FD_FLAGS_OPEN; + LIST_REMOVE(fd_list, &desc->vn->opened, desc); + deref_vnode(desc->vn); + +} +static void foreach_dirent(mount *what, void(*cb)(mount* what, dirent* ent, void* userdata), void* userdata) +{ + for (dirent* curr = LIST_GET_HEAD(dirent_list, &what->dirent_list); curr; ) + { + dirent* next = LIST_GET_NEXT(dirent_list, &what->dirent_list, curr); + cb(what, curr, userdata); + curr = next; + } +} +// Closes FDs and waits for pending async IO operations to finish. +static void stage_one(mount* unused, dirent* ent, void* userdata) +{ + OBOS_UNUSED(unused); + OBOS_UNUSED(userdata); + for (fd* curr = LIST_GET_HEAD(fd_list, &ent->vnode->opened); curr; ) + { + fd* next = LIST_GET_NEXT(fd_list, &ent->vnode->opened, curr); + close_fd(curr); + curr = next; + } + // TODO: Use a proper synchronization primitive to deal with this. + while (ent->vnode->nPendingAsyncIO) + Core_Yield(); +} +static void stage_two(mount* unused, dirent* ent, void* userdata) +{ + OBOS_UNUSED(unused); + OBOS_UNUSED(userdata); + VfsH_PageCacheFlush(&ent->vnode->pagecache, ent->vnode); + deref_vnode(ent->vnode); + OBOS_FreeString(&ent->name); + Vfs_Free(ent); +} +obos_status Vfs_Unmount(mount* what) +{ + if (!what) + return OBOS_STATUS_INVALID_ARGUMENT; + Core_MutexAcquire(&what->lock); + what->mounted_on->un.mounted = nullptr; + what->mounted_on->flags &= ~VFLAGS_MOUNTPOINT; + foreach_dirent(what, stage_one, nullptr); + namecache_ent* curr; + for (curr = RB_MIN(namecache, &what->nc); curr;) + { + namecache_ent* next = RB_RIGHT(curr, rb_cache); + deref_vnode(curr->ref); + OBOS_FreeString(&curr->path); + Vfs_Free(curr); + curr = next; + } + what->root->d_children.head = nullptr; + what->root->d_children.tail = nullptr; + what->root->d_children.nChildren = 0; + foreach_dirent(what, stage_two, nullptr); + LIST_REMOVE(mount_list, &Vfs_Mounted, what); + if (what->root == Vfs_Root) + { + Vfs_Root->vnode->mount_point = nullptr; + Vfs_Root->vnode->un.mounted = nullptr; + } + what->awaitingFree = true; + if (!what->nWaiting) + Vfs_Free(what); + else // the last thread to be waken up will free the mount point. + { + what->lock.ignoreAllAndBlowUp = true; + CoreH_SignalWaitingThreads(WAITABLE_OBJECT(what->lock), true, false); + } + return OBOS_STATUS_SUCCESS; +} +obos_status Vfs_UnmountP(const char* at) +{ + dirent* resolved = VfsH_DirentLookup(at); + if (!resolved) + return OBOS_STATUS_NOT_FOUND; + if (resolved->vnode->flags & VFLAGS_MOUNTPOINT) + return OBOS_STATUS_INVALID_ARGUMENT; + return Vfs_Unmount(resolved->vnode->un.mounted); +} + +RB_GENERATE(namecache, namecache_ent, rb_cache, cmp_namecache_ent); +LIST_GENERATE(mount_list, mount, node); diff --git a/src/oboskrnl/vfs/mount.h b/src/oboskrnl/vfs/mount.h new file mode 100644 index 00000000..9eb69831 --- /dev/null +++ b/src/oboskrnl/vfs/mount.h @@ -0,0 +1,45 @@ +/* + * oboskrnl/vfs/mount.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +#include + +typedef LIST_HEAD(mount_list, struct mount) mount_list; +LIST_PROTOTYPE(mount_list, struct mount, node); +typedef struct mount +{ + LIST_NODE(mount_list, struct mount) node; + mutex lock; + dirent* root; + vdev* fs_driver; + vdev* device; // the block device the filesystem is situated on. + vnode* mounted_on; + namecache nc; + dirent_list dirent_list; + atomic_size_t nWaiting; + bool awaitingFree; +} mount; +extern struct dirent* Vfs_Root; +extern mount_list Vfs_Mounted; + +// returns true if the operation succeeded. +bool VfsH_LockMountpoint(mount* point); +// returns true if the operation succeeded. +bool VfsH_UnlockMountpoint(mount* point); + +obos_status Vfs_Mount(const char* at, vdev* device, vdev* fs_driver, mount** mountpoint); +obos_status Vfs_Unmount(mount* what); +obos_status Vfs_UnmountP(const char* at); \ No newline at end of file diff --git a/src/oboskrnl/vfs/namecache.h b/src/oboskrnl/vfs/namecache.h new file mode 100644 index 00000000..622c60ce --- /dev/null +++ b/src/oboskrnl/vfs/namecache.h @@ -0,0 +1,34 @@ +/* + * oboskrnl/vfs/namecache.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +#include +#include +#include + +#include + +typedef RB_HEAD(namecache, namecache_ent) namecache; +typedef LIST_HEAD(namecache_list, struct namecache_ent) namecache_list; + +typedef struct namecache_ent +{ + RB_ENTRY(namecache_ent) rb_cache; + LIST_NODE(namecache_list, struct namecache_ent) list_node; + struct vnode* ref; + struct dirent* ent; + string path; // path relative to the mount point root. +} namecache_ent; + +inline static int cmp_namecache_ent(const struct namecache_ent* a, const struct namecache_ent* b) +{ + return uacpi_strcmp(OBOS_GetStringCPtr(&a->path), OBOS_GetStringCPtr(&b->path)); +} +RB_PROTOTYPE(namecache, namecache_ent, rb_cache, cmp_namecache_ent); +LIST_PROTOTYPE(namecache_list, struct namecache_ent, list_node); \ No newline at end of file diff --git a/src/oboskrnl/vfs/pagecache.c b/src/oboskrnl/vfs/pagecache.c new file mode 100644 index 00000000..3ab28b8a --- /dev/null +++ b/src/oboskrnl/vfs/pagecache.c @@ -0,0 +1,160 @@ +/* + * oboskrnl/vfs/pagecache.c + * + * Copyright (c) 2024 Omar Berrow +*/ + +#include "mm/bare_map.h" +#include "mm/pmm.h" +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +LIST_GENERATE(dirty_pc_list, struct pagecache_dirty_region, node); +LIST_GENERATE(mapped_region_list, struct pagecache_mapped_region, node); +pagecache_dirty_region* VfsH_PCDirtyRegionLookup(pagecache* pc, size_t off) +{ + Core_MutexAcquire(&pc->dirty_list_lock); + for (pagecache_dirty_region* curr = LIST_GET_HEAD(dirty_pc_list, &pc->dirty_regions); curr; ) + { + if (off >= curr->fileoff && off < (curr->fileoff + curr->sz)) + { + Core_MutexRelease(&pc->dirty_list_lock); + return curr; + } + + curr = LIST_GET_NEXT(dirty_pc_list, &pc->dirty_regions, curr); + } + Core_MutexRelease(&pc->dirty_list_lock); + return nullptr; +} +// Note! +// Does a lookup first, and if there is already a dirty region that can fit the contraints passed, it is used. +// If one contains the offset, but is too small, it is expanded. +// Otherwise, a new region is made. +// This returns the dirty region created. +pagecache_dirty_region* VfsH_PCDirtyRegionCreate(pagecache* pc, size_t off, size_t sz) +{ + OBOS_ASSERT(!(off >= pc->sz || (off+sz) >= pc->sz)); + if (off >= pc->sz || (off+sz) >= pc->sz) + return nullptr; // impossible for this to happen in normal cases. + pagecache_dirty_region* dirty = VfsH_PCDirtyRegionLookup(pc, off); + if (dirty) + { + if ((dirty->fileoff + off + sz) <= (dirty->fileoff + dirty->sz)) + return dirty; // we have space in this dirty region, return it + // not enough space, expand the region. + size_t new_cap = (dirty->fileoff + off + sz); + // TODO(oberrow): Does this need to be synchronized + dirty->sz = new_cap; + return dirty; + } + dirty = Vfs_Calloc(1, sizeof(pagecache_dirty_region)); + dirty->fileoff = off; + dirty->sz = sz; + dirty->owner = pc; + Core_MutexAcquire(&pc->dirty_list_lock); + LIST_APPEND(dirty_pc_list, &pc->dirty_regions, dirty); + Core_MutexRelease(&pc->dirty_list_lock); + return dirty; +} +void VfsH_PageCacheRef(pagecache* pc) +{ + pc->refcnt++; +} +void VfsH_PageCacheUnref(pagecache* pc) +{ + pc->refcnt--; + if (!pc->refcnt) + { + Vfs_Free(pc->data); + pc->data = nullptr; + pc->sz = 0; + } +} +void VfsH_PageCacheFlush(pagecache* pc, void* vn_) +{ + vnode* vn = (vnode*)vn_; + OBOS_ASSERT(vn); + OBOS_ASSERT(&vn->pagecache == pc); + Core_MutexAcquire(&pc->dirty_list_lock); + mount* const point = vn->mount_point ? vn->mount_point : vn->un.mounted; + driver_id* driver = point->fs_driver->driver; + for (pagecache_dirty_region* curr = LIST_GET_HEAD(dirty_pc_list, &pc->dirty_regions); curr; ) + { + pagecache_dirty_region* next = LIST_GET_NEXT(dirty_pc_list, &pc->dirty_regions, curr); + driver->header.ftable.write_sync(vn->desc, pc->data + curr->fileoff, curr->sz, curr->fileoff, nullptr); + curr = next; + } + Core_MutexRelease(&pc->dirty_list_lock); +} +void VfsH_PageCacheResize(pagecache* pc, void* vn_, size_t newSize) +{ + vnode* vn = (vnode*)vn_; + OBOS_ASSERT(newSize <= vn->filesize); + if (newSize == pc->sz) + return; + size_t filesize = newSize; + if (newSize % OBOS_PAGE_SIZE) + newSize = newSize + (OBOS_PAGE_SIZE-(newSize%OBOS_PAGE_SIZE)); + Core_MutexAcquire(&pc->lock); + size_t oldSz = pc->sz; + // pc->sz = newSize; + void* oldData = pc->data; + pc->data = Mm_VirtualMemoryAlloc(&Mm_KernelContext, nullptr, newSize, 0, VMA_FLAGS_NON_PAGED, nullptr, nullptr); + if (oldData && newSize) + { + memcpy(pc->data, oldData, oldSz > newSize ? oldSz - newSize : oldSz); + Mm_VirtualMemoryFree(&Mm_KernelContext, oldData, oldSz); + } + page what = {}; + for (pagecache_mapped_region* curr = LIST_GET_HEAD(mapped_region_list, &pc->mapped_regions); curr && oldData; ) + { + for (uintptr_t addr = curr->addr; addr < (curr->addr + curr->sz); addr += OBOS_PAGE_SIZE) + { + what.addr = addr; + page* const pg = RB_FIND(page_tree, &curr->ctx->pages, &what); + if (pg->isPrivateMapping) + { + curr = LIST_GET_NEXT(mapped_region_list, &pc->mapped_regions, curr); + break; + } + OBOS_ASSERT(pg->region == curr); + uintptr_t phys = 0; + OBOSS_GetPagePhysicalAddress((void*)oldData + curr->fileoff, &phys); + if (curr->fileoff >= pc->sz) + { + if (vn->filesize >= curr->fileoff) + phys = Mm_AllocatePhysicalPages(1, 1, nullptr); // TODO: Do some sort of CoW? + else + { + pg->prot.present = false; + phys = 0; + } + } + MmS_SetPageMapping(curr->ctx->pt, pg, phys); + } + + curr = LIST_GET_NEXT(mapped_region_list, &pc->mapped_regions, curr); + } + pc->sz = filesize; + if (filesize > oldSz) + vn->mount_point->fs_driver->driver->header.ftable.read_sync(vn->desc, pc->data, filesize-oldSz, oldSz, nullptr); + Core_MutexRelease(&pc->lock); +} \ No newline at end of file diff --git a/src/oboskrnl/vfs/pagecache.h b/src/oboskrnl/vfs/pagecache.h new file mode 100644 index 00000000..0b2f0bc5 --- /dev/null +++ b/src/oboskrnl/vfs/pagecache.h @@ -0,0 +1,70 @@ +/* + * oboskrnl/vfs/pagecache.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +#include + +#include + +#include + +typedef LIST_HEAD(dirty_pc_list, struct pagecache_dirty_region) dirty_pc_list; +LIST_PROTOTYPE(dirty_pc_list, struct pagecache_dirty_region, node); +typedef LIST_HEAD(mapped_region_list, struct pagecache_mapped_region) mapped_region_list; +LIST_PROTOTYPE(mapped_region_list, struct pagecache_mapped_region, node); +typedef struct pagecache +{ + // Take this lock when expanding the page cache. + mutex lock; + char* data; + size_t sz; + // Take this lock when using dirty region list. + mutex dirty_list_lock; + dirty_pc_list dirty_regions; + atomic_size_t refcnt; + mapped_region_list mapped_regions; +} pagecache; +typedef struct pagecache_dirty_region +{ + // take this lock when any reads or writes are done on the region this represents. + // do not take it if expanding 'sz' + mutex lock; + size_t fileoff; + atomic_size_t sz; + pagecache* owner; + LIST_NODE(dirty_pc_list, struct pagecache_dirty_region) node; +} pagecache_dirty_region; +typedef struct pagecache_mapped_region +{ + mutex lock; + size_t fileoff; + uintptr_t addr; + atomic_size_t sz; + pagecache* owner; + struct context* ctx; + LIST_NODE(mapped_region_list, struct pagecache_mapped_region) node; +} pagecache_mapped_region; +pagecache_dirty_region* VfsH_PCDirtyRegionLookup(pagecache* pc, size_t off); +// Note! +// Does a lookup first, and if there is already a dirty region that can fit the contraints passed, it is used. +// If one contains the offset, but is too small, it is expanded. +// Otherwise, a new region is made. +// This returns the dirty region created. +pagecache_dirty_region* VfsH_PCDirtyRegionCreate(pagecache* pc, size_t off, size_t sz); +// Adds a reference to the page cache +void VfsH_PageCacheRef(pagecache* pc); +// Removes a reference from the page cache. +// If pc->ref reaches zero, the page cache is freed. +void VfsH_PageCacheUnref(pagecache* pc); +// Flushes the page cache. +// vn is of type `vnode*` +void VfsH_PageCacheFlush(pagecache* pc, void* vn); +// Resizes the page cache. +// vn is of type `vnode*` +void VfsH_PageCacheResize(pagecache* pc, void* vn, size_t newSize); \ No newline at end of file diff --git a/src/oboskrnl/vfs/vnode.h b/src/oboskrnl/vfs/vnode.h new file mode 100644 index 00000000..47869a22 --- /dev/null +++ b/src/oboskrnl/vfs/vnode.h @@ -0,0 +1,104 @@ +/* + * oboskrnl/vfs/vnode.h + * + * Copyright (c) 2024 Omar Berrow +*/ + +#pragma once + +#include + +#include +#include + +#include +#include + +#include + +#include + +enum +{ + // This vnode has no type. + VNODE_TYPE_NON, + // This vnode represents a regular file. + VNODE_TYPE_REG, + // This vnode represents a directory. + VNODE_TYPE_DIR, + // This vnode represents a block device. + VNODE_TYPE_BLK, + // This vnode represents a character device. + VNODE_TYPE_CHR, + // This vnode represents a symbolic link. + VNODE_TYPE_LNK, + // This vnode represents a socket. + VNODE_TYPE_SOCK, + // This vnode represents a named pipe. + VNODE_TYPE_FIFO, + // This vnode represents a bad or dead file. + VNODE_TYPE_BAD +}; +enum +{ + VFLAGS_MOUNTPOINT = 1, + VFLAGS_IS_TTY = 2, +}; + +// basically a struct specinfo, but renamed. +typedef struct vdev +{ + dev_desc desc; + driver_id* driver; + void* data; + size_t refs; +} vdev; +typedef struct file_perm +{ + bool other_exec : 1; + bool other_write : 1; + bool other_read : 1; + bool group_exec : 1; + bool group_write : 1; + bool group_read : 1; + bool owner_exec : 1; + bool owner_write : 1; + bool owner_read : 1; +} OBOS_PACK file_perm; +typedef struct vnode +{ + void* data; + uint32_t vtype; + uint32_t flags; + struct mount* mount_point; + union { + struct mount* mounted; + vdev* device; + // TODO: Add more stuff, such as pipes, sockets, etc. + } un; + size_t refs; + atomic_size_t nPendingAsyncIO; + file_perm perm; + pagecache pagecache; + size_t filesize; // filesize. + uid owner_uid; // the owner's UID. + gid group_uid; // the group's GID. + dev_desc desc; // the cached device descriptor. + fd_list opened; +} vnode; +struct async_irp +{ + // This event object is set the operation is finished. + event* e; + union { + const void* cbuf; + void* buf; + } un; + size_t requestSize; + thread* worker; + bool rw : 1; // if false, the operation is a read, otherwise it is a write. + bool cached : 1; + uoff_t fileoff; + vnode* vn; +}; +OBOS_EXPORT vnode* Drv_AllocateVNode(driver_id* drv, dev_desc desc, size_t filesize, vdev** dev, uint32_t type); \ No newline at end of file diff --git a/src/uACPI/CMakeLists.txt b/src/uACPI/CMakeLists.txt index 1eb547ee..aaaf2218 100644 --- a/src/uACPI/CMakeLists.txt +++ b/src/uACPI/CMakeLists.txt @@ -37,6 +37,7 @@ target_compile_definitions(uacpi PUBLIC UACPI_OVERRIDE_ARCH_HELPERS=1 PUBLIC UACPI_FORMATTED_LOGGING=1 PUBLIC OBOS_KERNEL=1 + PUBLIC IS_UACPI_BUILD=1 PRIVATE OBOS_ARCHITECTURE_HAS_ACPI=${OBOS_ARCHITECTURE_HAS_ACPI} ) target_link_options(uacpi diff --git a/src/uACPI/kernel_api.c b/src/uACPI/kernel_api.c index b27fca89..e9100f59 100644 --- a/src/uACPI/kernel_api.c +++ b/src/uACPI/kernel_api.c @@ -4,6 +4,7 @@ Copyright (c) 2024 Omar Berrow */ +#include "locks/mutex.h" #include #include #include @@ -418,15 +419,9 @@ uacpi_status uacpi_kernel_io_write( return UACPI_STATUS_INVALID_ARGUMENT; return uacpi_kernel_raw_io_write(rng->base + offset, byte_width, value); } -typedef struct mutex -{ - atomic_flag locked; - // If not nullptr, locked. - thread* owner; -} mutex; uacpi_handle uacpi_kernel_create_mutex(void) { - return OBOS_KernelAllocator->Allocate(OBOS_KernelAllocator, sizeof(mutex), nullptr); + return OBOS_KernelAllocator->ZeroAllocate(OBOS_KernelAllocator, 1, sizeof(mutex), nullptr); } void uacpi_kernel_free_mutex(uacpi_handle hnd) { @@ -434,31 +429,15 @@ void uacpi_kernel_free_mutex(uacpi_handle hnd) } uacpi_bool uacpi_kernel_acquire_mutex(uacpi_handle hnd, uacpi_u16 t) { + OBOS_UNUSED(t); mutex *mut = (mutex*)hnd; - uint64_t wakeTime = 0; - if (t != 0xffff) - wakeTime = CoreS_GetTimerTick() + t * 4; - else - wakeTime = 0xffffffffffffffff; - size_t spin = 0; - while (atomic_flag_test_and_set_explicit(&mut->locked, memory_order_seq_cst) && CoreS_GetTimerTick() < wakeTime) - { - if (spin++ == 10000) - spin_hung(); - spinlock_hint(); - } - mut->owner = Core_GetCurrentThread(); + Core_MutexAcquire(mut); return UACPI_TRUE; } void uacpi_kernel_release_mutex(uacpi_handle hnd) { mutex *mut = (mutex*)hnd; - if (mut->owner != Core_GetCurrentThread()) - { - OBOS_Debug("Failed release of mutex %p. Owner != currentThread\n", hnd); - return; - } - atomic_flag_clear_explicit(&mut->locked, memory_order_seq_cst); + Core_MutexRelease(mut); } uacpi_thread_id uacpi_kernel_get_thread_id() { diff --git a/src/uACPI/uacpi_libc.h b/src/uACPI/uacpi_libc.h index ba51a0f6..c0d79439 100644 --- a/src/uACPI/uacpi_libc.h +++ b/src/uACPI/uacpi_libc.h @@ -19,20 +19,18 @@ // #define uacpi_offsetof(t, m) ((uintptr_t)(&((t*)0)->m)) -#define NULL ((void*)0) - #ifdef __cplusplus extern "C" { #endif - void *uacpi_memcpy(void *dest, const void* src, size_t sz); - void *uacpi_memset(void *dest, int src, size_t cnt); - int uacpi_memcmp(const void *src1, const void *src2, size_t cnt); - int uacpi_strncmp(const char *src1, const char *src2, size_t maxcnt); - int uacpi_strcmp(const char *src1, const char *src2); - void *uacpi_memmove(void *dest, const void* src, size_t sz); - size_t uacpi_strnlen(const char *src, size_t maxcnt); - size_t uacpi_strlen(const char *src); - int uacpi_snprintf(char* dest, size_t n, const char* format, ...); + OBOS_EXPORT void *uacpi_memcpy(void *dest, const void* src, size_t sz); + OBOS_EXPORT void *uacpi_memset(void *dest, int src, size_t cnt); + OBOS_EXPORT int uacpi_memcmp(const void *src1, const void *src2, size_t cnt); + OBOS_EXPORT int uacpi_strncmp(const char *src1, const char *src2, size_t maxcnt); + OBOS_EXPORT int uacpi_strcmp(const char *src1, const char *src2); + OBOS_EXPORT void *uacpi_memmove(void *dest, const void* src, size_t sz); + OBOS_EXPORT size_t uacpi_strnlen(const char *src, size_t maxcnt); + OBOS_EXPORT size_t uacpi_strlen(const char *src); + OBOS_EXPORT int uacpi_snprintf(char* dest, size_t n, const char* format, ...); #ifdef __cplusplus } #endif