From df3ff681d04a8b2eb741c01fae54798f9bbb9586 Mon Sep 17 00:00:00 2001 From: shibedrill Date: Fri, 16 May 2025 00:13:15 -0400 Subject: [PATCH] Port IO support, microkernel arch stuff --- ARCHITECTURE.md | 56 ++++++++++++++++++++++++++++++ Makefile.toml | 57 +++++++++++++++++++++++++----- limine.conf | 1 + server_config.toml | 13 +++++++ src/lib/arch/x86_64/mod.rs | 1 + src/lib/arch/x86_64/ports.rs | 67 ++++++++++++++++++++++++++++++++++++ 6 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 ARCHITECTURE.md create mode 100644 server_config.toml create mode 100644 src/lib/arch/x86_64/ports.rs diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..b756497 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,56 @@ +# Architecture Outline + +Gila is a microkernel, and almost all functionality of the OS is relegated to +"server" processes. A server is a process that provides a specific +functionality, using a kernel-provided interface, and it is given a "seat" by +the kernel which processes may query to reference it and invoke its functions. + +# Boot Process + +Gila initializes as a bare kernel, with the bootloader providing an init RAM +filesystem in the form of a .tar.lzma archive. The kernel reads this file, and +launches an init process (`/system/bin/init`). The init process has its own +configuration file located at `/system/cfg/init.toml`, which should detail the +steps needed to bring the system up to a multi-user status. + +If the init system needs to access a filesystem, it must first get the handle +of the filesystem server. If the filesystem server is not running when this +handle request is made, the kernel will launch the server before returning its +handle. From there, the filesystem server will request the handle of the disk +driver that corresponds to the requested filesystem. The kernel then launches +the disk driver server, and assigns it a seat based on the device it drives, +granting it access to the memory region responsible for that device. + +The benefit of this approach is threefold: + +- The system does not need to include a filesystem OR disk driver, if neither + the disk nor the filesystem are read or written. +- The driver or filesystem server can crash, and the whole stack can recover. +- The user or developer can trivially introduce new drivers without a reboot. + This goes for filesystem drivers AND disk device drivers. + +The system, hence, can be configured in two ways: + +- The drivers can all be included in the initramfs for diskless operation. +- The bare minimum drivers needed for disk access are included in the + initramfs, and all other drivers are included in the root filesystem. + +# APIs + +Processes access services similarly to the way the init process accesses +data from the file system. The process requests the ID of the server that +performs the function, and then communicates with it via IPC and shared +memory regions. APIs are to be defined in the standard library. + +# Device Drivers + +Device driver assignment is performed like so: + +- The kernel obtains necessary hardware topology info from ACPI or the DTB. +- For each device, the kernel spawns a driver process based on a compatibility + list that details which drivers work for which devices. +- The driver process, when it initializes, requests information from the + kernel about the device. The kernel maps relevant regions of physical memory + into the process's memory space, and returns information on the new mappings + to the process. +- The driver then makes its functions available via IPC and shared memory. \ No newline at end of file diff --git a/Makefile.toml b/Makefile.toml index 764843d..4c6d8a6 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -18,44 +18,83 @@ env_scripts = [ ] [env] +# Directory for Limine binaries LIMINEDIR = "/usr/share/limine" +# Compile target TARGET = "x86_64-unknown-none" +# Binaries included in the OS initramfs +BINS = [] [env.development] PROFILE = "dev" TARGETDIR = "debug" +ARTIFACTDIR = "target/${TARGET}/${TARGETDIR}" [env.release] PROFILE = "release" TARGETDIR = "release" +ARTIFACTDIR = "target/${TARGET}/${TARGETDIR}" [tasks.default] alias = "run" + + + + [tasks.clean] -dependencies = ["cleaniso"] +dependencies = ["clean_iso", "clean_initramfs"] command = "cargo" args = ["clean"] [tasks.prepare] script = "rustup install nightly; rustup target add ${TARGET}" -[tasks.cleaniso] +[tasks.clean_iso] command = "rm" -args = ["-rf", "build"] +args = ["-rf", "build/iso", "build/gila.iso"] + +[tasks.clean_initramfs] +command = "rm" +args = ["-rf", "build/initramfs", "build/initramfs.tar.lzma"] [tasks.lib] -condition = { files_modified = { input = ["./src/lib/**/*.rs"], output = ["./target/${TARGET}/${TARGETDIR}/libgila.rlib"] }, fail_message = "(inputs unchanged)"} +condition = { files_modified = { input = ["src/lib/**/*.rs"], output = ["${ARTIFACTDIR}/libgila.rlib"] }, fail_message = "(inputs unchanged)" } dependencies = ["prepare"] command = "cargo" args = ["build", "--lib"] [tasks.kernel] -condition = { files_modified = { input = ["./src/kernel/**/*.rs", "./src/kernel/**/*.ld", "./target/${TARGET}/${TARGETDIR}/libgila.rlib"], output = ["./target/${TARGET}/${TARGETDIR}/kernel"] }, fail_message = "(inputs unchanged)"} +condition = { files_modified = { input = [ + "src/kernel/**/*.rs", + "src/kernel/**/*.ld", + "${ARTIFACTDIR}/libgila.rlib" +], output = ["${ARTIFACTDIR}/kernel"] }, fail_message = "(inputs unchanged)" } dependencies = ["lib"] command = "cargo" args = ["build", "--profile", "${PROFILE}", "--bin", "kernel"] +[tasks.bins] +dependencies = [] + +[tasks.initramfs] +condition = { files_modified = { input = [ + +], output = ["build/initramfs.tar.lzma"] }, fail_message = "(inputs unchanged)" } +dependencies = ["bins"] +script = ''' + mkdir -p build/initramfs/system/bin + mkdir -p build/initramfs/system/cfg + + for bin in $(echo ${BINS} | tr ";" " ") + do + cp ${ARTIFACTDIR}/$bin build/initramfs/system/bin/ + done + cp server_config.toml build/initramfs/system/cfg/ + + tar -c --lzma -f build/initramfs.tar.lzma build/initramfs +''' + [tasks.iso] condition = { files_modified = { input = [ "./target/${TARGET}/${TARGETDIR}/kernel", @@ -65,9 +104,8 @@ condition = { files_modified = { input = [ "${LIMINEDIR}/limine-uefi-cd.bin", "${LIMINEDIR}/${LIMINEBIN}" ], output = ["./build/gila.iso"] }, fail_message = "(inputs unchanged)" } -dependencies = ["kernel"] +dependencies = ["kernel", "initramfs"] script = ''' - #!@shell mkdir -p build/iso/boot/limine mkdir -p build/iso/EFI/BOOT @@ -76,8 +114,9 @@ script = ''' cp -f ${LIMINEDIR}/limine-bios-cd.bin build/iso/boot/limine/ cp -f ${LIMINEDIR}/limine-uefi-cd.bin build/iso/boot/limine/ cp -f ${LIMINEDIR}/${LIMINEBIN} build/iso/EFI/BOOT + cp -f ./build/initramfs.tar.lzma build/iso/boot/ - cp -f target/${TARGET}/${TARGETDIR}/kernel build/iso/boot/ + cp -f ${ARTIFACTDIR}/kernel build/iso/boot/ mkdir -p build/iso/system/bin mkdir -p build/iso/system/tmp @@ -100,4 +139,4 @@ script = ''' [tasks.run] dependencies = ["iso"] command = "${QEMUCOMMAND}" -args = ["-drive", "file=build/gila.iso,format=raw,index=0,media=disk"] \ No newline at end of file +args = ["-drive", "file=build/gila.iso,format=raw,index=0,media=disk", "-serial", "stdio"] \ No newline at end of file diff --git a/limine.conf b/limine.conf index d494fa6..2dbcbb2 100644 --- a/limine.conf +++ b/limine.conf @@ -4,3 +4,4 @@ timeout: 5 protocol: limine kernel_path: boot():/boot/kernel cmdline: -loglevel=INFO -logdev=display,serial + module_path: boot():/boot/initramfs.tar.lzma diff --git a/server_config.toml b/server_config.toml new file mode 100644 index 0000000..e3d3315 --- /dev/null +++ b/server_config.toml @@ -0,0 +1,13 @@ +# The configuration file detailing which system servers +# are responsible for what responsibilities. + +# This configuration file details several tables. Each table +# is a category of server the OS will launch to provide +# userspace functionality. Each key in the table is a specific +# functionality. + +[filesystems] + +[devices] + +[services] \ No newline at end of file diff --git a/src/lib/arch/x86_64/mod.rs b/src/lib/arch/x86_64/mod.rs index 676936f..1715cbf 100644 --- a/src/lib/arch/x86_64/mod.rs +++ b/src/lib/arch/x86_64/mod.rs @@ -1,5 +1,6 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later +mod ports; mod registers_impl; mod syscall_impl; diff --git a/src/lib/arch/x86_64/ports.rs b/src/lib/arch/x86_64/ports.rs new file mode 100644 index 0000000..26554e7 --- /dev/null +++ b/src/lib/arch/x86_64/ports.rs @@ -0,0 +1,67 @@ +use core::{arch::asm, num::TryFromIntError}; + +pub unsafe fn port_read_u8(port: u16) -> u8 { + let result: u8; + unsafe { + asm! { + "in al dx", + in("dx") port, + out("al") result + } + } + result +} + +pub unsafe fn port_read_u16(port: u16) -> u16 { + let result: u16; + unsafe { + asm! { + "in ax dx", + in("dx") port, + out("ax") result + } + } + result +} + +pub unsafe fn port_read_u32(port: u16) -> u32 { + let result: u32; + unsafe { + asm! { + "in eax dx", + in("dx") port, + out("eax") result + } + } + result +} + +pub unsafe fn port_write_u8(port: u16, data: u8) { + unsafe { + asm! { + "out dx al", + in("dx") port, + in("al") data, + } + } +} + +pub unsafe fn port_write_u16(port: u16, data: u16) { + unsafe { + asm! { + "out dx ax", + in("dx") port, + in("ax") data, + } + } +} + +pub unsafe fn port_write_u32(port: u16, data: u32) { + unsafe { + asm! { + "out dx eax", + in("dx") port, + in("eax") data, + } + } +}