Compare commits
3 commits
9e6897d8ba
...
11f8ac03c6
Author | SHA1 | Date | |
---|---|---|---|
11f8ac03c6 | |||
59c9daf02f | |||
842a2b23fe |
32 changed files with 350 additions and 402 deletions
|
@ -2,6 +2,7 @@ display_library: x, options="gui_debug"
|
|||
port_e9_hack: enabled=1
|
||||
cpu: reset_on_triple_fault=0, model=corei7_icelake_u
|
||||
magic_break: enabled=1
|
||||
clock: sync=realtime, time0=local
|
||||
|
||||
ata0-master: type=cdrom, path=../kernel/aphrodite-x86.iso, status=inserted
|
||||
boot: cdrom
|
||||
|
|
|
@ -2,6 +2,7 @@ display_library: x, options="gui_debug"
|
|||
port_e9_hack: enabled=1
|
||||
cpu: reset_on_triple_fault=0, model=corei7_icelake_u
|
||||
magic_break: enabled=1
|
||||
clock: sync=realtime, time0=local
|
||||
|
||||
ata0-master: type=cdrom, path=../kernel/%{BUILT_FILE}, status=inserted
|
||||
boot: cdrom
|
||||
|
|
|
@ -5,7 +5,6 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
paste = "1.0.15"
|
||||
aphrodite_proc_macros = { path = "./aphrodite_proc_macros"}
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
[package]
|
||||
name = "aphrodite_common"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
path = "../src/common/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
strum = "0.27.0"
|
||||
strum_macros = "0.27.0"
|
||||
syn = "2.0.98"
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "aphrodite_proc_macros"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
path = "../src/proc_macros/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.38"
|
||||
syn = { version = "2.0.98", features = ["full"] }
|
||||
aphrodite_common = { path = "../aphrodite_common" }
|
||||
proc-macro2 = "1.0.93"
|
|
@ -122,7 +122,7 @@
|
|||
echo "[INFO] Checking target with clippy"
|
||||
cargo clippy --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target
|
||||
echo "[INFO] Building target"
|
||||
cargo build --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target 2>/dev/null
|
||||
cargo build --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target
|
||||
cp "target/$(echo $target_json | sed 's/\.json//')/release/entrypoint_$target" kernel-$target
|
||||
|
||||
if [[ "$CONFIG_BUILD_GRUB" = "true" ]]; then
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
fn main() {
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
println!("cargo:rerun-if-changed=src/kernel/arch/x86/change_code_segment.s");
|
||||
|
||||
let env = std::env::vars();
|
||||
|
||||
// Begin checks
|
||||
|
@ -49,6 +51,10 @@ fn main() {
|
|||
println!(
|
||||
r#"cargo:rustc-check-cfg=cfg(CONFIG_POWERON_TEST_ALLOC, values("true", "false", none()))"#
|
||||
);
|
||||
|
||||
println!(
|
||||
r#"cargo:rustc-check-cfg=cfg(CONFIG_POWERON_TEST_DISPLAY, values("true", "false", none()))"#
|
||||
);
|
||||
// End checks
|
||||
|
||||
// Configuration name used when a config is required but should always evaluate
|
||||
|
@ -62,4 +68,20 @@ fn main() {
|
|||
println!("cargo:rerun-if-env-changed={}", var);
|
||||
println!("cargo:rustc-cfg={}=\"{}\"", var, val);
|
||||
}
|
||||
|
||||
if !std::process::Command::new("as")
|
||||
.arg("src/kernel/arch/x86/x86.s")
|
||||
.arg("-march=i686")
|
||||
.arg("--32")
|
||||
.arg("-o")
|
||||
.arg(format!("{}/x86_asm.o", std::env::var("OUT_DIR").unwrap()))
|
||||
.spawn()?
|
||||
.wait()?.success() {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Assembler failed to run"));
|
||||
} else {
|
||||
println!("cargo::rustc-link-arg={}/x86_asm.o", std::env::var("OUT_DIR").unwrap());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
real_check=false
|
||||
|
||||
if [[ "$HAVE_GETOPT" = "true" ]]; then
|
||||
LONGOPTS=real_check
|
||||
LONGOPTS=real_check,real-check
|
||||
OPTIONS=c
|
||||
|
||||
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") || (
|
||||
|
@ -30,7 +30,7 @@
|
|||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-c|--real_check)
|
||||
-c|--real_check|--real-check)
|
||||
real_check=true
|
||||
shift
|
||||
;;
|
||||
|
|
|
@ -14,6 +14,7 @@ VERSION=generate
|
|||
CONFIG_DISABLE_MULTIBOOT2_SUPPORT=false
|
||||
|
||||
# Panic behavior. When debugging, generally halt on panic is more useful.
|
||||
# Halt on panic takes priority over spin on panic if both are enabled.
|
||||
CONFIG_HALT_ON_PANIC=true
|
||||
CONFIG_SPIN_ON_PANIC=false
|
||||
|
||||
|
@ -44,4 +45,7 @@ CONFIG_POWERON_TESTS=true
|
|||
|
||||
# Whether to run the allocator power on test.
|
||||
CONFIG_POWERON_TEST_ALLOC=true
|
||||
|
||||
# Whether to run the display power on test.
|
||||
CONFIG_POWERON_TEST_DISPLAY=true
|
||||
# End configs
|
|
@ -27,7 +27,7 @@ if [[ "$1" = "x86" ]]; then
|
|||
|
||||
sed -i "s@%{BUILT_FILE}@aphrodite-$1.iso@g" bochsrc
|
||||
|
||||
bochs -q
|
||||
bochs -q -debugger
|
||||
else
|
||||
if [[ "$TARGETS" =~ "$1" ]]; then
|
||||
echo "[ERROR] Cannot emulate specified architecture \"$1\"."
|
||||
|
|
|
@ -6,8 +6,11 @@ insmod gfxterm
|
|||
insmod efi_uga
|
||||
insmod efi_gop
|
||||
|
||||
menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' {
|
||||
echo 'Loading Aphrodite aphrodite-%{VERSION} ...'
|
||||
multiboot2 /boot/aphrodite.kernel
|
||||
menuentry "Aphrodite (default)" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' {
|
||||
echo 'Loading Aphrodite aphrodite-%{VERSION}...'
|
||||
if multiboot2 /boot/aphrodite.kernel; then
|
||||
boot
|
||||
else
|
||||
echo 'Error loading kernel; not attempting to boot'
|
||||
fi
|
||||
}
|
|
@ -17,11 +17,9 @@ use core::panic::PanicInfo;
|
|||
use aphrodite::arch::egatext;
|
||||
use aphrodite::arch::output::*;
|
||||
use aphrodite::boot::{BootInfo, MemoryMapping};
|
||||
use aphrodite::display::COLOR_DEFAULT;
|
||||
use aphrodite::multiboot2::{
|
||||
FramebufferInfo, MemoryMap, MemorySection, RawMemoryMap, RootTag, Tag,
|
||||
};
|
||||
use aphrodite::output::*;
|
||||
|
||||
#[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))]
|
||||
#[unsafe(link_section = ".bootheader")]
|
||||
|
@ -34,7 +32,7 @@ static MULTIBOOT2_HEADER: [u8; 48] = [
|
|||
0x0A, 0x00, // Relocatable tag
|
||||
0x00, 0x00, // Flags,
|
||||
0x18, 0x00, 0x00, 0x00, // Size of tag
|
||||
0x00, 0x00, 0x00, 0xB0, // Starting minimum location
|
||||
0x00, 0x00, 0x00, 0x00, // Starting minimum location
|
||||
0xFF, 0xFF, 0xFF, 0xFF, // Ending maximum location: End of 32-bit address space
|
||||
0x00, 0x00, 0x00, 0x00, // Image alignment
|
||||
0x01, 0x00, 0x00, 0x00, // Loading preference: lowest possible
|
||||
|
@ -70,7 +68,6 @@ static mut MAGIC: u32 = 0xFFFFFFFF;
|
|||
|
||||
#[unsafe(link_section = ".start")]
|
||||
#[unsafe(no_mangle)]
|
||||
#[aphrodite_proc_macros::kernel_item(ArchBootEntry)]
|
||||
extern "C" fn _start() -> ! {
|
||||
unsafe {
|
||||
// Copy values provided by the bootloader out
|
||||
|
@ -313,7 +310,7 @@ extern "C" fn _start() -> ! {
|
|||
sdebugsln("Bootloader information has been successfully loaded");
|
||||
sdebugunp(b'\n');
|
||||
|
||||
aphrodite::arch::initalize_rtc();
|
||||
//aphrodite::arch::initalize_rtc();
|
||||
|
||||
unsafe {
|
||||
if BI.output.clone().is_some() {
|
||||
|
@ -332,20 +329,14 @@ extern "C" fn _start() -> ! {
|
|||
sdebugs("Framebuffer bpp: ");
|
||||
sdebugbnpln(&aphrodite::u8_as_u8_slice(framebuffer_info.bpp));
|
||||
|
||||
sdebugsln("Beginning test output to screen...");
|
||||
|
||||
let ega: &dyn aphrodite::display::TextDisplay = &framebuffer_info;
|
||||
framebuffer_info.disable_cursor();
|
||||
ega.clear_screen(COLOR_DEFAULT).unwrap();
|
||||
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
||||
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
||||
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
||||
|
||||
aphrodite::indep_boot_entry::IndepBootEntry(Some(ega), &BI);
|
||||
aphrodite::indep_boot_entry::indep_boot_entry(Some(ega), &BI);
|
||||
}
|
||||
}
|
||||
|
||||
aphrodite::indep_boot_entry::IndepBootEntry(None, &BI);
|
||||
aphrodite::indep_boot_entry::indep_boot_entry(None, &BI);
|
||||
}
|
||||
|
||||
#[unsafe(link_section = ".panic")]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -23,28 +23,22 @@ pub mod interrupts {
|
|||
pub const USER_SYSCALL_VECTOR: u16 = 0xA0;
|
||||
|
||||
/// Returns whether interrupts are enabled or not.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsCheck)]
|
||||
fn interrupts_enabled() -> bool { false }
|
||||
|
||||
/// Enables interrupts.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsEnable)]
|
||||
fn enable_interrupts() {}
|
||||
|
||||
/// Disables interrupts.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsDisable)]
|
||||
fn disable_interrupts() {}
|
||||
|
||||
/// Disables interrupts and a value that can be used to restore them
|
||||
/// with [restore_irq].
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsPop)]
|
||||
fn pop_irq() -> u64 { 0 }
|
||||
|
||||
/// Restores interrupts after a [pop_irq] call.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsRestore)]
|
||||
fn restore_irq(_irq: u64) {}
|
||||
|
||||
/// Activates an IDT.
|
||||
#[aphrodite_proc_macros::kernel_item(ActivateIDT)]
|
||||
fn activate_idt(_idt: Idt) {}
|
||||
|
||||
/// An IDT.
|
||||
|
|
|
@ -4,39 +4,88 @@
|
|||
use core::alloc::Layout;
|
||||
use core::arch::asm;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// The GDTR. Used internally in [activate_gdt].
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct Gdtr {
|
||||
base: *const u8,
|
||||
size: usize,
|
||||
// raw pointer to the GDT
|
||||
base: u32,
|
||||
// size of the GDT in bytes
|
||||
size: u16,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Gdtr {}
|
||||
|
||||
/// Activates the GDT using `lgdt`. Does NOT, I repeat, does NOT change the
|
||||
/// segment registers!
|
||||
pub unsafe fn activate_gdt(ptr: *const [u8]) {
|
||||
let gdtr = Gdtr {
|
||||
base: ptr as *const u8,
|
||||
size: ptr.len(),
|
||||
};
|
||||
unsafe { asm!("lgdt {}", in(reg) (&gdtr) as *const Gdtr as usize) }
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov [3f], ax", // load limit
|
||||
"mov [3f+2], ebx", // load base
|
||||
"xor ax, ax", // clear ax
|
||||
"lldt ax", // deactivate LDT
|
||||
"lgdt [3f]", // load GDT
|
||||
"jmp 2f", // jump past the data
|
||||
"3:", // GDT data
|
||||
"nop; nop; nop; nop; nop; nop",
|
||||
"2:", // end
|
||||
in("ax") ptr.len() as u16,
|
||||
in("ebx") ptr as *const u8 as usize as u32,
|
||||
options(readonly)
|
||||
)
|
||||
// super::output::sdebugs("base: ");
|
||||
// super::output::sdebugbnp(&crate::u32_as_u8_slice(GDTR.base));
|
||||
// super::output::sdebugsnp(" size: ");
|
||||
// super::output::sdebugbnpln(&crate::u16_as_u8_slice(GDTR.size));
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a series of GDT entries to an allocated section of memory and returns
|
||||
/// a pointer.
|
||||
pub unsafe fn write_gdt_entries(
|
||||
entries: Vec<GDTEntry>,
|
||||
entries: &[GDTEntry],
|
||||
) -> Result<*const [u8], crate::Error<'static>> {
|
||||
let mut mem =
|
||||
unsafe { alloc::alloc::alloc(Layout::from_size_align(8 * entries.len(), 1).unwrap()) };
|
||||
for ele in &entries {
|
||||
let ele: &GDTEntry = ele;
|
||||
unsafe { ele.write_to_addr(mem as *mut ())? }
|
||||
unsafe { alloc::alloc::alloc(Layout::from_size_align(8 * entries.len(), 8).unwrap()) };
|
||||
for ele in entries {
|
||||
let serialized = ele.serialize()?;
|
||||
unsafe {
|
||||
core::ptr::write(mem as *mut [u8; 8], serialized);
|
||||
}
|
||||
mem = (mem as usize + 8) as *mut u8;
|
||||
}
|
||||
|
||||
Ok(core::ptr::from_raw_parts(mem, 8 * entries.len()))
|
||||
}
|
||||
|
||||
const fn concat_arrays<T, const M: usize, const N: usize>(a: [T; M], b: [T; N]) -> [T; M + N] {
|
||||
let mut result = core::mem::MaybeUninit::uninit();
|
||||
let dest = result.as_mut_ptr() as *mut T;
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(a.as_ptr(), dest, M);
|
||||
core::ptr::copy_nonoverlapping(b.as_ptr(), dest.add(M), N);
|
||||
core::mem::forget(a);
|
||||
core::mem::forget(b);
|
||||
result.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn serialize_gdt_entries(
|
||||
entries: [GDTEntry; 5],
|
||||
) -> [u8; 5 * 8] {
|
||||
concat_arrays(
|
||||
concat_arrays(
|
||||
concat_arrays(
|
||||
concat_arrays(entries[0].serialize_panicing(), entries[1].serialize_panicing()),
|
||||
entries[2].serialize_panicing(),
|
||||
),
|
||||
entries[3].serialize_panicing(),
|
||||
),
|
||||
entries[4].serialize_panicing(),
|
||||
)
|
||||
}
|
||||
|
||||
/// A GDT entry.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GDTEntry {
|
||||
|
@ -59,35 +108,52 @@ pub const GDT_NULL_ENTRY: GDTEntry = GDTEntry {
|
|||
|
||||
/// An error returned by [GDTEntry::write_to_addr] when the limit is greater
|
||||
/// than 0xFFFFF.
|
||||
const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1;
|
||||
pub const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1;
|
||||
|
||||
impl GDTEntry {
|
||||
const unsafe fn write_to_addr(self, ptr: *mut ()) -> Result<(), crate::Error<'static>> {
|
||||
const fn serialize(self) -> Result<[u8; 8], crate::Error<'static>> {
|
||||
if self.limit > 0xFFFFF {
|
||||
return Err(crate::Error::new(
|
||||
"Invalid GDT entry limit(more than 0xFFFFF)",
|
||||
GDT_WRITE_ADDR_INVALID_LIMIT,
|
||||
));
|
||||
}
|
||||
let mut serialized = (0u64).to_ne_bytes();
|
||||
let mut out = [0u8; 8];
|
||||
|
||||
serialized[0] = (self.limit & 0xFF) as u8;
|
||||
serialized[1] = ((self.limit >> 8) & 0xFF) as u8;
|
||||
serialized[6] = ((self.limit >> 16) & 0x0F) as u8;
|
||||
out[0] = (self.limit & 0xFF) as u8;
|
||||
out[1] = ((self.limit >> 8) & 0xFF) as u8;
|
||||
out[6] = ((self.limit >> 16) & 0x0F) as u8;
|
||||
|
||||
serialized[2] = (self.base & 0xFF) as u8;
|
||||
serialized[3] = ((self.base >> 8) & 0xFF) as u8;
|
||||
serialized[4] = ((self.base >> 16) & 0xFF) as u8;
|
||||
serialized[7] = ((self.base >> 24) & 0xFF) as u8;
|
||||
out[2] = (self.base & 0xFF) as u8;
|
||||
out[3] = ((self.base >> 8) & 0xFF) as u8;
|
||||
out[4] = ((self.base >> 16) & 0xFF) as u8;
|
||||
out[7] = ((self.base >> 24) & 0xFF) as u8;
|
||||
|
||||
serialized[5] = self.access;
|
||||
out[5] = self.access;
|
||||
|
||||
serialized[6] |= self.flags << 4;
|
||||
out[6] |= self.flags << 4;
|
||||
|
||||
unsafe {
|
||||
core::ptr::write(ptr as *mut [u8; 8], serialized);
|
||||
Ok(out)
|
||||
}
|
||||
const fn serialize_panicing(self) -> [u8; 8] {
|
||||
if self.limit > 0xFFFFF {
|
||||
panic!("Invalid GDT entry limit(more than 0xFFFFF)");
|
||||
}
|
||||
let mut out = [0u8; 8];
|
||||
|
||||
Ok(())
|
||||
out[0] = (self.limit & 0xFF) as u8;
|
||||
out[1] = ((self.limit >> 8) & 0xFF) as u8;
|
||||
out[6] = ((self.limit >> 16) & 0x0F) as u8;
|
||||
|
||||
out[2] = (self.base & 0xFF) as u8;
|
||||
out[3] = ((self.base >> 8) & 0xFF) as u8;
|
||||
out[4] = ((self.base >> 16) & 0xFF) as u8;
|
||||
out[7] = ((self.base >> 24) & 0xFF) as u8;
|
||||
|
||||
out[5] = self.access;
|
||||
|
||||
out[6] |= self.flags << 4;
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
|
36
kernel/src/kernel/arch/x86/interrupt_impls.rs
Normal file
36
kernel/src/kernel/arch/x86/interrupt_impls.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! Implementations of interrupts.
|
||||
#![cfg(target_arch = "x86")]
|
||||
#![allow(undefined_naked_function_abi)] // special calling convention anyway
|
||||
|
||||
use core::arch::naked_asm;
|
||||
|
||||
macro_rules! int_wrapper {
|
||||
($func:block, $num:expr) => {
|
||||
paste::paste! {
|
||||
/// autogenerated interrupt wrapper
|
||||
#[naked]
|
||||
pub unsafe fn [< int $num >]() {
|
||||
unsafe {
|
||||
naked_asm!(
|
||||
"pushad",
|
||||
"cld",
|
||||
"call {}",
|
||||
"popad",
|
||||
"iret",
|
||||
sym [< int $num _rust >]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// autogenerated interrupt body
|
||||
unsafe extern "C" fn [< int $num _rust >]() $func
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_wrapper!(
|
||||
{
|
||||
super::output::sdebugsln("Interrupt handler #0 ran");
|
||||
},
|
||||
0
|
||||
);
|
|
@ -9,7 +9,6 @@ use core::mem::MaybeUninit;
|
|||
pub const USER_SYSCALL_VECTOR: u16 = 0xA0;
|
||||
|
||||
/// Returns whether interrupts are enabled or not.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsCheck)]
|
||||
pub fn interrupts_enabled() -> bool {
|
||||
let flags: u32;
|
||||
unsafe {
|
||||
|
@ -22,9 +21,11 @@ pub fn interrupts_enabled() -> bool {
|
|||
}
|
||||
|
||||
/// Disables interrupts.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsDisable)]
|
||||
pub fn disable_interrupts() { unsafe { asm!("cli") } }
|
||||
|
||||
/// Enables interrupts.
|
||||
pub fn enable_interrupts() { unsafe { asm!("sti") } }
|
||||
|
||||
/// PoppedInterrupts implements drop and restores the interrupts upon being
|
||||
/// dropped. This is useful in functions where you need interrupts disabled
|
||||
/// during it but also want to use functions like [Result::unwrap] or
|
||||
|
@ -37,7 +38,6 @@ impl Drop for PoppedInterrupts {
|
|||
}
|
||||
|
||||
/// Disables interrupts and returns the value of them.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsPop)]
|
||||
pub fn pop_irq() -> PoppedInterrupts {
|
||||
let flags: u32;
|
||||
unsafe {
|
||||
|
@ -51,7 +51,6 @@ pub fn pop_irq() -> PoppedInterrupts {
|
|||
}
|
||||
|
||||
/// Restores interrupts after a [pop_irq] call.
|
||||
#[aphrodite_proc_macros::kernel_item(InterruptsRestore)]
|
||||
pub fn restore_irq(flags: PoppedInterrupts) {
|
||||
let flags = flags.0;
|
||||
unsafe {
|
||||
|
@ -71,12 +70,21 @@ struct Idtr {
|
|||
|
||||
/// Loads an interrupt descriptor table.
|
||||
unsafe fn load_idt(base: *const u8, size: usize) {
|
||||
let idtr = Idtr { base, size };
|
||||
unsafe { asm!("lidt {}", in(reg) (&idtr) as *const Idtr as usize) }
|
||||
static mut IDTR: Idtr = Idtr {
|
||||
base: 0 as *const u8,
|
||||
size: 0,
|
||||
};
|
||||
unsafe {
|
||||
IDTR = Idtr {
|
||||
base,
|
||||
size,
|
||||
};
|
||||
}
|
||||
unsafe { asm!("lidt {}", sym IDTR) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct IdtEntry {
|
||||
pub struct IdtEntry {
|
||||
pub offset_high: u16,
|
||||
pub data: u16,
|
||||
pub segment: u16,
|
||||
|
@ -108,8 +116,7 @@ impl From<IdtEntry> for RawIdtEntry {
|
|||
///
|
||||
/// # Panics
|
||||
/// Panics if the global allocator has not been setup
|
||||
#[aphrodite_proc_macros::kernel_item(ActivateIDT)]
|
||||
fn activate_idt(idt: Idt) {
|
||||
pub unsafe fn activate_idt(idt: Idt) {
|
||||
let mut entries = alloc::vec::Vec::new();
|
||||
for i in 0..idt.len {
|
||||
if idt.using_raw[i] {
|
||||
|
@ -195,7 +202,7 @@ fn activate_idt(idt: Idt) {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct Idt {
|
||||
vectors: [u16; 256],
|
||||
funcs: [MaybeUninit<fn()>; 256],
|
||||
funcs: [MaybeUninit<unsafe fn()>; 256],
|
||||
user_callable: [bool; 256],
|
||||
exception: [bool; 256],
|
||||
raw_entries: [IdtEntry; 256],
|
||||
|
@ -207,7 +214,7 @@ pub struct Idt {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct IdtBuilder {
|
||||
vectors: [u16; 256],
|
||||
funcs: [MaybeUninit<fn()>; 256],
|
||||
funcs: [MaybeUninit<unsafe fn()>; 256],
|
||||
user_callable: [bool; 256],
|
||||
exception: [bool; 256],
|
||||
raw_entries: [IdtEntry; 256],
|
||||
|
@ -238,7 +245,7 @@ impl IdtBuilder {
|
|||
pub fn add_fn(
|
||||
&mut self,
|
||||
vector: u16,
|
||||
func: fn(),
|
||||
func: unsafe fn(),
|
||||
user_callable: bool,
|
||||
exception: bool,
|
||||
) -> &mut Self {
|
||||
|
|
|
@ -1,180 +0,0 @@
|
|||
//! Hardware-level memory sections. Unimplemented for certain hardware, x86
|
||||
//! implements with GDT.
|
||||
#![cfg(target_arch = "x86")]
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::memsections::*;
|
||||
|
||||
use super::gdt::{GDTEntry, write_gdt_entries};
|
||||
|
||||
/// A list of memory sections. Create one with [MemorySectionBuilder].
|
||||
pub struct MemorySections {
|
||||
sections: Vec<MemorySection>,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct GDTR {
|
||||
address: u32,
|
||||
size: u16,
|
||||
}
|
||||
|
||||
unsafe impl crate::memsections::MemorySections for MemorySections {
|
||||
unsafe fn write(self) -> Result<(), crate::Error<'static>> {
|
||||
let mut entries: Vec<GDTEntry> = vec![];
|
||||
|
||||
for section in self.sections {
|
||||
let mut section: MemorySection = section;
|
||||
// rust-analyzer doesn't want to cooperate and recognize that section is already
|
||||
// MemorySection, so I'm telling it here.
|
||||
fn make_entry(section: &mut MemorySection, entries: &mut Vec<GDTEntry>) {
|
||||
if section.length == 0 {
|
||||
return;
|
||||
}
|
||||
let mut len = section.length as u32;
|
||||
while len > 0xFFFFF {
|
||||
len -= 0xFFFFF;
|
||||
}
|
||||
let mut access = 0b10000001u8;
|
||||
match section.owner {
|
||||
Owner::Kernelspace => {
|
||||
access |= 0b0000000;
|
||||
},
|
||||
Owner::Modulespace => {
|
||||
access |= 0b0100000;
|
||||
},
|
||||
Owner::Userspace => {
|
||||
access |= 0b1100000;
|
||||
},
|
||||
}
|
||||
if let SectionType::TaskSection { busy } = section.section_type {
|
||||
access |= 0b00000;
|
||||
if busy {
|
||||
access |= 0x9;
|
||||
} else {
|
||||
access |= 0xB;
|
||||
}
|
||||
} else {
|
||||
access |= 0b10000;
|
||||
if let SectionType::CodeSection {
|
||||
can_powerful_sections_jump,
|
||||
} = section.section_type
|
||||
{
|
||||
access |= 0b1000;
|
||||
if can_powerful_sections_jump {
|
||||
access |= 0b100;
|
||||
}
|
||||
if section.readable {
|
||||
access |= 0b10;
|
||||
}
|
||||
} else if section.section_type == SectionType::DataSection {
|
||||
access |= 0b0000;
|
||||
if section.writable {
|
||||
access |= 0b10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let flags = 0b1100u8;
|
||||
|
||||
let entry = GDTEntry {
|
||||
limit: len,
|
||||
base: section.address as u32,
|
||||
access,
|
||||
flags,
|
||||
};
|
||||
if section.length > 0xFFFFF {
|
||||
section.length -= 0xFFFFF;
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
while section.length > 0xFFFFF {
|
||||
make_entry(&mut section, &mut entries);
|
||||
}
|
||||
make_entry(&mut section, &mut entries);
|
||||
}
|
||||
unsafe {
|
||||
let _ = super::interrupts::pop_irq();
|
||||
|
||||
let segment_entries: Vec<GDTEntry> = entries.clone();
|
||||
|
||||
let ptr = write_gdt_entries(entries)?;
|
||||
|
||||
let gdtr = GDTR {
|
||||
address: ptr as *const u8 as usize as u32,
|
||||
size: (ptr.len() - 1) as u16,
|
||||
};
|
||||
|
||||
let addr = &gdtr as *const GDTR as *const () as usize as u32;
|
||||
|
||||
asm!(
|
||||
"lgdt eax",
|
||||
in("eax") addr
|
||||
);
|
||||
|
||||
let mut code_segment = 0u16;
|
||||
let mut code_set = false;
|
||||
let mut data_segment = 0u16;
|
||||
let mut data_set = false;
|
||||
|
||||
let mut i = 0;
|
||||
for entry in segment_entries {
|
||||
let entry: GDTEntry = entry;
|
||||
i += 1;
|
||||
if code_set && data_set {
|
||||
break;
|
||||
}
|
||||
|
||||
if entry.access & 0b11000 == 0b11000 && !code_set {
|
||||
code_segment = i - 1;
|
||||
code_set = true;
|
||||
} else if entry.access & 0b10000 == 0b10000 && !data_set {
|
||||
data_segment = i - 1;
|
||||
data_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
asm!(
|
||||
"jmp bx:2 ; `#[deny(named_asm_labels)]` on by default; see <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels>
|
||||
2: ; ax is already loaded with the correct value from rustland
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax",
|
||||
in("bx") code_segment,
|
||||
in("ax") data_segment,
|
||||
options(preserves_flags, nomem, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory section builder.
|
||||
pub struct MemorySectionBuilder {
|
||||
sections: Vec<MemorySection>,
|
||||
}
|
||||
|
||||
impl MemorySectionBuilder {
|
||||
/// Create a new MemorySectionBuilder.
|
||||
pub fn new() -> Self { MemorySectionBuilder { sections: vec![] } }
|
||||
|
||||
/// Adds a section to this MemorySectionBuilder.
|
||||
pub fn add_section(&mut self, section: MemorySection) -> &mut Self {
|
||||
self.sections.push(section);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Finishes this MemorySectionBuilder and returns a MemorySections.
|
||||
pub fn finish(self) -> MemorySections {
|
||||
MemorySections {
|
||||
sections: self.sections,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,19 +5,19 @@ use core::arch::asm;
|
|||
|
||||
pub mod egatext;
|
||||
mod gdt;
|
||||
mod interrupt_impls;
|
||||
pub mod interrupts;
|
||||
pub mod memory;
|
||||
pub mod output;
|
||||
pub mod paging;
|
||||
pub mod ports;
|
||||
|
||||
mod constants;
|
||||
|
||||
use alloc::vec;
|
||||
use constants::*;
|
||||
use gdt::GDTEntry;
|
||||
use interrupts::{pop_irq, restore_irq};
|
||||
use interrupts::{disable_interrupts, enable_interrupts, pop_irq, restore_irq};
|
||||
use ports::{inb, outb};
|
||||
use output::*;
|
||||
|
||||
/// Returns the most specific architecture available.
|
||||
pub const fn get_arch() -> super::Architecture { super::Architecture::X86 }
|
||||
|
@ -92,27 +92,66 @@ pub fn initalize_rtc() {
|
|||
unsafe { RTC_INITALIZED = true }
|
||||
}
|
||||
|
||||
pub fn sleep(seconds: u32) { initalize_rtc(); }
|
||||
|
||||
pub fn alloc_available_boot() {
|
||||
let irq = pop_irq();
|
||||
let mut entries = vec![];
|
||||
entries.push(gdt::GDT_NULL_ENTRY);
|
||||
entries.push(GDTEntry {
|
||||
limit: 0,
|
||||
disable_interrupts();
|
||||
{
|
||||
// GDT
|
||||
sdebugsln("Setting up GDT");
|
||||
|
||||
let entries = gdt::serialize_gdt_entries([
|
||||
gdt::GDT_NULL_ENTRY,
|
||||
GDTEntry { // kernel code segment, segment 0x08
|
||||
limit: 0xFFFFF,
|
||||
base: 0,
|
||||
access: 0b10011011,
|
||||
flags: 0b1100,
|
||||
}); // kernel code segment
|
||||
entries.push(GDTEntry {
|
||||
limit: 0,
|
||||
access: 0x9A,
|
||||
flags: 0xC,
|
||||
},
|
||||
GDTEntry { // kernel data segment, segment 0x10
|
||||
limit: 0xFFFFF,
|
||||
base: 0,
|
||||
access: 0b10010011,
|
||||
flags: 0b1100,
|
||||
}); //
|
||||
access: 0x92,
|
||||
flags: 0xC,
|
||||
},
|
||||
GDTEntry { // user code segment, segment 0x18
|
||||
limit: 0xFFFFF,
|
||||
base: 0,
|
||||
access: 0xFA,
|
||||
flags: 0xC,
|
||||
},
|
||||
GDTEntry { // user data segment, segment 0x20
|
||||
limit: 0xFFFFF,
|
||||
base: 0,
|
||||
access: 0xF2,
|
||||
flags: 0xC,
|
||||
}
|
||||
]);
|
||||
|
||||
sdebugsln("GDT prepared");
|
||||
|
||||
unsafe {
|
||||
gdt::activate_gdt(gdt::write_gdt_entries(entries).unwrap());
|
||||
gdt::activate_gdt(&entries);
|
||||
}
|
||||
|
||||
sdebugsln("GDT successfully activated; resetting segment registers");
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"call reloadSegments", // I hate rust's inline assembly
|
||||
out("ax") _
|
||||
);
|
||||
}
|
||||
sdebugsln("Segment registers reset");
|
||||
}
|
||||
{
|
||||
// IDT
|
||||
sdebugsln("Setting up IDT");
|
||||
let idt = self::interrupts::IdtBuilder::new()
|
||||
.add_fn(0, interrupt_impls::int0, false, true)
|
||||
.finish();
|
||||
unsafe {
|
||||
interrupts::activate_idt(idt);
|
||||
}
|
||||
enable_interrupts();
|
||||
sdebugsln("IDT successfully loaded");
|
||||
}
|
||||
restore_irq(irq);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
use core::arch::asm;
|
||||
|
||||
use aphrodite_proc_macros::kernel_item;
|
||||
|
||||
/// One page directory entry. Use [PageDirectoryEntry::create_fourmb] or
|
||||
/// [PageDirectoryEntry::create_other] to make these.
|
||||
pub enum PageDirectoryEntry {
|
||||
|
@ -120,11 +118,9 @@ static mut PAGE_DIRECTORY: PageDirectoryEntry =
|
|||
PageDirectoryEntry::create_other(0, false, 0, false, false, false, false, false, false, false);
|
||||
|
||||
/// Initalize paging.
|
||||
#[kernel_item(PagingInit)]
|
||||
pub fn initalize_paging() {}
|
||||
|
||||
/// Disables paging by clearing bit 31 in the cr0 register.
|
||||
#[kernel_item(PagingDeinit)]
|
||||
pub fn disable_paging() {
|
||||
unsafe {
|
||||
asm!(
|
||||
|
|
24
kernel/src/kernel/arch/x86/x86.s
Normal file
24
kernel/src/kernel/arch/x86/x86.s
Normal file
|
@ -0,0 +1,24 @@
|
|||
.intel_syntax noprefix
|
||||
|
||||
.global reloadSegments
|
||||
|
||||
reloadSegments:
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
xchg bx, bx
|
||||
call get_retaddr_ppro
|
||||
add eax, 7
|
||||
pushd 0x8
|
||||
push eax
|
||||
retf
|
||||
|
||||
.reload_cs:
|
||||
ret
|
||||
|
||||
get_retaddr_ppro:
|
||||
mov eax, [esp]
|
||||
ret
|
|
@ -127,7 +127,7 @@ impl core::iter::Iterator for MemoryMap {
|
|||
self.reset_iter();
|
||||
return None;
|
||||
}
|
||||
Some(self.sections[self.idx - 1].into())
|
||||
Some(self.sections[self.idx - 1])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Config-related stuff.
|
||||
|
||||
/// C
|
||||
/// Get configurations as a certain type
|
||||
#[macro_export]
|
||||
macro_rules! cfg_int {
|
||||
($cfg:literal, $type:ident) => {
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
#![allow(static_mut_refs)]
|
||||
|
||||
use crate::arch::output::*;
|
||||
use crate::display::{COLOR_DEFAULT, NoneTextDisplay};
|
||||
use crate::display::NoneTextDisplay;
|
||||
use crate::output::*;
|
||||
|
||||
use aphrodite_proc_macros::*;
|
||||
|
||||
/// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files
|
||||
/// eventually call this.
|
||||
#[kernel_item(IndepBootEntry)]
|
||||
fn indep_boot_entry(
|
||||
pub fn indep_boot_entry(
|
||||
display: Option<&dyn crate::display::TextDisplay>,
|
||||
#[allow(non_snake_case)] BI: &crate::boot::BootInfo,
|
||||
) -> ! {
|
||||
|
@ -22,13 +19,12 @@ fn indep_boot_entry(
|
|||
"Somehow the kernel successfully booted into IndepBootEntry with a dummy architecture"
|
||||
);
|
||||
|
||||
sdebugsln("IndepBootEntry running");
|
||||
|
||||
let display = display.unwrap_or(&NoneTextDisplay {});
|
||||
|
||||
display.clear_screen(COLOR_DEFAULT);
|
||||
sreset();
|
||||
|
||||
let mem_map = BI.memory_map.unwrap();
|
||||
crate::mem::MemMapAllocInit(mem_map).unwrap();
|
||||
crate::mem::memory_map_alloc_init(mem_map).unwrap();
|
||||
|
||||
crate::arch::alloc_available_boot();
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ use core::ptr::{NonNull, null_mut};
|
|||
|
||||
use crate::boot::{MemoryMap, MemoryType};
|
||||
|
||||
use aphrodite_proc_macros::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Allocation {
|
||||
/// Whether this allocation is used. This is used so that the
|
||||
|
@ -65,7 +63,6 @@ static mut ALLOCATOR: MaybeMemoryMapAlloc<'static> = MaybeMemoryMapAlloc::new(No
|
|||
static mut ALLOCATOR_MEMMAP: MaybeUninit<MemoryMap> = MaybeUninit::uninit();
|
||||
static mut ALLOCATOR_INITALIZED: bool = false;
|
||||
|
||||
#[kernel_item(MemMapAlloc)]
|
||||
pub fn get_allocator() -> Option<&'static MemoryMapAlloc<'static>> {
|
||||
if unsafe { ALLOCATOR_INITALIZED } {
|
||||
#[allow(static_mut_refs)]
|
||||
|
@ -90,8 +87,7 @@ pub unsafe fn get_allocator_unchecked() -> &'static MemoryMapAlloc<'static> {
|
|||
}
|
||||
}
|
||||
|
||||
#[kernel_item(MemMapAllocInit)]
|
||||
fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> {
|
||||
pub fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> {
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
ALLOCATOR_MEMMAP.write(memmap);
|
||||
|
@ -203,7 +199,7 @@ impl<'a> MemoryMapAlloc<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
if out.allocations == core::ptr::null_mut() {
|
||||
if out.allocations.is_null() {
|
||||
return Err(crate::Error::new(
|
||||
"no free memory with space for 32 allocations",
|
||||
ALLOCATIONS_NOT_ENOUGH_SPACE,
|
||||
|
@ -345,7 +341,7 @@ impl<'a> MemoryMapAlloc<'a> {
|
|||
/// Finds a free block of memory that can fit the requested size and
|
||||
/// alignment
|
||||
fn find_free_block(&self, size: u64, align: usize) -> Option<u64> {
|
||||
for mapping in self.memory_map.clone() {
|
||||
for mapping in *self.memory_map {
|
||||
if mapping.len < size {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#![deny(rustdoc::invalid_html_tags)]
|
||||
#![deny(rustdoc::invalid_rust_codeblocks)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(f128)]
|
||||
|
@ -20,6 +21,8 @@
|
|||
#![allow(internal_features)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(vec_into_raw_parts)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
|
|
@ -54,18 +54,18 @@ pub struct MemorySection {
|
|||
reserved: u32,
|
||||
}
|
||||
|
||||
impl Into<crate::boot::MemoryMapping> for MemorySection {
|
||||
fn into(self) -> crate::boot::MemoryMapping {
|
||||
impl From<MemorySection> for crate::boot::MemoryMapping {
|
||||
fn from(val: MemorySection) -> Self {
|
||||
MemoryMapping {
|
||||
mem_type: match self.mem_type {
|
||||
mem_type: match val.mem_type {
|
||||
1 => crate::boot::MemoryType::Free,
|
||||
2 => crate::boot::MemoryType::HardwareReserved,
|
||||
3 => crate::boot::MemoryType::HardwareSpecific(3, false),
|
||||
5 => crate::boot::MemoryType::Faulty,
|
||||
_ => crate::boot::MemoryType::Reserved,
|
||||
},
|
||||
start: self.base_addr,
|
||||
len: self.length,
|
||||
start: val.base_addr,
|
||||
len: val.length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
19
kernel/src/kernel/power_on_tests/display.rs
Normal file
19
kernel/src/kernel/power_on_tests/display.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
#![cfg(all(
|
||||
not(CONFIG_POWERON_TESTS = "false"),
|
||||
not(CONFIG_POWERON_TEST_DISPLAY = "false")
|
||||
))]
|
||||
|
||||
use crate::display::{TextDisplay, COLOR_BLACK, COLOR_DEFAULT};
|
||||
use crate::output::{toutputsln, sreset};
|
||||
|
||||
pub fn run(display: &dyn TextDisplay) {
|
||||
display.clear_screen(COLOR_DEFAULT).unwrap();
|
||||
sreset();
|
||||
toutputsln("Testing display...", display).unwrap();
|
||||
toutputsln("Testing display...", display).unwrap();
|
||||
toutputsln("Testing display...", display).unwrap();
|
||||
display.clear_screen(COLOR_BLACK).unwrap();
|
||||
sreset();
|
||||
display.clear_screen(COLOR_DEFAULT).unwrap();
|
||||
sreset();
|
||||
}
|
|
@ -27,7 +27,7 @@ pub fn run(display: &dyn TextDisplay) {
|
|||
tdebugsnpln(" byte(s) of memory...", display).unwrap();
|
||||
|
||||
let allocation = allocator.allocate(Layout::from_size_align(size, 1).unwrap());
|
||||
if let Err(_) = allocation {
|
||||
if allocation.is_err() {
|
||||
terrors("Failed to allocate: ", display).unwrap();
|
||||
unsafe { crate::mem::LAST_MEMMAP_ERR.unwrap_err().display_np(display) }
|
||||
panic!("Allocator test failure");
|
||||
|
@ -51,6 +51,5 @@ pub fn run(display: &dyn TextDisplay) {
|
|||
tdebugsln("Successfully deallocated!", display).unwrap();
|
||||
}
|
||||
}
|
||||
tdebugsln("", display).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
use crate::display::TextDisplay;
|
||||
|
||||
mod memmapalloc;
|
||||
mod display;
|
||||
|
||||
pub fn run(display: &dyn TextDisplay) {
|
||||
#[cfg(not(CONFIG_POWERON_TEST_ALLOC = "false"))]
|
||||
memmapalloc::run(display);
|
||||
|
||||
#[cfg(not(CONFIG_POWERON_TEST_DISPLAY = "false"))]
|
||||
display::run(display);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ pub const fn i16_as_u8_slice(mut value: i16) -> [u8; 6] {
|
|||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
buf[i] = char;
|
||||
value = value / 10;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
|
@ -33,7 +33,24 @@ pub const fn u32_as_u8_slice(mut value: u32) -> [u8; 10] {
|
|||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
buf[i] = char;
|
||||
value = value / 10;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
/// Converts an u16 to an [u8; 5].
|
||||
pub const fn u16_as_u8_slice(mut value: u16) -> [u8; 5] {
|
||||
let mut buf = [0u8; 5];
|
||||
let mut i = 4;
|
||||
if value == 0 {
|
||||
buf[0] = b'0';
|
||||
}
|
||||
while value > 0 {
|
||||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
buf[i] = char;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
|
@ -48,15 +65,15 @@ pub const fn u8_as_u8_slice(mut value: u8) -> [u8; 3] {
|
|||
}
|
||||
while value > 0 {
|
||||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
let char = b'0' + digit;
|
||||
buf[i] = char;
|
||||
value = value / 10;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
/// Converts an usize(32 or 64 bit) to an [u8; 10].
|
||||
/// Converts an usize(32 or 64 bit) to an [u8; 20].
|
||||
pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] {
|
||||
let mut buf = [0u8; 20];
|
||||
let mut i = 19;
|
||||
|
@ -67,7 +84,7 @@ pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] {
|
|||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
buf[i] = char;
|
||||
value = value / 10;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
|
@ -84,7 +101,7 @@ pub const fn u64_as_u8_slice(mut value: u64) -> [u8; 20] {
|
|||
let digit = value % 10;
|
||||
let char = b'0' + digit as u8;
|
||||
buf[i] = char;
|
||||
value = value / 10;
|
||||
value /= 10;
|
||||
i -= 1;
|
||||
}
|
||||
buf
|
||||
|
@ -99,7 +116,7 @@ pub fn str_as_i16(mut value: &[u8]) -> i16 {
|
|||
}
|
||||
for byte in value {
|
||||
let byte = *byte;
|
||||
if byte < b'0' || byte > b'9' {
|
||||
if !byte.is_ascii_digit() {
|
||||
continue;
|
||||
}
|
||||
out *= 10;
|
||||
|
@ -121,7 +138,7 @@ pub fn str_as_u32(value: &[u8]) -> u32 {
|
|||
let mut out = 0u32;
|
||||
for byte in value {
|
||||
let byte = *byte;
|
||||
if byte < b'0' || byte > b'9' {
|
||||
if !byte.is_ascii_digit() {
|
||||
continue;
|
||||
}
|
||||
out *= 10;
|
||||
|
@ -143,7 +160,7 @@ pub fn str_as_u128(value: &[u8]) -> u128 {
|
|||
let mut out = 0u128;
|
||||
for byte in value {
|
||||
let byte = *byte;
|
||||
if byte < b'0' || byte > b'9' {
|
||||
if !byte.is_ascii_digit() {
|
||||
continue;
|
||||
}
|
||||
out *= 10;
|
||||
|
@ -165,7 +182,7 @@ pub fn str_as_u64(value: &[u8]) -> u64 {
|
|||
let mut out = 0u64;
|
||||
for byte in value {
|
||||
let byte = *byte;
|
||||
if byte < b'0' || byte > b'9' {
|
||||
if !byte.is_ascii_digit() {
|
||||
continue;
|
||||
}
|
||||
out *= 10;
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{ItemFn, Signature, Token};
|
||||
|
||||
struct KernelItemNameInput {
|
||||
item: syn::Ident,
|
||||
}
|
||||
|
||||
impl Parse for KernelItemNameInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let item: syn::Ident = input.parse()?;
|
||||
Ok(KernelItemNameInput { item })
|
||||
}
|
||||
}
|
||||
|
||||
fn to_tokens(signature: Signature, tokens: &mut proc_macro2::TokenStream) {
|
||||
let ts = tokens;
|
||||
signature.constness.to_tokens(ts);
|
||||
signature.asyncness.to_tokens(ts);
|
||||
signature.unsafety.to_tokens(ts);
|
||||
signature.abi.to_tokens(ts);
|
||||
signature.fn_token.to_tokens(ts);
|
||||
signature.generics.to_tokens(ts);
|
||||
signature.paren_token.surround(ts, |tokens| {
|
||||
signature.inputs.to_tokens(tokens);
|
||||
if let Some(variadic) = &signature.variadic {
|
||||
if !signature.inputs.empty_or_trailing() {
|
||||
<Token![,]>::default().to_tokens(tokens);
|
||||
}
|
||||
variadic.to_tokens(tokens);
|
||||
}
|
||||
});
|
||||
signature.output.to_tokens(ts);
|
||||
signature.generics.where_clause.to_tokens(ts);
|
||||
}
|
||||
|
||||
fn to_token_stream(signature: Signature) -> proc_macro2::TokenStream {
|
||||
let mut tokens = proc_macro2::TokenStream::new();
|
||||
to_tokens(signature, &mut tokens);
|
||||
tokens
|
||||
}
|
||||
|
||||
/// Implement a kernel item.
|
||||
#[proc_macro_attribute]
|
||||
pub fn kernel_item(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let name: KernelItemNameInput = syn::parse_macro_input!(attr);
|
||||
let item_name = name.item;
|
||||
|
||||
let input_fn = syn::parse_macro_input!(item as ItemFn);
|
||||
let fn_name = input_fn.clone().sig.ident;
|
||||
let fn_sig = to_token_stream(input_fn.clone().sig);
|
||||
|
||||
quote! {
|
||||
/// The #item_name kernel item.
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const #item_name: #fn_sig = #fn_name;
|
||||
|
||||
#input_fn
|
||||
}
|
||||
.into()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue