Compare commits
No commits in common. "114e87d11fc065ff581f819edc4da5f71611db79" and "76b7e7b93cce0564f137cfe608bd7d6e986380c2" have entirely different histories.
114e87d11f
...
76b7e7b93c
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -67,6 +67,26 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2"
|
||||||
|
version = "0.7.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2_derive"
|
||||||
|
version = "0.7.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdt"
|
name = "fdt"
|
||||||
version = "0.2.0-alpha1"
|
version = "0.2.0-alpha1"
|
||||||
@ -83,15 +103,13 @@ name = "gila"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acpi",
|
"acpi",
|
||||||
"bitflags",
|
"enumflags2",
|
||||||
"fdt",
|
"fdt",
|
||||||
"flagset",
|
"flagset",
|
||||||
"intbits",
|
"intbits",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"limine",
|
"limine",
|
||||||
"lzma-rs",
|
"lzma-rs",
|
||||||
"num-derive",
|
|
||||||
"num-traits",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"spin 0.10.0",
|
"spin 0.10.0",
|
||||||
"talc",
|
"talc",
|
||||||
@ -155,17 +173,6 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-derive"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
|
@ -5,15 +5,13 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi = "5.1.0"
|
acpi = "5.1.0"
|
||||||
bitflags = "2.9.1"
|
enumflags2 = "0.7.11"
|
||||||
fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" }
|
fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" }
|
||||||
flagset = "0.4.7"
|
flagset = "0.4.7"
|
||||||
intbits = "0.2.0"
|
intbits = "0.2.0"
|
||||||
lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] }
|
lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] }
|
||||||
limine = "0.4.0"
|
limine = "0.4.0"
|
||||||
lzma-rs = { git = "https://github.com/glaeqen/lzma-no-std-rs/", version = "0.2.0", default-features = false }
|
lzma-rs = { git = "https://github.com/glaeqen/lzma-no-std-rs/", version = "0.2.0", default-features = false }
|
||||||
num-derive = "0.4.2"
|
|
||||||
num-traits = { version = "0.2.19", default-features = false }
|
|
||||||
once_cell = { version = "1.20.3", default-features = false, features = ["alloc", "critical-section"] }
|
once_cell = { version = "1.20.3", default-features = false, features = ["alloc", "critical-section"] }
|
||||||
spin = "0.10.0"
|
spin = "0.10.0"
|
||||||
talc = "4.4.2"
|
talc = "4.4.2"
|
||||||
|
43
DESIGN.md
43
DESIGN.md
@ -1,11 +1,11 @@
|
|||||||
# Design Outline
|
# Design Outline
|
||||||
|
|
||||||
Gila is a microkernel, and almost all functionality of the OS is relegated to
|
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
|
"server" processes. A server is a process that provides a specific
|
||||||
functionality, using a kernel-provided interface, and it is given a "seat" by
|
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.
|
the kernel which processes may query to reference it and invoke its functions.
|
||||||
|
|
||||||
## Boot Process
|
# Boot Process
|
||||||
|
|
||||||
Gila initializes as a bare kernel, with the bootloader providing an init RAM
|
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
|
filesystem in the form of a .tar.lzma archive. The kernel reads this file, and
|
||||||
@ -32,20 +32,17 @@ The benefit of this approach is threefold:
|
|||||||
The system, hence, can be configured in two ways:
|
The system, hence, can be configured in two ways:
|
||||||
|
|
||||||
- The drivers can all be included in the initramfs for diskless operation.
|
- The drivers can all be included in the initramfs for diskless operation.
|
||||||
- The bare minimum drivers needed for disk access are included in the
|
- The bare minimum drivers needed for disk access are included in the
|
||||||
initramfs, and all other drivers are included in the root filesystem.
|
initramfs, and all other drivers are included in the root filesystem.
|
||||||
|
|
||||||
## APIs
|
# APIs
|
||||||
|
|
||||||
Processes access services similarly to the way the init process accesses
|
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
|
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
|
performs the function, and then communicates with it via IPC and shared
|
||||||
memory regions. APIs are to be defined in the standard library.
|
memory regions. APIs are to be defined in the standard library.
|
||||||
|
|
||||||
Conventions for local procedure calls and message busing will be worked on
|
# Device Drivers
|
||||||
soon.
|
|
||||||
|
|
||||||
## Device Drivers
|
|
||||||
|
|
||||||
Device driver assignment is performed like so:
|
Device driver assignment is performed like so:
|
||||||
|
|
||||||
@ -57,26 +54,4 @@ Device driver assignment is performed like so:
|
|||||||
into the process's memory space, and returns information on the new mappings
|
into the process's memory space, and returns information on the new mappings
|
||||||
to the process.
|
to the process.
|
||||||
- The driver then makes its functions available via IPC and shared memory.
|
- The driver then makes its functions available via IPC and shared memory.
|
||||||
- The kernel stores information about the driver to avoid spawning duplicates.
|
- The kernel stores information about the driver to avoid spawning duplicates.
|
||||||
|
|
||||||
## Servers vs. Shared Libraries
|
|
||||||
|
|
||||||
Servers and shared libraries serve similar purposes: They make some
|
|
||||||
functionality usable by any process without code duplication. However, there
|
|
||||||
are times when processes and developers should prefer one or the other.
|
|
||||||
|
|
||||||
A server should be used when:
|
|
||||||
|
|
||||||
- The function must somehow acquire a mutually exclusive lock on a resource.
|
|
||||||
- The function should complete asynchronously.
|
|
||||||
|
|
||||||
A shared library should be used when:
|
|
||||||
|
|
||||||
- No resources involved need to be mutually exclusive.
|
|
||||||
- The function is non-blocking and synchronous.
|
|
||||||
|
|
||||||
Hence, servers are very important for things like disk drivers and file
|
|
||||||
system drivers, where non-synchronized writes could cause data loss. It should
|
|
||||||
also be noted that libraries *can*, and often will, call local procedures
|
|
||||||
from servers. The functionality of calling a procedure will be handled by the
|
|
||||||
standard library and shared across processes.
|
|
@ -5,7 +5,6 @@
|
|||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::asm;
|
pub use x86_64::asm;
|
||||||
pub use x86_64::paging;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub mod aarch64;
|
pub mod aarch64;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
#[allow(clippy::missing_safety_doc)]
|
#[allow(clippy::missing_safety_doc)]
|
||||||
pub fn halt() {
|
pub unsafe fn halt() {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("wfi");
|
asm!("wfi");
|
||||||
}
|
}
|
||||||
|
@ -6,53 +6,31 @@
|
|||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
pub fn read_cr0() -> u64 {
|
pub unsafe fn cr0() -> u64 {
|
||||||
let cr0: u64;
|
let mut cr0: u64;
|
||||||
unsafe { asm!("mov {0:r}, cr0", out(reg) cr0) }
|
unsafe { asm!("mov {0:r}, cr0", out(reg) cr0) }
|
||||||
cr0
|
cr0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_cr2() -> usize {
|
pub unsafe fn interrupt_disable() {
|
||||||
let cr2: usize;
|
|
||||||
unsafe { asm!("mov {0:r}, cr2", out(reg) cr2) }
|
|
||||||
cr2
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_cr3() -> u64 {
|
|
||||||
let cr3: u64;
|
|
||||||
unsafe { asm!("mov {0:r}, cr3", out(reg) cr3) }
|
|
||||||
cr3
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_cr3(val: u64) {
|
|
||||||
unsafe { asm!("mov cr3, {0:r}", in(reg) val) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn interrupt_disable() {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("cli");
|
asm!("cli");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupt_enable() {
|
pub unsafe fn interrupt_enable() {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("sti");
|
asm!("sti");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn halt() {
|
pub unsafe fn halt() {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("hlt");
|
asm!("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nop() {
|
pub unsafe fn port_read_u8(port: u16) -> u8 {
|
||||||
unsafe {
|
|
||||||
asm!("nop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn port_read_u8(port: u16) -> u8 {
|
|
||||||
let result: u8;
|
let result: u8;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
@ -64,7 +42,7 @@ pub fn port_read_u8(port: u16) -> u8 {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn port_read_u16(port: u16) -> u16 {
|
pub unsafe fn port_read_u16(port: u16) -> u16 {
|
||||||
let result: u16;
|
let result: u16;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
@ -76,7 +54,7 @@ pub fn port_read_u16(port: u16) -> u16 {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn port_read_u32(port: u16) -> u32 {
|
pub unsafe fn port_read_u32(port: u16) -> u32 {
|
||||||
let result: u32;
|
let result: u32;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
@ -88,7 +66,7 @@ pub fn port_read_u32(port: u16) -> u32 {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn port_write_u8(port: u16, data: u8) {
|
pub unsafe fn port_write_u8(port: u16, data: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
"out dx, al",
|
"out dx, al",
|
||||||
@ -98,7 +76,7 @@ pub fn port_write_u8(port: u16, data: u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn port_write_u16(port: u16, data: u16) {
|
pub unsafe fn port_write_u16(port: u16, data: u16) {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
"out dx, ax",
|
"out dx, ax",
|
||||||
@ -108,7 +86,7 @@ pub fn port_write_u16(port: u16, data: u16) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn port_write_u32(port: u16, data: u32) {
|
pub unsafe fn port_write_u32(port: u16, data: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
"out dx eax",
|
"out dx eax",
|
||||||
|
@ -5,33 +5,8 @@ use core::arch::asm;
|
|||||||
use core::ptr::addr_of_mut;
|
use core::ptr::addr_of_mut;
|
||||||
|
|
||||||
use intbits::Bits;
|
use intbits::Bits;
|
||||||
use x86_64::structures::gdt::*;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub unsafe fn gdt() -> (u8, u64) {
|
||||||
pub trait GdtEntryRead {
|
|
||||||
fn base(&self) -> usize;
|
|
||||||
fn limit(&self) -> usize;
|
|
||||||
fn flags(&self) -> u8;
|
|
||||||
fn access(&self) -> u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GdtEntryRead for Entry {
|
|
||||||
fn base(&self) -> usize {
|
|
||||||
((self.raw().bits(56..=63) << 24) & self.raw().bits(16..=39)) as usize
|
|
||||||
}
|
|
||||||
fn limit(&self) -> usize {
|
|
||||||
((self.raw().bits(48..=51) << 16) & self.raw().bits(16..=31)) as usize
|
|
||||||
}
|
|
||||||
fn access(&self) -> u8 {
|
|
||||||
self.raw().bits(40..=47) as u8
|
|
||||||
}
|
|
||||||
fn flags(&self) -> u8 {
|
|
||||||
self.raw().bits(52..=55) as u8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn read_gdt() -> GlobalDescriptorTable {
|
|
||||||
let mut addr: u128 = 0;
|
let mut addr: u128 = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm! {
|
asm! {
|
||||||
@ -39,10 +14,5 @@ pub fn read_gdt() -> GlobalDescriptorTable {
|
|||||||
in("rax") addr_of_mut!(addr),
|
in("rax") addr_of_mut!(addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// x86 gdtr: Lower 2 8-bit bytes (lower 16 bits) is the size of the GDT,
|
(addr.bits(0..=15) as u8, addr.bits(16..=79) as u64)
|
||||||
// in 8-bit bytes, minus one.
|
|
||||||
// Bits 16 through 79 are the virtual address of the GDT.
|
|
||||||
let (limit, addr) = (addr.bits(0..=15) as usize + 1, addr.bits(16..=79) as u64);
|
|
||||||
let gdt_raw: &[u64] = unsafe { core::slice::from_raw_parts(addr as *const u64, limit / 8) };
|
|
||||||
GlobalDescriptorTable::from_raw_entries(gdt_raw)
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ lazy_static! {
|
|||||||
pub static ref IDT: InterruptDescriptorTable = {
|
pub static ref IDT: InterruptDescriptorTable = {
|
||||||
let mut idt = InterruptDescriptorTable::new();
|
let mut idt = InterruptDescriptorTable::new();
|
||||||
idt.double_fault.set_handler_fn(double_fault);
|
idt.double_fault.set_handler_fn(double_fault);
|
||||||
idt.page_fault.set_handler_fn(page_fault);
|
|
||||||
idt
|
idt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -18,24 +17,3 @@ lazy_static! {
|
|||||||
extern "x86-interrupt" fn double_fault(info: InterruptStackFrame, _: u64) -> ! {
|
extern "x86-interrupt" fn double_fault(info: InterruptStackFrame, _: u64) -> ! {
|
||||||
crate::interrupt::double_fault(&format!("{info:#?}"));
|
crate::interrupt::double_fault(&format!("{info:#?}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn page_fault(info: InterruptStackFrame, errcode: PageFaultErrorCode) {
|
|
||||||
if errcode.contains(PageFaultErrorCode::USER_MODE) {
|
|
||||||
// Fault occurred in usermode. Non fatal.
|
|
||||||
todo!()
|
|
||||||
} else {
|
|
||||||
// Fault occurred in kernel mode. This is possibly fatal.
|
|
||||||
// This is recoverable if we simply hit an unavailable page,
|
|
||||||
// but unrecoverable if we hit a nonexistent or invalid page.
|
|
||||||
if errcode.contains(PageFaultErrorCode::PROTECTION_VIOLATION)
|
|
||||||
| errcode.contains(PageFaultErrorCode::MALFORMED_TABLE)
|
|
||||||
{
|
|
||||||
let addr = unsafe {
|
|
||||||
let a: usize;
|
|
||||||
core::arch::asm! {"mov {}, cr2", out(reg) a};
|
|
||||||
a
|
|
||||||
};
|
|
||||||
crate::interrupt::page_fault(addr, &format!("{info:#?}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,5 +4,4 @@
|
|||||||
pub mod asm;
|
pub mod asm;
|
||||||
pub mod gdt;
|
pub mod gdt;
|
||||||
pub mod interrupts;
|
pub mod interrupts;
|
||||||
pub mod paging;
|
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
|
@ -1,256 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use intbits::Bits;
|
|
||||||
use x86_64::structures::paging::page_table;
|
|
||||||
|
|
||||||
pub struct PageMapLevel5 {
|
|
||||||
entries: [PageMapLevel5Entry; 512],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageMapLevel5Entry {
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageMapLevel5Entry {
|
|
||||||
pub fn execute_disable(&self) -> bool {
|
|
||||||
self.value.bit(63)
|
|
||||||
}
|
|
||||||
pub fn present(&self) -> bool {
|
|
||||||
self.value.bit(0)
|
|
||||||
}
|
|
||||||
pub fn set_present(&mut self, present: bool) {
|
|
||||||
self.value.set_bit(0, present);
|
|
||||||
}
|
|
||||||
pub fn writable(&self) -> bool {
|
|
||||||
self.value.bit(1)
|
|
||||||
}
|
|
||||||
pub fn set_writable(&mut self, writable: bool) {
|
|
||||||
self.value.set_bit(1, writable);
|
|
||||||
}
|
|
||||||
pub fn user_accessible(&self) -> bool {
|
|
||||||
self.value.bit(2)
|
|
||||||
}
|
|
||||||
pub fn set_user_accessible(&mut self, accessible: bool) {
|
|
||||||
self.value.set_bit(2, accessible);
|
|
||||||
}
|
|
||||||
pub fn write_through(&self) -> bool {
|
|
||||||
self.value.bit(3)
|
|
||||||
}
|
|
||||||
pub fn set_write_through(&mut self, write_through: bool) {
|
|
||||||
self.value.set_bit(3, write_through);
|
|
||||||
}
|
|
||||||
pub fn cache_disable(&self) -> bool {
|
|
||||||
self.value.bit(4)
|
|
||||||
}
|
|
||||||
pub fn set_cache_disable(&mut self, cache_disable: bool) {
|
|
||||||
self.value.set_bit(4, cache_disable);
|
|
||||||
}
|
|
||||||
pub fn accessed(&self) -> bool {
|
|
||||||
self.value.bit(5)
|
|
||||||
}
|
|
||||||
pub fn set_accessed(&mut self, accessed: bool) {
|
|
||||||
self.value.set_bit(5, accessed);
|
|
||||||
}
|
|
||||||
pub fn physical_address(&self) -> usize {
|
|
||||||
self.value.bits(12..=51) as usize
|
|
||||||
}
|
|
||||||
pub fn set_physical_address(&mut self, address: u64) {
|
|
||||||
self.value &= 0xFFF0000000000FFF;
|
|
||||||
self.value |= (address & 0xFFFFFFFFFF) << 12;
|
|
||||||
}
|
|
||||||
pub fn available(&self) -> usize {
|
|
||||||
(self.value.bits(52..=62) << 4 & self.value.bits(8..=11) << 1 & self.value.bits(6..=6))
|
|
||||||
as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageMapLevel4 {
|
|
||||||
entries: [PageMapLevel4Entry; 512],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageMapLevel4Entry {
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageMapLevel4Entry {
|
|
||||||
pub fn execute_disable(&self) -> bool {
|
|
||||||
self.value.bit(63)
|
|
||||||
}
|
|
||||||
pub fn present(&self) -> bool {
|
|
||||||
self.value.bit(0)
|
|
||||||
}
|
|
||||||
pub fn set_present(&mut self, present: bool) {
|
|
||||||
self.value.set_bit(0, present);
|
|
||||||
}
|
|
||||||
pub fn writable(&self) -> bool {
|
|
||||||
self.value.bit(1)
|
|
||||||
}
|
|
||||||
pub fn set_writable(&mut self, writable: bool) {
|
|
||||||
self.value.set_bit(1, writable);
|
|
||||||
}
|
|
||||||
pub fn user_accessible(&self) -> bool {
|
|
||||||
self.value.bit(2)
|
|
||||||
}
|
|
||||||
pub fn set_user_accessible(&mut self, accessible: bool) {
|
|
||||||
self.value.set_bit(2, accessible);
|
|
||||||
}
|
|
||||||
pub fn write_through(&self) -> bool {
|
|
||||||
self.value.bit(3)
|
|
||||||
}
|
|
||||||
pub fn set_write_through(&mut self, write_through: bool) {
|
|
||||||
self.value.set_bit(3, write_through);
|
|
||||||
}
|
|
||||||
pub fn cache_disable(&self) -> bool {
|
|
||||||
self.value.bit(4)
|
|
||||||
}
|
|
||||||
pub fn set_cache_disable(&mut self, cache_disable: bool) {
|
|
||||||
self.value.set_bit(4, cache_disable);
|
|
||||||
}
|
|
||||||
pub fn accessed(&self) -> bool {
|
|
||||||
self.value.bit(5)
|
|
||||||
}
|
|
||||||
pub fn set_accessed(&mut self, accessed: bool) {
|
|
||||||
self.value.set_bit(5, accessed);
|
|
||||||
}
|
|
||||||
pub fn physical_address(&self) -> usize {
|
|
||||||
self.value.bits(12..=51) as usize
|
|
||||||
}
|
|
||||||
pub fn set_physical_address(&mut self, address: u64) {
|
|
||||||
self.value &= 0xFFF0000000000FFF;
|
|
||||||
self.value |= (address & 0xFFFFFFFFFF) << 12;
|
|
||||||
}
|
|
||||||
pub fn set_pointer_table(&mut self, table: &PageDirectoryPointerTable) {
|
|
||||||
self.set_physical_address(&raw const table as u64);
|
|
||||||
}
|
|
||||||
pub fn available(&self) -> usize {
|
|
||||||
(self.value.bits(52..=62) << 4 & self.value.bits(8..=11) << 1 & self.value.bits(6..=6))
|
|
||||||
as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageDirectoryPointerTable {
|
|
||||||
entries: [*mut PageDirectory; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageDirectoryPointer {
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageDirectoryPointer {
|
|
||||||
pub fn execute_disable(&self) -> bool {
|
|
||||||
self.value.bit(63)
|
|
||||||
}
|
|
||||||
pub fn present(&self) -> bool {
|
|
||||||
self.value.bit(0)
|
|
||||||
}
|
|
||||||
pub fn set_present(&mut self, present: bool) {
|
|
||||||
self.value.set_bit(0, present);
|
|
||||||
}
|
|
||||||
pub fn writable(&self) -> bool {
|
|
||||||
self.value.bit(1)
|
|
||||||
}
|
|
||||||
pub fn set_writable(&mut self, writable: bool) {
|
|
||||||
self.value.set_bit(1, writable);
|
|
||||||
}
|
|
||||||
pub fn user_accessible(&self) -> bool {
|
|
||||||
self.value.bit(2)
|
|
||||||
}
|
|
||||||
pub fn set_user_accessible(&mut self, accessible: bool) {
|
|
||||||
self.value.set_bit(2, accessible);
|
|
||||||
}
|
|
||||||
pub fn write_through(&self) -> bool {
|
|
||||||
self.value.bit(3)
|
|
||||||
}
|
|
||||||
pub fn set_write_through(&mut self, write_through: bool) {
|
|
||||||
self.value.set_bit(3, write_through);
|
|
||||||
}
|
|
||||||
pub fn cache_disable(&self) -> bool {
|
|
||||||
self.value.bit(4)
|
|
||||||
}
|
|
||||||
pub fn set_cache_disable(&mut self, cache_disable: bool) {
|
|
||||||
self.value.set_bit(4, cache_disable);
|
|
||||||
}
|
|
||||||
pub fn accessed(&self) -> bool {
|
|
||||||
self.value.bit(5)
|
|
||||||
}
|
|
||||||
pub fn set_accessed(&mut self, accessed: bool) {
|
|
||||||
self.value.set_bit(5, accessed);
|
|
||||||
}
|
|
||||||
pub fn physical_address(&self) -> *mut PageDirectory {
|
|
||||||
self.value.bits(12..=51) as *mut PageDirectory
|
|
||||||
}
|
|
||||||
pub fn set_physical_address(&mut self, address: u64) {
|
|
||||||
self.value &= 0xFFF0000000000FFF;
|
|
||||||
self.value |= (address & 0xFFFFFFFFFF) << 12;
|
|
||||||
}
|
|
||||||
pub fn set_page_directory(&mut self, dir: &PageDirectory) {
|
|
||||||
self.set_physical_address(&raw const dir as u64);
|
|
||||||
}
|
|
||||||
pub fn available(&self) -> usize {
|
|
||||||
(self.value.bits(52..=62) << 4 & self.value.bits(8..=11) << 1 & self.value.bits(6..=6))
|
|
||||||
as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageDirectory {
|
|
||||||
entries: [PageDirectoryEntry; 512],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageDirectoryEntry {
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PageDirectoryEntry {
|
|
||||||
pub fn execute_disable(&self) -> bool {
|
|
||||||
self.value.bit(63)
|
|
||||||
}
|
|
||||||
pub fn present(&self) -> bool {
|
|
||||||
self.value.bit(0)
|
|
||||||
}
|
|
||||||
pub fn set_present(&mut self, present: bool) {
|
|
||||||
self.value.set_bit(0, present);
|
|
||||||
}
|
|
||||||
pub fn writable(&self) -> bool {
|
|
||||||
self.value.bit(1)
|
|
||||||
}
|
|
||||||
pub fn set_writable(&mut self, writable: bool) {
|
|
||||||
self.value.set_bit(1, writable);
|
|
||||||
}
|
|
||||||
pub fn user_accessible(&self) -> bool {
|
|
||||||
self.value.bit(2)
|
|
||||||
}
|
|
||||||
pub fn set_user_accessible(&mut self, accessible: bool) {
|
|
||||||
self.value.set_bit(2, accessible);
|
|
||||||
}
|
|
||||||
pub fn write_through(&self) -> bool {
|
|
||||||
self.value.bit(3)
|
|
||||||
}
|
|
||||||
pub fn set_write_through(&mut self, write_through: bool) {
|
|
||||||
self.value.set_bit(3, write_through);
|
|
||||||
}
|
|
||||||
pub fn cache_disable(&self) -> bool {
|
|
||||||
self.value.bit(4)
|
|
||||||
}
|
|
||||||
pub fn set_cache_disable(&mut self, cache_disable: bool) {
|
|
||||||
self.value.set_bit(4, cache_disable);
|
|
||||||
}
|
|
||||||
pub fn accessed(&self) -> bool {
|
|
||||||
self.value.bit(5)
|
|
||||||
}
|
|
||||||
pub fn set_accessed(&mut self, accessed: bool) {
|
|
||||||
self.value.set_bit(5, accessed);
|
|
||||||
}
|
|
||||||
pub fn physical_address(&self) -> usize {
|
|
||||||
self.value.bits(12..=51) as usize
|
|
||||||
}
|
|
||||||
pub fn set_physical_address(&mut self, address: u64) {
|
|
||||||
self.value &= 0xFFF0000000000FFF;
|
|
||||||
self.value |= (address & 0xFFFFFFFFFF) << 12;
|
|
||||||
}
|
|
||||||
pub fn available(&self) -> usize {
|
|
||||||
(self.value.bits(52..=62) << 4 & self.value.bits(8..=11) << 1 & self.value.bits(6..=6))
|
|
||||||
as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use page_table::PageTable;
|
|
@ -12,7 +12,7 @@ pub struct Serialport {
|
|||||||
impl LogSubscriber for Serialport {
|
impl LogSubscriber for Serialport {
|
||||||
fn write(&self, msg: &str) {
|
fn write(&self, msg: &str) {
|
||||||
for c in msg.chars() {
|
for c in msg.chars() {
|
||||||
port_write_u8(self.port, c as u8);
|
unsafe { port_write_u8(self.port, c as u8) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ use crate::format;
|
|||||||
use crate::log::LogLevel;
|
use crate::log::LogLevel;
|
||||||
use crate::memory::alloc::string::String;
|
use crate::memory::alloc::string::String;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub static INITRAMFS_DEFAULT_PATH: &str = "/boot/initramfs.tar.lzma";
|
pub static INITRAMFS_DEFAULT_PATH: &str = "/boot/initramfs.tar.lzma";
|
||||||
|
|
||||||
pub static LOG_DEFAULT_LEVEL: LogLevel = LogLevel::Info;
|
pub static LOG_DEFAULT_LEVEL: LogLevel = LogLevel::Info;
|
||||||
|
@ -10,7 +10,7 @@ use limine::response::{DeviceTreeBlobResponse, RsdpResponse};
|
|||||||
use crate::boot::DTB_REQUEST;
|
use crate::boot::DTB_REQUEST;
|
||||||
use crate::device::acpi::RSDP_REQUEST;
|
use crate::device::acpi::RSDP_REQUEST;
|
||||||
use crate::log::{LOGGER, LogLevel};
|
use crate::log::{LOGGER, LogLevel};
|
||||||
use crate::{format, log_error, log_info, log_trace, log_warning};
|
use crate::{format, log_info, log_trace, log_warning};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = match DTB_REQUEST.get_response() {
|
pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = match DTB_REQUEST.get_response() {
|
||||||
@ -37,11 +37,8 @@ lazy_static! {
|
|||||||
|
|
||||||
// Just to check
|
// Just to check
|
||||||
pub fn init_statics() {
|
pub fn init_statics() {
|
||||||
let dtb = DTB.is_some();
|
let _ = DTB.is_some();
|
||||||
let rdsp = RSDP.is_some();
|
let _ = RSDP.is_some();
|
||||||
if !dtb & !rdsp {
|
|
||||||
log_error!("Device: Neither DTB nor ACPI available, booted system will be useless")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -4,7 +4,3 @@
|
|||||||
pub fn double_fault(info: &str) -> ! {
|
pub fn double_fault(info: &str) -> ! {
|
||||||
panic!("Double fault: {}", info);
|
panic!("Double fault: {}", info);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_fault(addr: usize, info: &str) -> ! {
|
|
||||||
panic!("Page fault at 0x{:X}: {}", addr, info);
|
|
||||||
}
|
|
||||||
|
@ -67,7 +67,7 @@ impl Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The logger exists for the entire lifetime of the kernel.
|
/// The logger exists for the entire lifetime of the kernel.
|
||||||
pub static LOGGER: Logger = Logger {
|
pub static LOGGER: Logger = Logger {
|
||||||
inner: Mutex::new(LoggerInner::new()),
|
inner: Mutex::new(LoggerInner::new()),
|
||||||
};
|
};
|
||||||
@ -97,8 +97,8 @@ impl LoggerInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling log will sequentially acquire lock on all logging subscribers
|
/// Calling log will sequentially acquire lock on all logging subscribers
|
||||||
// to write to them with a formatted log message.
|
/// to write to them with a formatted log message.
|
||||||
pub fn log(&self, level: LogLevel, msg: &str) {
|
pub fn log(&self, level: LogLevel, msg: &str) {
|
||||||
// Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder.
|
// Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder.
|
||||||
if level == LogLevel::Disabled {
|
if level == LogLevel::Disabled {
|
||||||
|
@ -19,16 +19,22 @@ mod process;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
use arch::x86_64::gdt::gdt;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use arch::x86_64::serial::Serialport;
|
use arch::x86_64::serial::Serialport;
|
||||||
|
|
||||||
|
use arch::asm::*;
|
||||||
use boot::{modules::*, params, *};
|
use boot::{modules::*, params, *};
|
||||||
use constants::*;
|
use constants::*;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use memory::{MEMMAP_REQUEST, HHDM_RESPONSE};
|
||||||
use memory::alloc::{format, string::*, vec};
|
use memory::alloc::{format, string::*, vec};
|
||||||
use memory::{HHDM_RESPONSE, MEMMAP_REQUEST};
|
|
||||||
use params::*;
|
use params::*;
|
||||||
|
|
||||||
|
use intbits::{self, Bits};
|
||||||
|
use limine::file::File;
|
||||||
use limine::memory_map::EntryType;
|
use limine::memory_map::EntryType;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use lzma_rs::lzma_decompress;
|
use lzma_rs::lzma_decompress;
|
||||||
@ -107,6 +113,27 @@ unsafe extern "C" fn main() -> ! {
|
|||||||
log_trace!("{log_msg}")
|
log_trace!("{log_msg}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let irfs_path = {
|
||||||
|
if let Some(key) = PARAMS.get("-initramfs") {
|
||||||
|
key
|
||||||
|
} else {
|
||||||
|
INITRAMFS_DEFAULT_PATH
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log_trace!("Boot: initramfs path requested: {irfs_path}");
|
||||||
|
let _initramfs = {
|
||||||
|
let mut result: Option<&File> = None;
|
||||||
|
for file in MODULE_RESPONSE.modules() {
|
||||||
|
if file.path().to_string_lossy() == irfs_path {
|
||||||
|
result = Some(file);
|
||||||
|
log_info!("Boot: initramfs found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
.expect("initramfs not found in modules list");
|
||||||
|
|
||||||
// Panic if this is absent. It's needed to set up the GDT and paging
|
// Panic if this is absent. It's needed to set up the GDT and paging
|
||||||
let mmap_response = MEMMAP_REQUEST
|
let mmap_response = MEMMAP_REQUEST
|
||||||
.get_response()
|
.get_response()
|
||||||
@ -120,7 +147,7 @@ unsafe extern "C" fn main() -> ! {
|
|||||||
let mut unusable: u64 = 0;
|
let mut unusable: u64 = 0;
|
||||||
for entry in mmap_response.entries() {
|
for entry in mmap_response.entries() {
|
||||||
log_msg.push_str(&format!(
|
log_msg.push_str(&format!(
|
||||||
"\n\t\t0x{:X} bytes @ 0x{:X}: {}",
|
"\n\t\t0x{:x} bytes @ 0x{:x}: {}",
|
||||||
entry.length,
|
entry.length,
|
||||||
entry.base,
|
entry.base,
|
||||||
match entry.entry_type {
|
match entry.entry_type {
|
||||||
@ -172,14 +199,43 @@ unsafe extern "C" fn main() -> ! {
|
|||||||
panic!("Memory map contains no entries");
|
panic!("Memory map contains no entries");
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info!("HHDM offset: 0x{:X}", HHDM_RESPONSE.offset());
|
log_info!("HHDM offset: 0x{:x}", HHDM_RESPONSE.offset());
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
{
|
||||||
|
log_trace!(
|
||||||
|
"Boot: Protected mode enable: {}",
|
||||||
|
unsafe { cr0() & 0b1 } == 1
|
||||||
|
);
|
||||||
|
log_trace!(
|
||||||
|
"Boot: Paging enable: {}",
|
||||||
|
unsafe { cr0() & (0b1 << 31) } == 1
|
||||||
|
);
|
||||||
|
let pd_addr = {
|
||||||
|
let cr3: u64;
|
||||||
|
unsafe {
|
||||||
|
asm! {
|
||||||
|
"mov {}, cr3",
|
||||||
|
out(reg) cr3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_trace!("Boot: cr3 value: 0b{:b}", cr3);
|
||||||
|
cr3.bits(12..=31)
|
||||||
|
};
|
||||||
|
log_trace!("Boot: Physical address of page directory: 0x{:x}", pd_addr);
|
||||||
|
log_trace!("Boot: Virtual address of page directory: 0x{:x}", pd_addr + HHDM_RESPONSE.offset());
|
||||||
|
log_trace!("Boot: GDT: 0x{:x} bytes @ 0x{:x}", unsafe { gdt().0 }, unsafe { gdt().1} );
|
||||||
|
log_trace!("Boot: GDT: 0x{:x}", unsafe { *((gdt().1 + 0x0) as *const u64) })
|
||||||
|
}
|
||||||
|
|
||||||
panic!("Bailing");
|
panic!("Bailing");
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
loop {
|
loop {
|
||||||
for _i in 0..50000000u64 {
|
for _i in 0..50000000u64 {
|
||||||
crate::arch::asm::nop();
|
unsafe {
|
||||||
|
core::arch::asm!("nop");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
core::hint::black_box(());
|
core::hint::black_box(());
|
||||||
log_trace!("Heartbeat");
|
log_trace!("Heartbeat");
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// Copyright (c) 2025 shibedrill
|
// Copyright (c) 2025 shibedrill
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
#![allow(unused_imports)]
|
||||||
use limine::{
|
|
||||||
request::{ExecutableAddressRequest, HhdmRequest, MemoryMapRequest},
|
|
||||||
response::HhdmResponse,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use enumflags2::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use limine::{request::{ExecutableAddressRequest, HhdmRequest, MemoryMapRequest}, response::HhdmResponse};
|
||||||
|
|
||||||
|
use core::alloc::{Allocator, Layout};
|
||||||
use talc::*;
|
use talc::*;
|
||||||
|
|
||||||
pub extern crate alloc;
|
pub extern crate alloc;
|
||||||
@ -25,15 +26,39 @@ pub static MEMMAP_REQUEST: MemoryMapRequest = limine::request::MemoryMapRequest:
|
|||||||
pub static HHDM_REQUEST: HhdmRequest = limine::request::HhdmRequest::new();
|
pub static HHDM_REQUEST: HhdmRequest = limine::request::HhdmRequest::new();
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref HHDM_RESPONSE: &'static HhdmResponse = HHDM_REQUEST
|
pub static ref HHDM_RESPONSE: &'static HhdmResponse = HHDM_REQUEST.get_response().expect("Did not get HHDM response from bootloader");
|
||||||
.get_response()
|
|
||||||
.expect("Did not get HHDM response from bootloader");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 10kb kernel heap. Need to figure out how to make this less stupid...
|
|
||||||
static mut ARENA: [u8; 10000] = [0; 10000];
|
static mut ARENA: [u8; 10000] = [0; 10000];
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
|
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> = Talc::new(unsafe {
|
||||||
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
|
// if we're in a hosted environment, the Rust runtime may allocate before
|
||||||
.lock();
|
// main() is called, so we need to initialize the arena automatically
|
||||||
|
ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut()))
|
||||||
|
})
|
||||||
|
.lock();
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct MemoryRegion {
|
||||||
|
start_address: usize,
|
||||||
|
end_address: usize,
|
||||||
|
flags: BitFlags<MemoryRegionFlags>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRegion {
|
||||||
|
// TODO: Memory allocation and virtual addressing
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn new(flags: BitFlags<MemoryRegionFlags>) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitflags]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MemoryRegionFlags {
|
||||||
|
Readable,
|
||||||
|
Writable,
|
||||||
|
Executable,
|
||||||
|
}
|
||||||
|
@ -11,8 +11,6 @@ use crate::{LOGGER, LogLevel};
|
|||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
pub fn panic(info: &PanicInfo) -> ! {
|
pub fn panic(info: &PanicInfo) -> ! {
|
||||||
log_critical!("Panic in {}: {}", info.location().unwrap(), info.message());
|
log_critical!("Panic in {}: {}", info.location().unwrap(), info.message());
|
||||||
// TODO: If any userspace facilities are still working, *attempt* to flush panic info and
|
|
||||||
// logs to disk or whatever else. Then kill all processes and reboot.
|
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("nop");
|
asm!("nop");
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
// Copyright (c) 2025 shibedrill
|
// Copyright (c) 2025 shibedrill
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
use crate::arch::paging::{PageDirectory, PageTable};
|
#![allow(unused_imports, dead_code)]
|
||||||
|
|
||||||
|
use enumflags2::{BitFlags, bitflags, make_bitflags};
|
||||||
|
|
||||||
|
use crate::arch::asm;
|
||||||
|
use crate::memory::MemoryRegion;
|
||||||
|
use crate::memory::MemoryRegionFlags;
|
||||||
use crate::memory::alloc;
|
use crate::memory::alloc;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub static PROCESS_TABLE: Mutex<Vec<Process>> = Mutex::new(Vec::new());
|
pub struct ProcessTable {}
|
||||||
// Currently running process. Invalid once the referenced process dies and
|
|
||||||
// is destructed.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub static CURRENT_PROCESS: Mutex<Option<&Process>> = Mutex::new(None);
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
@ -20,6 +21,8 @@ pub struct Process {
|
|||||||
proc_id: u32,
|
proc_id: u32,
|
||||||
/// ID of the parent process.
|
/// ID of the parent process.
|
||||||
parent_proc_id: u32,
|
parent_proc_id: u32,
|
||||||
|
/// List of all child processes.
|
||||||
|
child_proc_ids: Vec<u32>,
|
||||||
/// Human readable name of the process, should be the command.
|
/// Human readable name of the process, should be the command.
|
||||||
name: String,
|
name: String,
|
||||||
/// The base address of the process stack within its memory region.
|
/// The base address of the process stack within its memory region.
|
||||||
@ -27,30 +30,62 @@ pub struct Process {
|
|||||||
/// The length of the stack. This, plus the start address, is the stack size.
|
/// The length of the stack. This, plus the start address, is the stack size.
|
||||||
stack_length: usize,
|
stack_length: usize,
|
||||||
/// The stack pointer, which is relative to the memory region start.
|
/// The stack pointer, which is relative to the memory region start.
|
||||||
stack_pointer: usize,
|
stack_ptr: usize,
|
||||||
/// The page direcory pointing to this process's pages.
|
/// The region of memory allocated to the process.
|
||||||
page_directory: PageDirectory,
|
mem_region: MemoryRegion,
|
||||||
/// All pages owned by the process.
|
|
||||||
/// When creating a process, we should create one read-write data page,
|
|
||||||
/// with execution disabled, and one read-only executable page.
|
|
||||||
page_tables: Vec<PageTable>,
|
|
||||||
/// Process priority. Lower number is higher priority.
|
/// Process priority. Lower number is higher priority.
|
||||||
priority: u16,
|
prio: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn context_switch() -> ! {
|
pub unsafe fn context_switch() -> ! {
|
||||||
loop {}
|
#[allow(unused_unsafe)]
|
||||||
|
unsafe {
|
||||||
|
loop {
|
||||||
|
asm::halt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum ProcessState {
|
pub enum ProcessState {
|
||||||
// The process is the one being currently executed.
|
|
||||||
Running,
|
Running,
|
||||||
// The process is blocked by a syscall.
|
|
||||||
Waiting,
|
Waiting,
|
||||||
// The process is voluntarily napping until an alarm goes off.
|
|
||||||
Sleeping,
|
Sleeping,
|
||||||
// The process has been "paused", and can be resumed or killed.
|
|
||||||
Suspended,
|
Suspended,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interprocess communication system:
|
||||||
|
// Processes communicate through "sessions" which are mediated by the kernel.
|
||||||
|
// Sessions can only be opened if two processes both request a session with
|
||||||
|
// each other (mutual operation). Submitting a session request is a blocking
|
||||||
|
// system call that will return either once the other process accepts, or the
|
||||||
|
// kernel denies the request due to missing capability, or the other process
|
||||||
|
// not existing yet.
|
||||||
|
pub struct ProcessSession {
|
||||||
|
proc_id_a: u32,
|
||||||
|
proc_id_b: u32,
|
||||||
|
shared_mem: MemoryRegion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessSession {
|
||||||
|
pub fn new(a: ProcessSessionRequest, b: ProcessSessionRequest) -> Self {
|
||||||
|
ProcessSession {
|
||||||
|
proc_id_a: a.proc_id,
|
||||||
|
proc_id_b: b.proc_id,
|
||||||
|
shared_mem: MemoryRegion::new(
|
||||||
|
MemoryRegionFlags::Readable | MemoryRegionFlags::Writable,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcessSessionRequest {
|
||||||
|
proc_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessSessionRequest {
|
||||||
|
pub fn accept(self, b: ProcessSessionRequest) -> ProcessSession {
|
||||||
|
ProcessSession::new(self, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
// Copyright (c) 2025 shibedrill
|
// Copyright (c) 2025 shibedrill
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/// Critical section: Mark a section of code as critical.
|
|
||||||
/// Interrupts will be disabled until the section completes.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! critical_section {
|
macro_rules! critical_section {
|
||||||
($b:block) => {
|
($b:block) => {
|
||||||
$crate::arch::asm::interrupt_disable();
|
unsafe { $crate::arch::asm::interrupt_disable(); }
|
||||||
$b
|
$b
|
||||||
$crate::arch::asm::interrupt_enable();
|
unsafe { $crate::arch::asm::interrupt_enable(); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::syscall_impl;
|
pub use x86_64 as current;
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub mod aarch64;
|
pub mod aarch64;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Copyright (c) 2025 shibedrill
|
// Copyright (c) 2025 shibedrill
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
pub mod syscall_impl;
|
mod registers_impl;
|
||||||
|
mod syscall_impl;
|
||||||
|
7
src/lib/arch/x86_64/registers_impl.rs
Normal file
7
src/lib/arch/x86_64/registers_impl.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2025 shibedrill
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Registers {
|
||||||
|
// Private fields
|
||||||
|
}
|
@ -6,131 +6,3 @@
|
|||||||
// The system call API for x86_64.
|
// The system call API for x86_64.
|
||||||
|
|
||||||
use crate::syscall::*;
|
use crate::syscall::*;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_0(call: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_1(call: u64, one: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_2(call: u64, one: u64, two: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
in("rsi") two,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_3(call: u64, one: u64, two: u64, three: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
in("rsi") two,
|
|
||||||
in("rdx") three,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_4(call: u64, one: u64, two: u64, three: u64, four: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
in("rsi") two,
|
|
||||||
in("rdx") three,
|
|
||||||
in("r10") four,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_5(call: u64, one: u64, two: u64, three: u64, four: u64, five: u64) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
in("rsi") two,
|
|
||||||
in("rdx") three,
|
|
||||||
in("r10") four,
|
|
||||||
in("r8") five,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn caller_syscall_6(
|
|
||||||
call: u64,
|
|
||||||
one: u64,
|
|
||||||
two: u64,
|
|
||||||
three: u64,
|
|
||||||
four: u64,
|
|
||||||
five: u64,
|
|
||||||
six: u64,
|
|
||||||
) -> u64 {
|
|
||||||
let returncode: u64;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inlateout("rax") call => returncode,
|
|
||||||
in("rdi") one,
|
|
||||||
in("rsi") two,
|
|
||||||
in("rdx") three,
|
|
||||||
in("r10") four,
|
|
||||||
in("r8") five,
|
|
||||||
in("r9") six,
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack, preserves_flags)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
returncode
|
|
||||||
}
|
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
mod arch;
|
mod arch;
|
||||||
pub mod registers;
|
pub mod registers;
|
||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
pub use arch::*;
|
pub use arch::current::*;
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
// Every architecture MUST implement this as part of the ABI.
|
// Every architecture MUST implement this as part of the ABI.
|
||||||
// Additional registers can be implemented with architecture-specific traits.
|
// Additional registers can be implemented with architecture-specific traits.
|
||||||
|
|
||||||
pub trait RegStoreLoad
|
#[allow(clippy::missing_safety_doc)]
|
||||||
|
pub unsafe trait RegStoreLoad
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
Self: Default,
|
Self: Default,
|
||||||
@ -27,14 +28,14 @@ where
|
|||||||
|
|
||||||
// Get the six argument or return register values.
|
// Get the six argument or return register values.
|
||||||
// This MUST read registers in the same order as set_syscall_args sets them.
|
// This MUST read registers in the same order as set_syscall_args sets them.
|
||||||
fn read_syscall_args(&self) -> [u64; 6];
|
fn read_syscall_args(&self) -> [usize; 6];
|
||||||
// Get the syscall number or error number.
|
// Get the syscall number or error number.
|
||||||
// This MUST NOT zero or otherwise overwrite the register.
|
// This MUST NOT zero or otherwise overwrite the register.
|
||||||
fn read_syscall_num(&self) -> u64;
|
fn read_syscall_num(&self) -> usize;
|
||||||
// Set the six argument or return register values.
|
// Set the six argument or return register values.
|
||||||
// This MUST write registers in the same order as read_syscall_args reads them.
|
// This MUST write registers in the same order as read_syscall_args reads them.
|
||||||
fn set_syscall_args(&mut self, new_values: [u64; 6]);
|
fn set_syscall_args(&mut self, new_values: [usize; 6]);
|
||||||
// Set the syscall number or error number.
|
// Set the syscall number or error number.
|
||||||
// This MUST NOT change any other registers.
|
// This MUST NOT change any other registers.
|
||||||
fn set_syscall_num(&mut self, new_value: u64);
|
fn set_syscall_num(&mut self, new_value: usize);
|
||||||
}
|
}
|
||||||
|
28
src/lib/syscall.rs
Normal file
28
src/lib/syscall.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) 2025 shibedrill
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
// TODO: Implement a nice API for system calls.
|
||||||
|
// I don't want to have to define the argument/return value registers twice
|
||||||
|
// per architecture. How do I make this work?
|
||||||
|
|
||||||
|
pub enum SyscallArgs {
|
||||||
|
Args0(),
|
||||||
|
Args1(usize),
|
||||||
|
Args2(usize, usize),
|
||||||
|
Args3(usize, usize, usize),
|
||||||
|
Args4(usize, usize, usize, usize),
|
||||||
|
Args5(usize, usize, usize, usize, usize),
|
||||||
|
Args6(usize, usize, usize, usize, usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(usize)]
|
||||||
|
pub enum SyscallError {
|
||||||
|
Ok, // No error.
|
||||||
|
Unspecified, // Unspecified error occurred.
|
||||||
|
SyscallNotExist, // System call does not exist.
|
||||||
|
ProcessNotExist, // The process mentioned does not exist.
|
||||||
|
PermissionDenied, // The process lacks capabilities.
|
||||||
|
Aborted, // The kernel gave up on a blocking request.
|
||||||
|
}
|
@ -1,41 +0,0 @@
|
|||||||
use num_derive::FromPrimitive;
|
|
||||||
use num_traits::FromPrimitive;
|
|
||||||
|
|
||||||
use crate::arch;
|
|
||||||
|
|
||||||
#[derive(FromPrimitive, Debug)]
|
|
||||||
pub enum Syscall {
|
|
||||||
Exit,
|
|
||||||
Pid,
|
|
||||||
Message,
|
|
||||||
MemMap,
|
|
||||||
MemUnmap,
|
|
||||||
Resolve,
|
|
||||||
Spawn,
|
|
||||||
Version,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromPrimitive, Debug)]
|
|
||||||
pub enum SyscallStatus {
|
|
||||||
Success,
|
|
||||||
NoSuchCall,
|
|
||||||
NoSuchProcess,
|
|
||||||
NoSuchServer,
|
|
||||||
NoPermission,
|
|
||||||
OutOfMemory,
|
|
||||||
Aborted,
|
|
||||||
Unspecified,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnknownStatus {}
|
|
||||||
|
|
||||||
impl From<u64> for SyscallStatus {
|
|
||||||
fn from(value: u64) -> Self {
|
|
||||||
SyscallStatus::from_u64(value).unwrap_or(Self::Unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit(code: u64) -> SyscallStatus {
|
|
||||||
arch::syscall_impl::caller_syscall_1(Syscall::Exit as u64, code).into()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user