Port IO support, microkernel arch stuff
This commit is contained in:
parent
204c8e24ae
commit
df3ff681d0
56
ARCHITECTURE.md
Normal file
56
ARCHITECTURE.md
Normal 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.
|
@ -18,44 +18,83 @@ env_scripts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
# Directory for Limine binaries
|
||||||
LIMINEDIR = "/usr/share/limine"
|
LIMINEDIR = "/usr/share/limine"
|
||||||
|
# Compile target
|
||||||
TARGET = "x86_64-unknown-none"
|
TARGET = "x86_64-unknown-none"
|
||||||
|
# Binaries included in the OS initramfs
|
||||||
|
BINS = []
|
||||||
|
|
||||||
[env.development]
|
[env.development]
|
||||||
PROFILE = "dev"
|
PROFILE = "dev"
|
||||||
TARGETDIR = "debug"
|
TARGETDIR = "debug"
|
||||||
|
ARTIFACTDIR = "target/${TARGET}/${TARGETDIR}"
|
||||||
|
|
||||||
[env.release]
|
[env.release]
|
||||||
PROFILE = "release"
|
PROFILE = "release"
|
||||||
TARGETDIR = "release"
|
TARGETDIR = "release"
|
||||||
|
ARTIFACTDIR = "target/${TARGET}/${TARGETDIR}"
|
||||||
|
|
||||||
[tasks.default]
|
[tasks.default]
|
||||||
alias = "run"
|
alias = "run"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[tasks.clean]
|
[tasks.clean]
|
||||||
dependencies = ["cleaniso"]
|
dependencies = ["clean_iso", "clean_initramfs"]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["clean"]
|
args = ["clean"]
|
||||||
|
|
||||||
[tasks.prepare]
|
[tasks.prepare]
|
||||||
script = "rustup install nightly; rustup target add ${TARGET}"
|
script = "rustup install nightly; rustup target add ${TARGET}"
|
||||||
|
|
||||||
[tasks.cleaniso]
|
[tasks.clean_iso]
|
||||||
command = "rm"
|
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]
|
[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"]
|
dependencies = ["prepare"]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "--lib"]
|
args = ["build", "--lib"]
|
||||||
|
|
||||||
[tasks.kernel]
|
[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"]
|
dependencies = ["lib"]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build", "--profile", "${PROFILE}", "--bin", "kernel"]
|
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]
|
[tasks.iso]
|
||||||
condition = { files_modified = { input = [
|
condition = { files_modified = { input = [
|
||||||
"./target/${TARGET}/${TARGETDIR}/kernel",
|
"./target/${TARGET}/${TARGETDIR}/kernel",
|
||||||
@ -65,9 +104,8 @@ condition = { files_modified = { input = [
|
|||||||
"${LIMINEDIR}/limine-uefi-cd.bin",
|
"${LIMINEDIR}/limine-uefi-cd.bin",
|
||||||
"${LIMINEDIR}/${LIMINEBIN}"
|
"${LIMINEDIR}/${LIMINEBIN}"
|
||||||
], output = ["./build/gila.iso"] }, fail_message = "(inputs unchanged)" }
|
], output = ["./build/gila.iso"] }, fail_message = "(inputs unchanged)" }
|
||||||
dependencies = ["kernel"]
|
dependencies = ["kernel", "initramfs"]
|
||||||
script = '''
|
script = '''
|
||||||
#!@shell
|
|
||||||
mkdir -p build/iso/boot/limine
|
mkdir -p build/iso/boot/limine
|
||||||
mkdir -p build/iso/EFI/BOOT
|
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-bios-cd.bin build/iso/boot/limine/
|
||||||
cp -f ${LIMINEDIR}/limine-uefi-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 ${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/bin
|
||||||
mkdir -p build/iso/system/tmp
|
mkdir -p build/iso/system/tmp
|
||||||
@ -100,4 +139,4 @@ script = '''
|
|||||||
[tasks.run]
|
[tasks.run]
|
||||||
dependencies = ["iso"]
|
dependencies = ["iso"]
|
||||||
command = "${QEMUCOMMAND}"
|
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"]
|
@ -4,3 +4,4 @@ timeout: 5
|
|||||||
protocol: limine
|
protocol: limine
|
||||||
kernel_path: boot():/boot/kernel
|
kernel_path: boot():/boot/kernel
|
||||||
cmdline: -loglevel=INFO -logdev=display,serial
|
cmdline: -loglevel=INFO -logdev=display,serial
|
||||||
|
module_path: boot():/boot/initramfs.tar.lzma
|
||||||
|
13
server_config.toml
Normal file
13
server_config.toml
Normal 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]
|
@ -1,5 +1,6 @@
|
|||||||
// Copyright (c) 2025 shibedrill
|
// Copyright (c) 2025 shibedrill
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
mod ports;
|
||||||
mod registers_impl;
|
mod registers_impl;
|
||||||
mod syscall_impl;
|
mod syscall_impl;
|
||||||
|
67
src/lib/arch/x86_64/ports.rs
Normal file
67
src/lib/arch/x86_64/ports.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user