Compare commits
2 Commits
4c32bcfb35
...
dd596d5378
| Author | SHA1 | Date | |
|---|---|---|---|
|
dd596d5378
|
|||
|
b0f804e412
|
Generated
+4
@@ -9,6 +9,10 @@ dependencies = [
|
|||||||
"limine",
|
"limine",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "init"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "limine"
|
name = "limine"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ name = "dusk"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [".", "userspace/init"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
limine = "0.6.5"
|
limine = "0.6.5"
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ MEMORY ?= 512M
|
|||||||
# In MB
|
# In MB
|
||||||
ISO_SIZE ?= 512
|
ISO_SIZE ?= 512
|
||||||
QEMU_OPTS ?=
|
QEMU_OPTS ?=
|
||||||
#MKSQUASHFS_OPTS ?=
|
|
||||||
GDB ?=
|
GDB ?=
|
||||||
CPUS ?= 1
|
CPUS ?= 1
|
||||||
# FAT type
|
# FAT type
|
||||||
ESP_BITS ?= 32
|
ESP_BITS ?= 32
|
||||||
EXPORT_SYMBOLS = true
|
#EXPORT_SYMBOLS = true
|
||||||
|
|
||||||
ISO_PATH = ${ARTIFACTS_PATH}/iso_root
|
ISO_PATH = ${ARTIFACTS_PATH}/iso_root
|
||||||
#INITRAMFS_PATH = ${ARTIFACTS_PATH}/initramfs
|
INITRAMFS_PATH = ${ARTIFACTS_PATH}/initramfs
|
||||||
IMAGE_PATH = ${ARTIFACTS_PATH}/${IMAGE_NAME}
|
IMAGE_PATH = ${ARTIFACTS_PATH}/${IMAGE_NAME}
|
||||||
ESP_IMAGE = ${ARTIFACTS_PATH}/esp.img
|
ESP_IMAGE = ${ARTIFACTS_PATH}/esp.img
|
||||||
|
USERSPACE_CARGO_OPTS = --target ${ARCH}-unknown-none
|
||||||
CARGO_OPTS = -Zjson-target-spec --target=src/arch/${ARCH}/${ARCH}-unknown-none.json
|
CARGO_OPTS = -Zjson-target-spec --target=src/arch/${ARCH}/${ARCH}-unknown-none.json
|
||||||
QEMU_OPTS += -m ${MEMORY} -drive id=hd0,format=raw,file=${IMAGE_PATH}
|
QEMU_OPTS += -m ${MEMORY} -drive id=hd0,format=raw,file=${IMAGE_PATH}
|
||||||
LIMINE_BOOT_VARIATION = X64
|
LIMINE_BOOT_VARIATION = X64
|
||||||
@@ -27,6 +27,7 @@ KERNEL_FILE = target/${ARCH}-unknown-none/${MODE}/dusk.elf
|
|||||||
|
|
||||||
ifeq (${MODE},release)
|
ifeq (${MODE},release)
|
||||||
CARGO_OPTS += --release
|
CARGO_OPTS += --release
|
||||||
|
USERSPACE_CARGO_OPTS += --release
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq (${CPUS},1)
|
ifneq (${CPUS},1)
|
||||||
@@ -50,7 +51,7 @@ endif
|
|||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build: prepare-bin-files compile-bootloader compile-binaries run-scripts build-iso
|
build: prepare-bin-files compile-bootloader compile-binaries compile-initramfs build-iso
|
||||||
|
|
||||||
check:
|
check:
|
||||||
cargo check -Zjson-target-spec
|
cargo check -Zjson-target-spec
|
||||||
@@ -63,13 +64,16 @@ prepare-bin-files:
|
|||||||
# Make bin/ and bin/iso_root
|
# Make bin/ and bin/iso_root
|
||||||
mkdir -p ${ARTIFACTS_PATH}
|
mkdir -p ${ARTIFACTS_PATH}
|
||||||
mkdir -p ${ISO_PATH}
|
mkdir -p ${ISO_PATH}
|
||||||
# mkdir -p ${INITRAMFS_PATH}
|
mkdir -p ${INITRAMFS_PATH}
|
||||||
run-scripts:
|
|
||||||
# Place the build ID into the binary so it can be read at runtime
|
|
||||||
@HASH=$$(md5sum ${KERNEL_FILE} | cut -c1-12) && \
|
|
||||||
sed -i "s/__BUILD_ID__/$${HASH}/" ${KERNEL_FILE}
|
|
||||||
|
|
||||||
#python scripts/initramfs-test.py 100 ${INITRAMFS_PATH}/
|
compile-user:
|
||||||
|
RUSTFLAGS="-C relocation-model=static" cargo build --package init ${USERSPACE_CARGO_OPTS}
|
||||||
|
|
||||||
|
copy-initramfs-files: compile-user
|
||||||
|
cp -v target/${ARCH}-unknown-none/${MODE}/init ${INITRAMFS_PATH}/init.elf
|
||||||
|
|
||||||
|
compile-initramfs: copy-initramfs-files
|
||||||
|
(cd ${INITRAMFS_PATH} && find . -mindepth 1 | cpio -o -H newc) > ${ARTIFACTS_PATH}/initramfs.img
|
||||||
|
|
||||||
copy-iso-files:
|
copy-iso-files:
|
||||||
# Limine files
|
# Limine files
|
||||||
@@ -81,7 +85,7 @@ copy-iso-files:
|
|||||||
|
|
||||||
# OS files
|
# OS files
|
||||||
cp -v ${KERNEL_FILE} ${ISO_PATH}/boot
|
cp -v ${KERNEL_FILE} ${ISO_PATH}/boot
|
||||||
#cp -v ${ARTIFACTS_PATH}/initramfs.img ${ISO_PATH}/boot
|
cp -v ${ARTIFACTS_PATH}/initramfs.img ${ISO_PATH}/boot
|
||||||
|
|
||||||
build-esp: copy-iso-files
|
build-esp: copy-iso-files
|
||||||
# Create and populate formatted FAT image for ESP partition (130048 1K-blocks = ~127MiB)
|
# Create and populate formatted FAT image for ESP partition (130048 1K-blocks = ~127MiB)
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ timeout: 0
|
|||||||
protocol: limine
|
protocol: limine
|
||||||
|
|
||||||
path: boot():/boot/dusk.elf
|
path: boot():/boot/dusk.elf
|
||||||
|
module_path: boot():/boot/initramfs.img
|
||||||
|
|
||||||
|
|||||||
@@ -183,28 +183,6 @@ impl LocalApic {
|
|||||||
self.access.write(APIC_TIMER_INITIAL_COUNT, 0);
|
self.access.write(APIC_TIMER_INITIAL_COUNT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn delay_ticks(&self, count: u32) {
|
|
||||||
// if count == 0 {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// APIC_TIMER_COUNT.store(0, Ordering::SeqCst);
|
|
||||||
|
|
||||||
// self.access
|
|
||||||
// .write(APIC_LVT_TIMER, APIC_TIMER_VECTOR as u64 | APIC_LVT_MASKED);
|
|
||||||
// self.access.write(APIC_TIMER_DIVIDE_CONFIG, 0b11);
|
|
||||||
// self.access.write(APIC_TIMER_INITIAL_COUNT, count as u64);
|
|
||||||
// self.access.write(APIC_LVT_TIMER, APIC_TIMER_VECTOR as u64);
|
|
||||||
|
|
||||||
// while APIC_TIMER_COUNT.load(Ordering::SeqCst) == 0 {
|
|
||||||
// unsafe {
|
|
||||||
// core::arch::asm!("sti", "hlt", "cli", options(nomem, nostack));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// self.stop_timer();
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn id(&self) -> u32 {
|
pub fn id(&self) -> u32 {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-32
@@ -132,37 +132,6 @@ pub fn init_interrupt_controller(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// pub enum TimerError {
|
|
||||||
// DurationOverflow,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl InterruptController {
|
|
||||||
// pub fn delay(&self, duration: core::time::Duration) -> Result<(), TimerError> {
|
|
||||||
// let nanoseconds = duration.as_nanos();
|
|
||||||
|
|
||||||
// let ticks = (nanoseconds
|
|
||||||
// .checked_mul(self.local_timer_frequency as u128)
|
|
||||||
// .ok_or(TimerError::DurationOverflow)?
|
|
||||||
// + 999_999_999)
|
|
||||||
// / 1_000_000_000;
|
|
||||||
|
|
||||||
// if ticks == 0 {
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut remaining = ticks;
|
|
||||||
|
|
||||||
// while remaining > 0 {
|
|
||||||
// let chunk = remaining.min(u32::MAX as u128) as u32;
|
|
||||||
// self.local_apic.delay_ticks(chunk);
|
|
||||||
// remaining -= chunk as u128;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure:
|
/// The caller must ensure:
|
||||||
@@ -202,7 +171,6 @@ pub unsafe fn enter_user(
|
|||||||
"mov ds, {user_data_selector:x}",
|
"mov ds, {user_data_selector:x}",
|
||||||
"mov es, {user_data_selector:x}",
|
"mov es, {user_data_selector:x}",
|
||||||
"mov fs, {user_data_selector:x}",
|
"mov fs, {user_data_selector:x}",
|
||||||
"mov gs, {user_data_selector:x}",
|
|
||||||
|
|
||||||
"push {user_data_selector}",
|
"push {user_data_selector}",
|
||||||
"push {user_stack_pointer}",
|
"push {user_stack_pointer}",
|
||||||
@@ -227,6 +195,10 @@ pub unsafe fn enter_user(
|
|||||||
"xor r14, r14",
|
"xor r14, r14",
|
||||||
"xor r15, r15",
|
"xor r15, r15",
|
||||||
|
|
||||||
|
// Kernel GS is CpuLocal; leave it in IA32_KERNEL_GS_BASE so
|
||||||
|
// syscall_entry can recover it with SWAPGS.
|
||||||
|
"swapgs",
|
||||||
|
"mov gs, {user_data_selector:x}",
|
||||||
"iretq",
|
"iretq",
|
||||||
user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize,
|
user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize,
|
||||||
user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize,
|
user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize,
|
||||||
|
|||||||
+14
-41
@@ -390,16 +390,6 @@ impl PageTable {
|
|||||||
.ok_or(UnmapError::PageNotMapped)
|
.ok_or(UnmapError::PageNotMapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_private_tables(
|
|
||||||
frames: &mut [Option<OwnedFrame>; MAX_INTERMEDIATE_LEVELS],
|
|
||||||
count: usize,
|
|
||||||
allocator: &mut FrameAllocator,
|
|
||||||
) {
|
|
||||||
for frame in frames[..count].iter_mut().rev().filter_map(Option::take) {
|
|
||||||
unsafe { allocator.dealloc(frame) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_user_upgrades(
|
fn apply_user_upgrades(
|
||||||
&mut self,
|
&mut self,
|
||||||
upgrades: &[Option<EntryLocation>; MAX_INTERMEDIATE_LEVELS],
|
upgrades: &[Option<EntryLocation>; MAX_INTERMEDIATE_LEVELS],
|
||||||
@@ -509,34 +499,18 @@ impl PageTable {
|
|||||||
let private_table_count = levels.len() - missing_depth;
|
let private_table_count = levels.len() - missing_depth;
|
||||||
let mut private_tables: [Option<OwnedFrame>; MAX_INTERMEDIATE_LEVELS] =
|
let mut private_tables: [Option<OwnedFrame>; MAX_INTERMEDIATE_LEVELS] =
|
||||||
core::array::from_fn(|_| None);
|
core::array::from_fn(|_| None);
|
||||||
let mut allocated_count = 0;
|
|
||||||
|
|
||||||
while allocated_count < private_table_count {
|
|
||||||
let private_frame = match allocator.alloc() {
|
|
||||||
Some(frame) => frame,
|
|
||||||
None => {
|
|
||||||
Self::discard_private_tables(&mut private_tables, allocated_count, allocator);
|
|
||||||
return Err(MapError::OutOfFrames);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if PageTableEntry::new_table(
|
|
||||||
private_frame.frame_address(),
|
|
||||||
permissions.user_accessible,
|
|
||||||
self.config,
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
unsafe { allocator.dealloc(private_frame) };
|
|
||||||
Self::discard_private_tables(&mut private_tables, allocated_count, allocator);
|
|
||||||
return Err(MapError::PhysicalAddressTooLarge);
|
|
||||||
}
|
|
||||||
|
|
||||||
private_tables[allocated_count] = Some(private_frame);
|
|
||||||
allocated_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prepare_result = (|| {
|
let prepare_result = (|| {
|
||||||
|
for slot in &mut private_tables[..private_table_count] {
|
||||||
|
let frame = allocator.alloc().ok_or(MapError::OutOfFrames)?;
|
||||||
|
let address = frame.frame_address();
|
||||||
|
// Track ownership before validation so every error uses the same cleanup.
|
||||||
|
*slot = Some(frame);
|
||||||
|
|
||||||
|
PageTableEntry::new_table(address, permissions.user_accessible, self.config)
|
||||||
|
.map_err(|_| MapError::PhysicalAddressTooLarge)?;
|
||||||
|
}
|
||||||
|
|
||||||
for private_index in 0..private_table_count {
|
for private_index in 0..private_table_count {
|
||||||
let private_frame = private_tables[private_index]
|
let private_frame = private_tables[private_index]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -572,7 +546,9 @@ impl PageTable {
|
|||||||
let publication_entry = match prepare_result {
|
let publication_entry = match prepare_result {
|
||||||
Ok(entry) => entry,
|
Ok(entry) => entry,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
Self::discard_private_tables(&mut private_tables, allocated_count, allocator);
|
for frame in private_tables.into_iter().rev().flatten() {
|
||||||
|
unsafe { allocator.dealloc(frame) };
|
||||||
|
}
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -583,10 +559,7 @@ impl PageTable {
|
|||||||
[publication_location.index] = publication_entry;
|
[publication_location.index] = publication_entry;
|
||||||
|
|
||||||
// The published page table now owns these frames.
|
// The published page table now owns these frames.
|
||||||
for frame in private_tables[..allocated_count]
|
for frame in private_tables.into_iter().flatten() {
|
||||||
.iter_mut()
|
|
||||||
.flat_map(Option::take)
|
|
||||||
{
|
|
||||||
let _ = frame.into_raw();
|
let _ = frame.into_raw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,11 @@ const BINARY: u8 = 0;
|
|||||||
pub const PIT_FREQUENCY: u64 = 1_193_182;
|
pub const PIT_FREQUENCY: u64 = 1_193_182;
|
||||||
pub const PIT_CALIBRATION_COUNT: u16 = u16::MAX;
|
pub const PIT_CALIBRATION_COUNT: u16 = u16::MAX;
|
||||||
|
|
||||||
pub struct Pit;
|
pub fn start_pit_one_shot(count: u16) {
|
||||||
|
unsafe {
|
||||||
|
write_u8(PIT_COMMAND, CHANNEL_0 | LOW_HIGH | MODE_0 | BINARY);
|
||||||
|
|
||||||
impl Pit {
|
write_u8(PIT_CHANNEL_0, count as u8);
|
||||||
pub fn start_one_shot(count: u16) {
|
write_u8(PIT_CHANNEL_0, (count >> 8) as u8);
|
||||||
unsafe {
|
|
||||||
write_u8(PIT_COMMAND, CHANNEL_0 | LOW_HIGH | MODE_0 | BINARY);
|
|
||||||
|
|
||||||
write_u8(PIT_CHANNEL_0, count as u8);
|
|
||||||
write_u8(PIT_CHANNEL_0, (count >> 8) as u8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ pub fn init(cpu_local: *const CpuLocal) {
|
|||||||
write_msr(IA32_FMASK, RFLAGS_MASK);
|
write_msr(IA32_FMASK, RFLAGS_MASK);
|
||||||
write_msr(IA32_GS_BASE, 0);
|
write_msr(IA32_GS_BASE, 0);
|
||||||
write_msr(IA32_KERNEL_GS_BASE, cpu_local as u64);
|
write_msr(IA32_KERNEL_GS_BASE, cpu_local as u64);
|
||||||
|
|
||||||
|
// Kernel code always runs with GS pointing at CpuLocal. User entry
|
||||||
|
// swaps this into IA32_KERNEL_GS_BASE before transitioning to ring 3.
|
||||||
|
asm!("swapgs", options(nostack, preserves_flags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +107,7 @@ unsafe extern "C" fn syscall_entry() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn syscall_dispatch(frame: &mut SyscallFrame) {
|
extern "C" fn syscall_dispatch(frame: &mut SyscallFrame) {
|
||||||
let ret = crate::task::syscall::handle(
|
let ret = crate::syscall::handle(
|
||||||
frame.rax, frame.rdi, frame.rsi, frame.rdx, frame.r10, frame.r8, frame.r9,
|
frame.rax, frame.rdi, frame.rsi, frame.rdx, frame.r10, frame.r8, frame.r9,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
io_apic::IoApic,
|
io_apic::IoApic,
|
||||||
x86_64::{
|
x86_64::{
|
||||||
interrupts::enable_interrupts,
|
interrupts::enable_interrupts,
|
||||||
pit::{PIT_CALIBRATION_COUNT, PIT_FREQUENCY, Pit},
|
pit::{PIT_CALIBRATION_COUNT, PIT_FREQUENCY, start_pit_one_shot},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
platform::acpi::IsaIrqRoute,
|
platform::acpi::IsaIrqRoute,
|
||||||
@@ -37,7 +37,7 @@ pub fn calibrate_local_apic(
|
|||||||
.map_err(|_| TimerCalibrationError::IoApicNotHandled)?;
|
.map_err(|_| TimerCalibrationError::IoApicNotHandled)?;
|
||||||
local_apic.start_calibration_counter();
|
local_apic.start_calibration_counter();
|
||||||
|
|
||||||
Pit::start_one_shot(PIT_CALIBRATION_COUNT);
|
start_pit_one_shot(PIT_CALIBRATION_COUNT);
|
||||||
|
|
||||||
enable_interrupts();
|
enable_interrupts();
|
||||||
|
|
||||||
|
|||||||
+40
-5
@@ -1,13 +1,13 @@
|
|||||||
use ::limine as limine_api;
|
use ::limine as limine_api;
|
||||||
use limine::paging::PagingMode;
|
use limine::paging::PagingMode;
|
||||||
use limine::request::{PagingModeRequest, RsdpRequest};
|
use limine::request::{ExecutableCmdlineRequest, ModulesRequest, PagingModeRequest, RsdpRequest};
|
||||||
|
|
||||||
use limine_api::request::{ExecutableAddressRequest, HhdmRequest, MemmapRequest};
|
use limine_api::request::{ExecutableAddressRequest, HhdmRequest, MemmapRequest};
|
||||||
use limine_api::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
|
use limine_api::{BaseRevision, RequestsEndMarker, RequestsStartMarker};
|
||||||
|
|
||||||
use crate::memory::{
|
use crate::memory::{
|
||||||
KernelMemoryLayout, KernelSegment, MemoryMap, MemoryRegion, MemoryRegionKind, PagePermissions,
|
BootString, InitramfsImage, KernelMemoryLayout, KernelSegment, MemoryMap, MemoryRegion,
|
||||||
PhysicalAddr, VirtualAddr,
|
MemoryRegionKind, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sets the base revision to the latest revision supported by the crate.
|
/// Sets the base revision to the latest revision supported by the crate.
|
||||||
@@ -22,6 +22,14 @@ static BASE_REVISION: BaseRevision = BaseRevision::new();
|
|||||||
#[unsafe(link_section = ".requests")]
|
#[unsafe(link_section = ".requests")]
|
||||||
static KERNEL_ADDRESS_REQUEST: ExecutableAddressRequest = ExecutableAddressRequest::new();
|
static KERNEL_ADDRESS_REQUEST: ExecutableAddressRequest = ExecutableAddressRequest::new();
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
#[unsafe(link_section = ".requests")]
|
||||||
|
static KERNEL_CMDLINE_REQUEST: ExecutableCmdlineRequest = ExecutableCmdlineRequest::new();
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
#[unsafe(link_section = ".requests")]
|
||||||
|
static KERNEL_MODULE_REQUEST: ModulesRequest = ModulesRequest::new();
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[unsafe(link_section = ".requests")]
|
#[unsafe(link_section = ".requests")]
|
||||||
static HHDM_REQUEST: HhdmRequest = HhdmRequest::new();
|
static HHDM_REQUEST: HhdmRequest = HhdmRequest::new();
|
||||||
@@ -58,8 +66,12 @@ unsafe extern "C" {
|
|||||||
static __data_end: u64;
|
static __data_end: u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_COMMAND_LINE_LENGTH: usize = 512;
|
||||||
|
|
||||||
pub struct BootInfo {
|
pub struct BootInfo {
|
||||||
pub kernel_layout: KernelMemoryLayout,
|
pub kernel_layout: KernelMemoryLayout,
|
||||||
|
pub command_line: BootString<MAX_COMMAND_LINE_LENGTH>,
|
||||||
|
pub initramfs: InitramfsImage,
|
||||||
pub hhdm_offset: usize,
|
pub hhdm_offset: usize,
|
||||||
pub memory_map: MemoryMap,
|
pub memory_map: MemoryMap,
|
||||||
pub rsdp: VirtualAddr,
|
pub rsdp: VirtualAddr,
|
||||||
@@ -75,6 +87,9 @@ impl BootInfo {
|
|||||||
pub enum BootError {
|
pub enum BootError {
|
||||||
UnsupportedBaseRevision,
|
UnsupportedBaseRevision,
|
||||||
FailedToGetKernelAddress,
|
FailedToGetKernelAddress,
|
||||||
|
FailedToGetKernelCmdline,
|
||||||
|
FailedToGetModules,
|
||||||
|
FailedToGetInitramfs,
|
||||||
FailedToGetHHDMAddress,
|
FailedToGetHHDMAddress,
|
||||||
FailedToGetMemmap,
|
FailedToGetMemmap,
|
||||||
TooManyMemoryRegions,
|
TooManyMemoryRegions,
|
||||||
@@ -90,10 +105,28 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
let kernel_address = KERNEL_ADDRESS_REQUEST
|
let kernel_address = KERNEL_ADDRESS_REQUEST
|
||||||
.response()
|
.response()
|
||||||
.ok_or(BootError::FailedToGetKernelAddress)?;
|
.ok_or(BootError::FailedToGetKernelAddress)?;
|
||||||
|
let kernel_cmdline = KERNEL_CMDLINE_REQUEST
|
||||||
|
.response()
|
||||||
|
.ok_or(BootError::FailedToGetKernelCmdline)?;
|
||||||
|
let command_line = BootString::from_bytes(kernel_cmdline.cmdline().as_bytes());
|
||||||
|
|
||||||
|
let modules = KERNEL_MODULE_REQUEST
|
||||||
|
.response()
|
||||||
|
.ok_or(BootError::FailedToGetModules)?;
|
||||||
|
let initramfs = modules
|
||||||
|
.modules()
|
||||||
|
.get(0)
|
||||||
|
.map(|module| {
|
||||||
|
let start = VirtualAddr::new(module.data().as_ptr() as usize);
|
||||||
|
let length = module.data().len();
|
||||||
|
InitramfsImage { start, length }
|
||||||
|
})
|
||||||
|
.ok_or(BootError::FailedToGetInitramfs)?;
|
||||||
|
|
||||||
let hhdm_offset = HHDM_REQUEST
|
let hhdm_offset = HHDM_REQUEST
|
||||||
.response()
|
.response()
|
||||||
.ok_or(BootError::FailedToGetHHDMAddress)?
|
.ok_or(BootError::FailedToGetHHDMAddress)?
|
||||||
.offset;
|
.offset as usize;
|
||||||
|
|
||||||
let rsdp = RSDP_REQUEST.response().ok_or(BootError::FailedToGetRsdp)?;
|
let rsdp = RSDP_REQUEST.response().ok_or(BootError::FailedToGetRsdp)?;
|
||||||
|
|
||||||
@@ -204,7 +237,9 @@ pub fn load_boot_info() -> Result<BootInfo, BootError> {
|
|||||||
kernel_data_segment,
|
kernel_data_segment,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
hhdm_offset: hhdm_offset as usize,
|
command_line,
|
||||||
|
initramfs,
|
||||||
|
hhdm_offset,
|
||||||
memory_map,
|
memory_map,
|
||||||
rsdp: VirtualAddr::new(rsdp.address as usize),
|
rsdp: VirtualAddr::new(rsdp.address as usize),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -160,6 +160,12 @@ pub fn init() -> Result<(), SerialPortError> {
|
|||||||
com1().init()
|
com1().init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_bytes(bytes: &[u8]) {
|
||||||
|
for byte in bytes {
|
||||||
|
com1().write_byte(*byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print(args: core::fmt::Arguments) {
|
pub fn print(args: core::fmt::Arguments) {
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// CPIO newc
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Header {
|
||||||
|
pub c_magic: [u8; 6],
|
||||||
|
pub c_ino: [u8; 8],
|
||||||
|
pub c_mode: [u8; 8],
|
||||||
|
pub c_uid: [u8; 8],
|
||||||
|
pub c_gid: [u8; 8],
|
||||||
|
pub c_nlink: [u8; 8],
|
||||||
|
pub c_mtime: [u8; 8],
|
||||||
|
pub c_filesize: [u8; 8],
|
||||||
|
pub c_devmajor: [u8; 8],
|
||||||
|
pub c_devminor: [u8; 8],
|
||||||
|
pub c_rdevmajor: [u8; 8],
|
||||||
|
pub c_rdevminor: [u8; 8],
|
||||||
|
pub c_namesize: [u8; 8],
|
||||||
|
pub c_check: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
|
if bytes.len() < core::mem::size_of::<Header>() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let header: Header = unsafe { core::ptr::read(bytes.as_ptr() as *const Header) };
|
||||||
|
|
||||||
|
if header.c_magic != *b"070701" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> {
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
while offset + core::mem::size_of::<Header>() <= archive.len() {
|
||||||
|
let header = Header::from_bytes(&archive[offset..])?;
|
||||||
|
let header_start = offset;
|
||||||
|
offset += core::mem::size_of::<Header>();
|
||||||
|
|
||||||
|
let file_len =
|
||||||
|
usize::from_str_radix(core::str::from_utf8(&header.c_filesize).ok()?, 16).ok()?;
|
||||||
|
let name_len =
|
||||||
|
usize::from_str_radix(core::str::from_utf8(&header.c_namesize).ok()?, 16).ok()?;
|
||||||
|
|
||||||
|
if offset + name_len > archive.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_bytes = &archive[offset..offset + name_len];
|
||||||
|
let name = core::str::from_utf8(name_bytes)
|
||||||
|
.ok()?
|
||||||
|
.trim_end_matches('\0');
|
||||||
|
|
||||||
|
if name == "TRAILER!!!" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_start = header_start + ((core::mem::size_of::<Header>() + name_len + 3) & !3);
|
||||||
|
if data_start + file_len > archive.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == target {
|
||||||
|
return Some(&archive[data_start..data_start + file_len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = data_start + ((file_len + 3) & !3);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ElfIsa {
|
||||||
|
None,
|
||||||
|
Sparc,
|
||||||
|
X86,
|
||||||
|
Mips,
|
||||||
|
Ppc,
|
||||||
|
Arm,
|
||||||
|
SuperH,
|
||||||
|
Ia64,
|
||||||
|
Amd64,
|
||||||
|
AArch64,
|
||||||
|
Riscv,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfIsa {
|
||||||
|
fn from_u16(value: u16) -> Result<Self, ElfError> {
|
||||||
|
match value {
|
||||||
|
0x00 => Ok(Self::None),
|
||||||
|
0x02 => Ok(Self::Sparc),
|
||||||
|
0x03 => Ok(Self::X86),
|
||||||
|
0x08 => Ok(Self::Mips),
|
||||||
|
0x14 => Ok(Self::Ppc),
|
||||||
|
0x28 => Ok(Self::Arm),
|
||||||
|
0x2A => Ok(Self::SuperH),
|
||||||
|
0x32 => Ok(Self::Ia64),
|
||||||
|
0x3E => Ok(Self::Amd64),
|
||||||
|
0xB7 => Ok(Self::AArch64),
|
||||||
|
0xF3 => Ok(Self::Riscv),
|
||||||
|
_ => Err(ElfError::InvalidElf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ElfClass {
|
||||||
|
Elf32,
|
||||||
|
Elf64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfClass {
|
||||||
|
fn from_u8(value: u8) -> Result<Self, ElfError> {
|
||||||
|
match value {
|
||||||
|
1 => Ok(Self::Elf32),
|
||||||
|
2 => Ok(Self::Elf64),
|
||||||
|
_ => Err(ElfError::InvalidElf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn header_size(self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::Elf32 => 52,
|
||||||
|
Self::Elf64 => 64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Endianness {
|
||||||
|
Little,
|
||||||
|
Big,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Endianness {
|
||||||
|
fn from_u8(value: u8) -> Result<Self, ElfError> {
|
||||||
|
match value {
|
||||||
|
1 => Ok(Self::Little),
|
||||||
|
2 => Ok(Self::Big),
|
||||||
|
_ => Err(ElfError::InvalidElf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ElfType {
|
||||||
|
Relocatable = 1,
|
||||||
|
Executable = 2,
|
||||||
|
SharedObject = 3,
|
||||||
|
Core = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfType {
|
||||||
|
fn from_u16(value: u16) -> Result<Self, ElfError> {
|
||||||
|
match value {
|
||||||
|
1 => Ok(Self::Relocatable),
|
||||||
|
2 => Ok(Self::Executable),
|
||||||
|
3 => Ok(Self::SharedObject),
|
||||||
|
4 => Ok(Self::Core),
|
||||||
|
_ => Err(ElfError::InvalidElf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct ElfHeader {
|
||||||
|
magic: [u8; 4],
|
||||||
|
pub class: ElfClass,
|
||||||
|
endianness: Endianness,
|
||||||
|
version: u8,
|
||||||
|
os_abi: u8,
|
||||||
|
_reserved: [u8; 8],
|
||||||
|
pub object_type: ElfType,
|
||||||
|
pub machine: ElfIsa,
|
||||||
|
version_1: u32,
|
||||||
|
entry: u64, // 2115136
|
||||||
|
program_header_offset: u64, // 64
|
||||||
|
section_header_offset: u64, // 2759752
|
||||||
|
flags: u32, // 0
|
||||||
|
header_size: u16, // 64
|
||||||
|
program_header_entry_size: u16, // 56
|
||||||
|
program_header_count: u16, // 6
|
||||||
|
section_header_entry_size: u16, // 64
|
||||||
|
section_header_count: u16, // 17
|
||||||
|
section_name_index: u16, // 15
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ElfError {
|
||||||
|
InvalidElf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfHeader {
|
||||||
|
pub fn parse(bytes: &[u8]) -> Result<Self, ElfError> {
|
||||||
|
let mut reader = Reader::new(bytes);
|
||||||
|
|
||||||
|
let magic = reader.read_array()?;
|
||||||
|
if magic != *b"\x7fELF" {
|
||||||
|
return Err(ElfError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let class = ElfClass::from_u8(reader.read_u8()?)?;
|
||||||
|
let endianness = Endianness::from_u8(reader.read_u8()?)?;
|
||||||
|
reader.set_endianness(endianness);
|
||||||
|
|
||||||
|
let version = reader.read_u8()?;
|
||||||
|
let os_abi = reader.read_u8()?;
|
||||||
|
let reserved = reader.read_array()?;
|
||||||
|
let object_type = ElfType::from_u16(reader.read_u16()?)?;
|
||||||
|
let machine = ElfIsa::from_u16(reader.read_u16()?)?;
|
||||||
|
let version_1 = reader.read_u32()?;
|
||||||
|
let entry = reader.read_word(class)?;
|
||||||
|
let program_header_offset = reader.read_word(class)?;
|
||||||
|
let section_header_offset = reader.read_word(class)?;
|
||||||
|
let flags = reader.read_u32()?;
|
||||||
|
let header_size = reader.read_u16()?;
|
||||||
|
let program_header_entry_size = reader.read_u16()?;
|
||||||
|
let program_header_count = reader.read_u16()?;
|
||||||
|
let section_header_entry_size = reader.read_u16()?;
|
||||||
|
let section_header_count = reader.read_u16()?;
|
||||||
|
let section_name_index = reader.read_u16()?;
|
||||||
|
|
||||||
|
if header_size != class.header_size() {
|
||||||
|
return Err(ElfError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
magic,
|
||||||
|
class,
|
||||||
|
endianness,
|
||||||
|
version,
|
||||||
|
os_abi,
|
||||||
|
_reserved: reserved,
|
||||||
|
object_type,
|
||||||
|
machine,
|
||||||
|
version_1,
|
||||||
|
entry,
|
||||||
|
program_header_offset,
|
||||||
|
section_header_offset,
|
||||||
|
flags,
|
||||||
|
header_size,
|
||||||
|
program_header_entry_size,
|
||||||
|
program_header_count,
|
||||||
|
section_header_entry_size,
|
||||||
|
section_header_count,
|
||||||
|
section_name_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ProgramHeaderType {
|
||||||
|
Null = 0,
|
||||||
|
Load = 1,
|
||||||
|
Dynamic = 2,
|
||||||
|
Interpreter = 3,
|
||||||
|
Note = 4,
|
||||||
|
Shlib = 5,
|
||||||
|
Phdr = 6,
|
||||||
|
GnuStack = 0x6474e551,
|
||||||
|
Relro = 0x6474e552,
|
||||||
|
Other(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramHeaderType {
|
||||||
|
fn from_u32(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::Null,
|
||||||
|
1 => Self::Load,
|
||||||
|
2 => Self::Dynamic,
|
||||||
|
3 => Self::Interpreter,
|
||||||
|
4 => Self::Note,
|
||||||
|
5 => Self::Shlib,
|
||||||
|
6 => Self::Phdr,
|
||||||
|
0x6474e551 => Self::GnuStack,
|
||||||
|
0x6474e552 => Self::Relro,
|
||||||
|
_ => Self::Other(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProgramHeader {
|
||||||
|
pub segment_type: ProgramHeaderType,
|
||||||
|
pub flags: u32,
|
||||||
|
pub file_offset: u64,
|
||||||
|
pub virtual_address: u64,
|
||||||
|
_physical_address: u64,
|
||||||
|
pub file_size: u64,
|
||||||
|
pub memory_size: u64,
|
||||||
|
pub alignment: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramHeader {
|
||||||
|
pub fn parse(bytes: &[u8], class: ElfClass, endianness: Endianness) -> Result<Self, ElfError> {
|
||||||
|
let mut reader = Reader::new(bytes);
|
||||||
|
reader.set_endianness(endianness);
|
||||||
|
|
||||||
|
let segment_type = ProgramHeaderType::from_u32(reader.read_u32()?);
|
||||||
|
|
||||||
|
let flags = if class == ElfClass::Elf64 {
|
||||||
|
reader.read_u32()?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_offset = reader.read_word(class)?;
|
||||||
|
let virtual_address = reader.read_word(class)?;
|
||||||
|
let physical_address = reader.read_word(class)?;
|
||||||
|
let file_size = reader.read_word(class)?;
|
||||||
|
let memory_size = reader.read_word(class)?;
|
||||||
|
|
||||||
|
let flags = if class == ElfClass::Elf32 {
|
||||||
|
reader.read_u32()?
|
||||||
|
} else {
|
||||||
|
flags
|
||||||
|
};
|
||||||
|
|
||||||
|
let alignment = reader.read_word(class)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
segment_type,
|
||||||
|
flags,
|
||||||
|
file_offset,
|
||||||
|
virtual_address,
|
||||||
|
_physical_address: physical_address,
|
||||||
|
file_size,
|
||||||
|
memory_size,
|
||||||
|
alignment,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Elf<'a> {
|
||||||
|
bytes: &'a [u8],
|
||||||
|
header: ElfHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Elf<'a> {
|
||||||
|
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
|
||||||
|
let header = ElfHeader::parse(bytes)?;
|
||||||
|
|
||||||
|
Ok(Self { bytes, header })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program_headers(&self) -> Result<ProgramHeaders<'_>, ElfError> {
|
||||||
|
let offset =
|
||||||
|
usize::try_from(self.header.program_header_offset).map_err(|_| ElfError::InvalidElf)?;
|
||||||
|
let entry_size = usize::from(self.header.program_header_entry_size);
|
||||||
|
let count = usize::from(self.header.program_header_count);
|
||||||
|
|
||||||
|
let expected_entry_size = match self.header.class {
|
||||||
|
ElfClass::Elf32 => 32,
|
||||||
|
ElfClass::Elf64 => 56,
|
||||||
|
};
|
||||||
|
|
||||||
|
if entry_size != expected_entry_size {
|
||||||
|
return Err(ElfError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let table_size = entry_size.checked_mul(count).ok_or(ElfError::InvalidElf)?;
|
||||||
|
let table_end = offset.checked_add(table_size).ok_or(ElfError::InvalidElf)?;
|
||||||
|
let bytes = self
|
||||||
|
.bytes
|
||||||
|
.get(offset..table_end)
|
||||||
|
.ok_or(ElfError::InvalidElf)?;
|
||||||
|
|
||||||
|
Ok(ProgramHeaders {
|
||||||
|
bytes,
|
||||||
|
class: self.header.class,
|
||||||
|
endianness: self.header.endianness,
|
||||||
|
entry_size,
|
||||||
|
remaining: count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
self.bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn machine(&self) -> ElfIsa {
|
||||||
|
self.header.machine
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entry(&self) -> usize {
|
||||||
|
self.header.entry as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProgramHeaders<'a> {
|
||||||
|
bytes: &'a [u8],
|
||||||
|
class: ElfClass,
|
||||||
|
endianness: Endianness,
|
||||||
|
entry_size: usize,
|
||||||
|
remaining: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ProgramHeaders<'a> {
|
||||||
|
type Item = Result<ProgramHeader, ElfError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.remaining == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry = match self.bytes.get(..self.entry_size) {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => {
|
||||||
|
self.remaining = 0;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.bytes = &self.bytes[self.entry_size..];
|
||||||
|
self.remaining -= 1;
|
||||||
|
|
||||||
|
Some(ProgramHeader::parse(entry, self.class, self.endianness))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.remaining, Some(self.remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for ProgramHeaders<'_> {}
|
||||||
|
|
||||||
|
struct Reader<'a> {
|
||||||
|
bytes: &'a [u8],
|
||||||
|
offset: usize,
|
||||||
|
endianness: Endianness,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Reader<'a> {
|
||||||
|
const fn new(bytes: &'a [u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
bytes,
|
||||||
|
offset: 0,
|
||||||
|
endianness: Endianness::Little,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_endianness(&mut self, endianness: Endianness) {
|
||||||
|
self.endianness = endianness;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ElfError> {
|
||||||
|
let end = self.offset.checked_add(N).ok_or(ElfError::InvalidElf)?;
|
||||||
|
let bytes = self
|
||||||
|
.bytes
|
||||||
|
.get(self.offset..end)
|
||||||
|
.ok_or(ElfError::InvalidElf)?;
|
||||||
|
self.offset = end;
|
||||||
|
|
||||||
|
bytes.try_into().map_err(|_| ElfError::InvalidElf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u8(&mut self) -> Result<u8, ElfError> {
|
||||||
|
Ok(self.read_array::<1>()?[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u16(&mut self) -> Result<u16, ElfError> {
|
||||||
|
let bytes = self.read_array()?;
|
||||||
|
Ok(match self.endianness {
|
||||||
|
Endianness::Little => u16::from_le_bytes(bytes),
|
||||||
|
Endianness::Big => u16::from_be_bytes(bytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32(&mut self) -> Result<u32, ElfError> {
|
||||||
|
let bytes = self.read_array()?;
|
||||||
|
Ok(match self.endianness {
|
||||||
|
Endianness::Little => u32::from_le_bytes(bytes),
|
||||||
|
Endianness::Big => u32::from_be_bytes(bytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u64(&mut self) -> Result<u64, ElfError> {
|
||||||
|
let bytes = self.read_array()?;
|
||||||
|
Ok(match self.endianness {
|
||||||
|
Endianness::Little => u64::from_le_bytes(bytes),
|
||||||
|
Endianness::Big => u64::from_be_bytes(bytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_word(&mut self, class: ElfClass) -> Result<u64, ElfError> {
|
||||||
|
match class {
|
||||||
|
ElfClass::Elf32 => Ok(u64::from(self.read_u32()?)),
|
||||||
|
ElfClass::Elf64 => self.read_u64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod cpio;
|
||||||
|
pub mod elf;
|
||||||
+27
-58
@@ -6,15 +6,17 @@
|
|||||||
mod arch;
|
mod arch;
|
||||||
mod boot;
|
mod boot;
|
||||||
mod debug;
|
mod debug;
|
||||||
|
mod format;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
mod syscall;
|
||||||
mod task;
|
mod task;
|
||||||
|
|
||||||
|
use core::arch::global_asm;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::serial,
|
debug::serial,
|
||||||
memory::{
|
memory::{AddressSpace, KernelStackPool, MemoryRegionKind, UserStack},
|
||||||
AddressSpace, KernelStackPool, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr,
|
|
||||||
},
|
|
||||||
task::tcb::Tcb,
|
task::tcb::Tcb,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +119,9 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
|||||||
MemoryRegionKind::BootloaderReclaimable,
|
MemoryRegionKind::BootloaderReclaimable,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let init_code = format::cpio::find_file(boot_info.initramfs.data(), "init.elf")
|
||||||
|
.expect("Failed to load init program from initramfs");
|
||||||
|
|
||||||
println!("Initializing local ACPI...",);
|
println!("Initializing local ACPI...",);
|
||||||
|
|
||||||
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
|
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
|
||||||
@@ -134,69 +139,33 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
|||||||
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
||||||
.expect("failed to initialize interrupt controller");
|
.expect("failed to initialize interrupt controller");
|
||||||
|
|
||||||
println!("Creating user address space...");
|
let user_kernel_stack = kernel_stack_pool
|
||||||
|
|
||||||
let task_kernel_stack = kernel_stack_pool
|
|
||||||
.allocate(&mut address_space, &mut allocator)
|
.allocate(&mut address_space, &mut allocator)
|
||||||
.expect("failed to allocate user task stack");
|
.expect("failed to allocate task kernel stack");
|
||||||
|
|
||||||
let mut user_addr_space = address_space
|
let mut user_address_space = address_space
|
||||||
.new_user(&mut allocator)
|
.new_user(&mut allocator)
|
||||||
.expect("failed to create user address space");
|
.expect("failed to create user address space");
|
||||||
|
|
||||||
println!("Allocating user stack...");
|
let image = task::loader::load_elf(
|
||||||
|
init_code,
|
||||||
|
&mut user_address_space,
|
||||||
|
&mut allocator,
|
||||||
|
direct_map,
|
||||||
|
);
|
||||||
|
|
||||||
let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator)
|
let user_stack = UserStack::allocate(&mut user_address_space, &mut allocator)
|
||||||
.expect("failed to allocate user stack");
|
.expect("failed to allocate user stack");
|
||||||
|
let task = Tcb::new_user(
|
||||||
|
0, // overwritten by add_task for now
|
||||||
|
user_address_space,
|
||||||
|
user_kernel_stack,
|
||||||
|
image.expect("Failed to load elf"),
|
||||||
|
user_stack.top(),
|
||||||
|
);
|
||||||
|
|
||||||
let user_instruction_pointer = VirtualAddr::new(0x8000);
|
task::scheduler::add_task(task).expect("scheduler is full");
|
||||||
let user_code_page = allocator
|
task::scheduler::start();
|
||||||
.alloc()
|
|
||||||
.expect("failed to allocate user code page")
|
|
||||||
.frame_address();
|
|
||||||
user_addr_space
|
|
||||||
.map(
|
|
||||||
user_code_page.start_address(),
|
|
||||||
user_instruction_pointer,
|
|
||||||
PagePermissions::new(true, true, true),
|
|
||||||
&mut allocator,
|
|
||||||
memory::CachePolicy::WriteBack,
|
|
||||||
)
|
|
||||||
.expect("failed to map user code page");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let user_code: &[u8] = &[
|
|
||||||
0xCC, // INT3
|
|
||||||
0x0F, 0x05, // SYSCALL
|
|
||||||
0xEB, 0xFE, // JMP -2 (loop forever if exit returns)
|
|
||||||
];
|
|
||||||
|
|
||||||
let user_code_virtual = direct_map
|
|
||||||
.translate(user_code_page.start_address())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("Copying user code to {:#X}", user_code_virtual.as_usize());
|
|
||||||
|
|
||||||
core::ptr::copy_nonoverlapping(
|
|
||||||
user_code.as_ptr(),
|
|
||||||
user_code_virtual.as_mut_ptr::<u8>(),
|
|
||||||
user_code.len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("Activating user address space...");
|
|
||||||
|
|
||||||
let user_tcb = Tcb::new_user(
|
|
||||||
0,
|
|
||||||
user_addr_space,
|
|
||||||
task_kernel_stack,
|
|
||||||
user_instruction_pointer,
|
|
||||||
user_stack.top(),
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("Running first user task {user_tcb:?}...");
|
|
||||||
|
|
||||||
task::tcb::run_first_user(&user_tcb);
|
|
||||||
}
|
|
||||||
|
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig},
|
arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig},
|
||||||
memory::{
|
memory::{
|
||||||
CachePolicy, DirectMap, FRAME_SIZE, FrameAddr, FrameAllocator, KernelMemoryLayout,
|
CachePolicy, DirectMap, FRAME_SIZE, FrameAddr, FrameAllocator, KernelMemoryLayout,
|
||||||
MemoryRegion, MemoryRegionKind, PagePermissions, PhysicalAddr, VirtualAddr,
|
MemoryRegion, MemoryRegionKind, PagePermissions, PhysicalAddr, USER_SPACE_END, VirtualAddr,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,13 +113,18 @@ impl AddressSpace {
|
|||||||
// undermind the permissions of the explicitly mapped kernel image
|
// undermind the permissions of the explicitly mapped kernel image
|
||||||
if matches!(
|
if matches!(
|
||||||
region.kind,
|
region.kind,
|
||||||
MemoryRegionKind::Reserved
|
MemoryRegionKind::Reserved | MemoryRegionKind::BadMemory
|
||||||
| MemoryRegionKind::BadMemory
|
|
||||||
| MemoryRegionKind::KernelAndModules
|
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we shouldnt HHDM the kernel image, but we should map modules
|
||||||
|
if region.kind == MemoryRegionKind::KernelAndModules
|
||||||
|
&& region.start == layout.segments[0].physical_base
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let cache_policy = if matches!(
|
let cache_policy = if matches!(
|
||||||
region.kind,
|
region.kind,
|
||||||
MemoryRegionKind::MappedReserved | MemoryRegionKind::Framebuffer
|
MemoryRegionKind::MappedReserved | MemoryRegionKind::Framebuffer
|
||||||
@@ -195,7 +200,7 @@ impl AddressSpace {
|
|||||||
let global = self.kind == AddressSpaceKind::Kernel;
|
let global = self.kind == AddressSpaceKind::Kernel;
|
||||||
|
|
||||||
if self.kind == AddressSpaceKind::User {
|
if self.kind == AddressSpaceKind::User {
|
||||||
if virtual_addr.as_usize() >= 0x0000_8000_0000_0000 {
|
if virtual_addr.as_usize() >= USER_SPACE_END.as_usize() {
|
||||||
return Err(MapError::InvalidUserAddress);
|
return Err(MapError::InvalidUserAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,48 @@
|
|||||||
mod address_space;
|
mod address_space;
|
||||||
mod frame;
|
mod frame;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
mod user;
|
||||||
|
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||||
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame};
|
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame};
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack};
|
pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack};
|
||||||
|
#[allow(unused)]
|
||||||
|
pub use user::*;
|
||||||
|
|
||||||
|
pub struct BootString<const N: usize> {
|
||||||
|
bytes: [u8; N],
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> BootString<N> {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Self {
|
||||||
|
let len = bytes.len();
|
||||||
|
let mut boot_string = Self { bytes: [0; N], len };
|
||||||
|
boot_string.bytes[..len].copy_from_slice(bytes);
|
||||||
|
boot_string
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
core::str::from_utf8(&self.bytes[..self.len]).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_MODULE_PATH_LENGTH: usize = 256;
|
||||||
|
|
||||||
|
pub struct InitramfsImage {
|
||||||
|
pub start: VirtualAddr,
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitramfsImage {
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
unsafe { core::slice::from_raw_parts(self.start.as_ptr(), self.length) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct KernelSegment {
|
pub struct KernelSegment {
|
||||||
pub physical_base: PhysicalAddr,
|
pub physical_base: PhysicalAddr,
|
||||||
@@ -72,6 +108,14 @@ impl VirtualAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Add<usize> for VirtualAddr {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: usize) -> Self::Output {
|
||||||
|
Self(self.0 + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum MemoryRegionKind {
|
pub enum MemoryRegionKind {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
use crate::{memory::VirtualAddr, syscall::Status};
|
||||||
|
|
||||||
|
pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000);
|
||||||
|
|
||||||
|
pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
||||||
|
// TODO: guard against unmapped pages
|
||||||
|
let end = src
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(dst.len())
|
||||||
|
.ok_or(Status::BadAddress)?;
|
||||||
|
if end > USER_SPACE_END.as_usize() {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), dst.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
|
||||||
|
let end = dst
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(src.len())
|
||||||
|
.ok_or(Status::BadAddress)?;
|
||||||
|
if end > USER_SPACE_END.as_usize() {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr::<u8>(), src.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> {
|
||||||
|
if dst.as_usize() % core::mem::align_of::<T>() != 0 {
|
||||||
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
|
let end = dst
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(core::mem::size_of::<T>())
|
||||||
|
.ok_or(Status::BadAddress)?;
|
||||||
|
if end > USER_SPACE_END.as_usize() {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(dst.as_mut_ptr::<T>()).write(*val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> {
|
||||||
|
if src.as_usize() % core::mem::align_of::<T>() != 0 {
|
||||||
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
|
let end = src
|
||||||
|
.as_usize()
|
||||||
|
.checked_add(core::mem::size_of::<T>())
|
||||||
|
.ok_or(Status::BadAddress)?;
|
||||||
|
if end > USER_SPACE_END.as_usize() {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = unsafe { src.as_ptr::<T>().read() };
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
mod table;
|
||||||
|
|
||||||
|
use table::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum Status {
|
||||||
|
// Status::Success = 0
|
||||||
|
InvalidArgument = 1, // EINVAL
|
||||||
|
BadAddress = 2, // EFAULT
|
||||||
|
BadFileDescriptor = 3, // EBADF
|
||||||
|
NoSuchTask = 4, // ESRCH
|
||||||
|
OutOfMemory = 5, // ENOMEM
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum SyscallNumber {
|
||||||
|
Yield = 1,
|
||||||
|
Exit = 2,
|
||||||
|
Write = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u64> for SyscallNumber {
|
||||||
|
type Error = Status;
|
||||||
|
fn try_from(val: u64) -> Result<Self, Self::Error> {
|
||||||
|
match val {
|
||||||
|
1 => Ok(Self::Yield),
|
||||||
|
2 => Ok(Self::Exit),
|
||||||
|
3 => Ok(Self::Write),
|
||||||
|
_ => Err(Status::InvalidArgument),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, _arg5: u64) -> u64 {
|
||||||
|
let result = (|| -> Result<(), Status> {
|
||||||
|
let syscall = SyscallNumber::try_from(num)?;
|
||||||
|
match syscall {
|
||||||
|
SyscallNumber::Yield => sys_yield(),
|
||||||
|
SyscallNumber::Exit => sys_exit(arg0 as usize),
|
||||||
|
SyscallNumber::Write => {
|
||||||
|
sys_write(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(()) => 0,
|
||||||
|
Err(err) => err as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use crate::memory::{VirtualAddr, copy_from_user, copy_val_to_user};
|
||||||
|
|
||||||
|
use super::Status;
|
||||||
|
|
||||||
|
pub fn sys_yield() -> Result<(), Status> {
|
||||||
|
crate::task::scheduler::yield_current();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sys_exit(exit_code: usize) -> ! {
|
||||||
|
crate::task::scheduler::exit_current(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Result<(), Status> {
|
||||||
|
if fd != 1 && fd != 2 {
|
||||||
|
return Err(Status::BadFileDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chunk = [0u8; 128];
|
||||||
|
let mut written = 0;
|
||||||
|
while written < len {
|
||||||
|
let n = (len - written).min(chunk.len());
|
||||||
|
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
|
||||||
|
crate::debug::serial::write_bytes(&chunk[..n]);
|
||||||
|
written += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if out_ptr != 0 {
|
||||||
|
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
use crate::{
|
||||||
|
format,
|
||||||
|
memory::{
|
||||||
|
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, OwnedFrame, PagePermissions,
|
||||||
|
VirtualAddr,
|
||||||
|
},
|
||||||
|
println,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_LOAD_SEGMENTS: usize = 32;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct LoadedRegion {
|
||||||
|
start: VirtualAddr,
|
||||||
|
mapped_pages: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoadedImage {
|
||||||
|
pub entry: VirtualAddr,
|
||||||
|
regions: [LoadedRegion; MAX_LOAD_SEGMENTS],
|
||||||
|
region_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadedImage {
|
||||||
|
/// # Safety
|
||||||
|
/// The supplied address space must contain this image's original mappings.
|
||||||
|
/// Its frames must be exclusively owned by this image and no longer in use.
|
||||||
|
pub unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||||
|
for region in self.regions[..self.region_count].iter().rev() {
|
||||||
|
for page in (0..region.mapped_pages).rev() {
|
||||||
|
let address = VirtualAddr::new(region.start.as_usize() + page * FRAME_SIZE);
|
||||||
|
let frame = unsafe {
|
||||||
|
address_space
|
||||||
|
.unmap(address, allocator)
|
||||||
|
.expect("loaded image mapping was unexpectedly missing")
|
||||||
|
};
|
||||||
|
unsafe { allocator.dealloc(OwnedFrame::from_raw(frame)) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ElfLoadError {
|
||||||
|
AddressTranslationFailed,
|
||||||
|
FailedToMapSegment,
|
||||||
|
AddressOverflow,
|
||||||
|
InvalidStack,
|
||||||
|
OutOfMemory,
|
||||||
|
InvalidElf,
|
||||||
|
TooManyLoadSegments,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||||
|
// on x86_64, we only support ELFs that are either 32 bit x86 or 64 bit x86
|
||||||
|
matches!(
|
||||||
|
elf.machine(),
|
||||||
|
format::elf::ElfIsa::X86 | format::elf::ElfIsa::Amd64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_elf(
|
||||||
|
bytes: &[u8],
|
||||||
|
user_address_space: &mut AddressSpace,
|
||||||
|
allocator: &mut FrameAllocator,
|
||||||
|
direct_map: DirectMap,
|
||||||
|
) -> Result<LoadedImage, ElfLoadError> {
|
||||||
|
let program = format::elf::Elf::parse(bytes).map_err(|_| ElfLoadError::InvalidElf)?;
|
||||||
|
if !is_loadable(&program) {
|
||||||
|
return Err(ElfLoadError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut image = LoadedImage {
|
||||||
|
entry: VirtualAddr::new(program.entry()),
|
||||||
|
regions: core::array::from_fn(|_| LoadedRegion {
|
||||||
|
start: VirtualAddr::new(0),
|
||||||
|
mapped_pages: 0,
|
||||||
|
}),
|
||||||
|
region_count: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = (|| {
|
||||||
|
for header in program
|
||||||
|
.program_headers()
|
||||||
|
.map_err(|_| ElfLoadError::InvalidElf)?
|
||||||
|
{
|
||||||
|
println!("Processing program header: {:?}", header);
|
||||||
|
let header = header.map_err(|_| ElfLoadError::InvalidElf)?;
|
||||||
|
|
||||||
|
if header.file_size > header.memory_size {
|
||||||
|
return Err(ElfLoadError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
match header.segment_type {
|
||||||
|
format::elf::ProgramHeaderType::Load => {
|
||||||
|
if image.region_count == MAX_LOAD_SEGMENTS {
|
||||||
|
return Err(ElfLoadError::TooManyLoadSegments);
|
||||||
|
}
|
||||||
|
// TODO: give a fuck about alignment
|
||||||
|
// TODO: handle program segments that overlap
|
||||||
|
let segment_start = header.virtual_address as usize;
|
||||||
|
if (program.entry() >= segment_start
|
||||||
|
&& program.entry() < segment_start + header.memory_size as usize)
|
||||||
|
&& header.flags & 0x01 == 0
|
||||||
|
{
|
||||||
|
// entry is within NX segment
|
||||||
|
return Err(ElfLoadError::InvalidElf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let page_start = segment_start & !(FRAME_SIZE - 1);
|
||||||
|
let page_offset = segment_start - page_start;
|
||||||
|
|
||||||
|
let mapped_length = page_offset
|
||||||
|
.checked_add(header.memory_size as usize)
|
||||||
|
.ok_or(ElfLoadError::AddressOverflow)?
|
||||||
|
.div_ceil(FRAME_SIZE)
|
||||||
|
* FRAME_SIZE;
|
||||||
|
|
||||||
|
let frame_count = mapped_length / FRAME_SIZE;
|
||||||
|
|
||||||
|
let executable = header.flags & 0x01 != 0;
|
||||||
|
let writable = header.flags & 0x02 != 0;
|
||||||
|
// TODO: support only-executable segments
|
||||||
|
// let readable = header.flags & 0x04 != 0;
|
||||||
|
|
||||||
|
let region = &mut image.regions[image.region_count];
|
||||||
|
region.start = VirtualAddr::new(page_start);
|
||||||
|
image.region_count += 1;
|
||||||
|
|
||||||
|
for i in 0..frame_count {
|
||||||
|
let frame = allocator.alloc().ok_or(ElfLoadError::OutOfMemory)?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Mapping code frame: {:X?} to {:X?}",
|
||||||
|
frame,
|
||||||
|
page_start + i * FRAME_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
if user_address_space
|
||||||
|
.map(
|
||||||
|
frame.frame_address().start_address(),
|
||||||
|
VirtualAddr::new(page_start + i * FRAME_SIZE),
|
||||||
|
PagePermissions::new(writable, executable, true),
|
||||||
|
allocator,
|
||||||
|
memory::CachePolicy::WriteBack,
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
unsafe { allocator.dealloc(frame) };
|
||||||
|
return Err(ElfLoadError::FailedToMapSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = frame.into_raw();
|
||||||
|
region.mapped_pages += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut copied = 0;
|
||||||
|
|
||||||
|
while copied < header.file_size as usize {
|
||||||
|
let destination = VirtualAddr::new(segment_start + copied);
|
||||||
|
let physical = user_address_space
|
||||||
|
.to_physical(destination)
|
||||||
|
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||||
|
let direct_mapped = direct_map
|
||||||
|
.translate(physical)
|
||||||
|
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||||
|
|
||||||
|
let page_remaining = FRAME_SIZE - destination.as_usize() % FRAME_SIZE;
|
||||||
|
let copy_length = page_remaining.min(header.file_size as usize - copied);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
program
|
||||||
|
.bytes()
|
||||||
|
.as_ptr()
|
||||||
|
.add(header.file_offset as usize + copied),
|
||||||
|
direct_mapped.as_mut_ptr(),
|
||||||
|
copy_length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
copied += copy_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format::elf::ProgramHeaderType::GnuStack => {
|
||||||
|
// if the stack is NOT R/W NX, we refuse to map it
|
||||||
|
if header.flags != 6 {
|
||||||
|
return Err(ElfLoadError::InvalidStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
|
||||||
|
if let Err(error) = result {
|
||||||
|
// Only pages created by this load are recorded; none have been handed to a task.
|
||||||
|
unsafe { image.destroy(user_address_space, allocator) };
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
pub mod syscall;
|
pub mod loader;
|
||||||
|
pub mod scheduler;
|
||||||
pub mod tcb;
|
pub mod tcb;
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
use core::cell::UnsafeCell;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arch::ThreadContext,
|
||||||
|
memory::{AddressSpace, VirtualAddr},
|
||||||
|
println,
|
||||||
|
task::tcb::{ExitReason, Tcb, ThreadState},
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_TASKS: usize = 32;
|
||||||
|
|
||||||
|
type TaskId = usize;
|
||||||
|
|
||||||
|
struct Scheduler {
|
||||||
|
current: Option<TaskId>,
|
||||||
|
tasks: [Option<Tcb>; MAX_TASKS],
|
||||||
|
ready: ReadyQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
current: None,
|
||||||
|
tasks: [const { None }; MAX_TASKS],
|
||||||
|
ready: ReadyQueue::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
|
||||||
|
assert_ne!(current_id, next_id);
|
||||||
|
|
||||||
|
let current = self.tasks[current_id].as_mut().unwrap();
|
||||||
|
let prev_ctx = &mut current.context as *mut ThreadContext;
|
||||||
|
let prev_addr_space = ¤t.address_space as *const AddressSpace;
|
||||||
|
|
||||||
|
let next = self.tasks[next_id].as_ref().unwrap();
|
||||||
|
let next_ctx = &next.context as *const ThreadContext;
|
||||||
|
let next_addr_space = &next.address_space as *const AddressSpace;
|
||||||
|
let next_kernel_stack = next.kernel_stack.top();
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
previous_context: prev_ctx,
|
||||||
|
next_context: next_ctx,
|
||||||
|
next_address_space: next_addr_space,
|
||||||
|
next_kernel_stack: next_kernel_stack,
|
||||||
|
activate_address_space: unsafe { *next_addr_space != *prev_addr_space },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Switch {
|
||||||
|
previous_context: *mut ThreadContext,
|
||||||
|
next_context: *const ThreadContext,
|
||||||
|
next_address_space: *const AddressSpace,
|
||||||
|
next_kernel_stack: VirtualAddr,
|
||||||
|
activate_address_space: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Switch {
|
||||||
|
unsafe fn perform(self) {
|
||||||
|
if self.activate_address_space {
|
||||||
|
unsafe {
|
||||||
|
(&*self.next_address_space).activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::arch::set_kernel_stack(self.next_kernel_stack);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
crate::arch::switch_context(self.previous_context, self.next_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReadyQueue {
|
||||||
|
entries: [TaskId; MAX_TASKS],
|
||||||
|
head: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadyQueue {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
entries: [0; MAX_TASKS],
|
||||||
|
head: 0,
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_back(&mut self, task: TaskId) -> bool {
|
||||||
|
if self.len == MAX_TASKS {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tail = (self.head + self.len) % MAX_TASKS;
|
||||||
|
self.entries[tail] = task;
|
||||||
|
self.len += 1;
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_front(&mut self) -> Option<TaskId> {
|
||||||
|
if self.len == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = self.entries[self.head];
|
||||||
|
self.head = (self.head + 1) % MAX_TASKS;
|
||||||
|
self.len -= 1;
|
||||||
|
|
||||||
|
Some(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlobalScheduler(UnsafeCell<Scheduler>);
|
||||||
|
|
||||||
|
unsafe impl Sync for GlobalScheduler {}
|
||||||
|
|
||||||
|
static SCHEDULER: GlobalScheduler = GlobalScheduler(UnsafeCell::new(Scheduler::new()));
|
||||||
|
|
||||||
|
pub fn add_task(mut task: Tcb) -> Result<TaskId, Tcb> {
|
||||||
|
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
|
|
||||||
|
let result = {
|
||||||
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
|
|
||||||
|
match scheduler.tasks.iter().position(Option::is_none) {
|
||||||
|
Some(id) => {
|
||||||
|
task.id = id;
|
||||||
|
task.state = ThreadState::Ready;
|
||||||
|
scheduler.tasks[id] = Some(task);
|
||||||
|
assert!(scheduler.ready.push_back(id));
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
None => Err(task),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::arch::restore_interrupts(interrupt_state);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start() -> ! {
|
||||||
|
crate::arch::disable_interrupts();
|
||||||
|
|
||||||
|
let mut bootstrap_context = ThreadContext::empty();
|
||||||
|
|
||||||
|
let switch = {
|
||||||
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
|
let next_id = scheduler.ready.pop_front().expect("no tasks to run");
|
||||||
|
|
||||||
|
let next = scheduler.tasks[next_id]
|
||||||
|
.as_mut()
|
||||||
|
.expect("ready task is missing");
|
||||||
|
|
||||||
|
next.state = ThreadState::Running;
|
||||||
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
previous_context: &mut bootstrap_context,
|
||||||
|
next_context: &next.context,
|
||||||
|
next_address_space: &next.address_space,
|
||||||
|
next_kernel_stack: next.kernel_stack.top(),
|
||||||
|
activate_address_space: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
switch.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("scheduler returned to bootstrap context");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yield_current() {
|
||||||
|
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
|
|
||||||
|
let switch = {
|
||||||
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
|
|
||||||
|
let Some(next_id) = scheduler.ready.pop_front() else {
|
||||||
|
crate::arch::restore_interrupts(interrupt_state);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_id = scheduler.current.expect("no current task");
|
||||||
|
|
||||||
|
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Ready;
|
||||||
|
assert!(scheduler.ready.push_back(current_id));
|
||||||
|
|
||||||
|
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
||||||
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
|
scheduler.make_switch(current_id, next_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
switch.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this runs when this task is selected to run again
|
||||||
|
crate::arch::restore_interrupts(interrupt_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_current(exit_code: usize) -> ! {
|
||||||
|
crate::arch::disable_interrupts();
|
||||||
|
|
||||||
|
let switch = {
|
||||||
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
|
let current_id = scheduler.current.expect("no current task");
|
||||||
|
let Some(next_id) = scheduler.ready.pop_front() else {
|
||||||
|
println!("All tasks exited");
|
||||||
|
crate::hcf();
|
||||||
|
};
|
||||||
|
|
||||||
|
let current = scheduler.tasks[current_id].as_mut().unwrap();
|
||||||
|
current.state = ThreadState::Dead(ExitReason::Exited(exit_code));
|
||||||
|
|
||||||
|
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
||||||
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
|
scheduler.make_switch(current_id, next_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
switch.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("dead task was scheduled again");
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use crate::{hcf, println};
|
|
||||||
|
|
||||||
pub fn handle(
|
|
||||||
syscall_num: u64,
|
|
||||||
arg0: u64,
|
|
||||||
arg1: u64,
|
|
||||||
arg2: u64,
|
|
||||||
arg3: u64,
|
|
||||||
arg4: u64,
|
|
||||||
arg5: u64,
|
|
||||||
) -> u64 {
|
|
||||||
println!(
|
|
||||||
"Syscall nr={:#X} args=({:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X})",
|
|
||||||
syscall_num, arg0, arg1, arg2, arg3, arg4, arg5
|
|
||||||
);
|
|
||||||
hcf();
|
|
||||||
0
|
|
||||||
}
|
|
||||||
+13
-33
@@ -1,16 +1,23 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
arch::ThreadContext,
|
arch::ThreadContext,
|
||||||
memory::{AddressSpace, KernelStack, VirtualAddr},
|
memory::{AddressSpace, KernelStack, VirtualAddr},
|
||||||
println,
|
task::loader::LoadedImage,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Thread Control Block
|
// Thread Control Block
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ExitReason {
|
||||||
|
Exited(usize),
|
||||||
|
Killed,
|
||||||
|
Fault,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ThreadState {
|
pub enum ThreadState {
|
||||||
Ready,
|
Ready,
|
||||||
Running,
|
Running,
|
||||||
Blocked,
|
Blocked,
|
||||||
Dead,
|
Dead(ExitReason),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -20,6 +27,7 @@ pub struct Tcb {
|
|||||||
pub kernel_stack: KernelStack,
|
pub kernel_stack: KernelStack,
|
||||||
pub context: ThreadContext,
|
pub context: ThreadContext,
|
||||||
pub address_space: AddressSpace,
|
pub address_space: AddressSpace,
|
||||||
|
pub image: LoadedImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tcb {
|
impl Tcb {
|
||||||
@@ -27,10 +35,10 @@ impl Tcb {
|
|||||||
id: usize,
|
id: usize,
|
||||||
address_space: AddressSpace,
|
address_space: AddressSpace,
|
||||||
kernel_stack: KernelStack,
|
kernel_stack: KernelStack,
|
||||||
user_entry: VirtualAddr,
|
image: LoadedImage,
|
||||||
user_stack: VirtualAddr,
|
user_stack: VirtualAddr,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let context = ThreadContext::new(user_entry, user_stack, kernel_stack.top());
|
let context = ThreadContext::new(image.entry, user_stack, kernel_stack.top());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@@ -38,35 +46,7 @@ impl Tcb {
|
|||||||
kernel_stack,
|
kernel_stack,
|
||||||
context,
|
context,
|
||||||
address_space,
|
address_space,
|
||||||
|
image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn run_first_user(user_tcb: &Tcb) {
|
|
||||||
let previous_interrupts = crate::arch::disable_interrupts_and_save();
|
|
||||||
|
|
||||||
let mut boot_thread_ctx = ThreadContext::empty();
|
|
||||||
unsafe {
|
|
||||||
user_tcb.address_space.activate();
|
|
||||||
crate::arch::set_kernel_stack(user_tcb.kernel_stack.top());
|
|
||||||
crate::arch::switch_context(&mut boot_thread_ctx, &user_tcb.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::arch::restore_interrupts(previous_interrupts);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn switch(prev: &mut Tcb, next: &Tcb) {
|
|
||||||
let previous_interrupts = crate::arch::disable_interrupts_and_save();
|
|
||||||
|
|
||||||
if prev.address_space != next.address_space {
|
|
||||||
unsafe { next.address_space.activate() };
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::arch::set_kernel_stack(next.kernel_stack.top());
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
crate::arch::switch_context(&mut prev.context, &next.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::arch::restore_interrupts(previous_interrupts);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "init"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "init"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::arch::asm;
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
fn sys_yield() {
|
||||||
|
unsafe {
|
||||||
|
asm!("mov rax, 1", "syscall");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sys_write(fd: usize, buf: &str) -> Result<(), usize> {
|
||||||
|
unsafe {
|
||||||
|
let status;
|
||||||
|
|
||||||
|
asm!(
|
||||||
|
"mov rax, 3",
|
||||||
|
"syscall",
|
||||||
|
in("rdi") fd,
|
||||||
|
in("rsi") buf.as_ptr(),
|
||||||
|
in("rdx") buf.len(),
|
||||||
|
in("r10") 0,
|
||||||
|
lateout("rax") status,
|
||||||
|
);
|
||||||
|
|
||||||
|
if status != 0 { Err(status) } else { Ok(()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Writer;
|
||||||
|
|
||||||
|
impl core::fmt::Write for Writer {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
sys_write(1, s).map_err(|_| core::fmt::Error)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($arg:tt)*) => (let _ = $crate::Writer.write_fmt(format_args!($($arg)*)););
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
() => ($crate::print!("\n"));
|
||||||
|
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sys_exit(exit_code: usize) -> ! {
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"mov rax, 2",
|
||||||
|
"syscall",
|
||||||
|
in("rdi") exit_code,
|
||||||
|
options(noreturn)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Heap {
|
||||||
|
pub data: UnsafeCell<[u8; 1024]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Heap {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
data: UnsafeCell::new([0; 1024]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
unsafe { (*self.data.get()).len() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Deref for Heap {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { (*self.data.get()).as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for Heap {}
|
||||||
|
|
||||||
|
static HEAP: Heap = Heap::new();
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _start() -> ! {
|
||||||
|
(0..100).for_each(|i| {
|
||||||
|
println!("Hello {}", i);
|
||||||
|
sys_yield();
|
||||||
|
});
|
||||||
|
|
||||||
|
let len = HEAP.len();
|
||||||
|
for i in 0..len {
|
||||||
|
unsafe { HEAP.data.get().as_mut().unwrap()[i] = i as u8 };
|
||||||
|
}
|
||||||
|
|
||||||
|
(0..1024).for_each(|i| {
|
||||||
|
println!("{}", unsafe { HEAP.data.get().as_ref().unwrap()[i] });
|
||||||
|
sys_yield();
|
||||||
|
});
|
||||||
|
|
||||||
|
sys_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
println!("{info}");
|
||||||
|
sys_exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user