Port IO support, microkernel arch stuff

This commit is contained in:
River 2025-05-16 00:13:15 -04:00
parent 204c8e24ae
commit df3ff681d0
Signed by: shibedrill
GPG Key ID: 5FE0CB25945EFAA2
6 changed files with 186 additions and 9 deletions

56
ARCHITECTURE.md Normal file
View File

@ -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.

View File

@ -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"]
args = ["-drive", "file=build/gila.iso,format=raw,index=0,media=disk", "-serial", "stdio"]

View File

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

13
server_config.toml Normal file
View File

@ -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]

View File

@ -1,5 +1,6 @@
// Copyright (c) 2025 shibedrill
// SPDX-License-Identifier: GPL-3.0-or-later
mod ports;
mod registers_impl;
mod syscall_impl;

View File

@ -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,
}
}
}