Comparar commits
2 Commits
72e5221588
...
f9be9904fc
| Autor | SHA1 | Fecha | |
|---|---|---|---|
|
f9be9904fc
|
|||
| 315b8aa0f5 |
14
.cargo/config.toml
Archivo normal
14
.cargo/config.toml
Archivo normal
@@ -0,0 +1,14 @@
|
|||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-none"
|
||||||
|
|
||||||
|
[target.x86_64-unknown-none]
|
||||||
|
runner = "qemu-system-x86_64 -kernel"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "relocation-model=static",
|
||||||
|
"-C", "link-arg=-Tkernel/linker.ld",
|
||||||
|
"-C", "link-arg=-no-pie"
|
||||||
|
]
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "alloc"]
|
||||||
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
2
Makefile
2
Makefile
@@ -29,7 +29,7 @@ all: kernel modules drivers
|
|||||||
# Build the core kernel
|
# Build the core kernel
|
||||||
kernel:
|
kernel:
|
||||||
@echo "Building Rust kernel ($(ARCH), $(BUILD_TYPE))"
|
@echo "Building Rust kernel ($(ARCH), $(BUILD_TYPE))"
|
||||||
$(CARGO) build $(CARGO_FLAGS)
|
$(CARGO) build $(CARGO_FLAGS) --bin rust-kernel --target x86_64-unknown-none
|
||||||
|
|
||||||
# Build kernel modules
|
# Build kernel modules
|
||||||
modules: $(RUST_MODULES)
|
modules: $(RUST_MODULES)
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -92,11 +92,11 @@ make clean && RUSTFLAGS="-Awarnings" make kernel
|
|||||||
### Basic Execution
|
### Basic Execution
|
||||||
```bash
|
```bash
|
||||||
# Run kernel in QEMU (basic)
|
# Run kernel in QEMU (basic)
|
||||||
qemu-system-x86_64 -kernel target/release/rust-kernel
|
qemu-system-x86_64 -kernel kernel/target/x86_64-unknown-none/release/rust-kernel
|
||||||
|
|
||||||
# Run with more memory and serial output
|
# Run with more memory and serial output
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-kernel target/release/rust-kernel \
|
-kernel kernel/target/x86_64-unknown-none/release/rust-kernel \
|
||||||
-m 128M \
|
-m 128M \
|
||||||
-serial stdio \
|
-serial stdio \
|
||||||
-no-reboot \
|
-no-reboot \
|
||||||
@@ -107,7 +107,7 @@ qemu-system-x86_64 \
|
|||||||
```bash
|
```bash
|
||||||
# Full-featured QEMU run with debugging
|
# Full-featured QEMU run with debugging
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-kernel target/release/rust-kernel \
|
-kernel kernel/target/x86_64-unknown-none/release/rust-kernel \
|
||||||
-m 256M \
|
-m 256M \
|
||||||
-smp 2 \
|
-smp 2 \
|
||||||
-serial stdio \
|
-serial stdio \
|
||||||
@@ -124,13 +124,13 @@ qemu-system-x86_64 \
|
|||||||
```bash
|
```bash
|
||||||
# Run QEMU with GDB server
|
# Run QEMU with GDB server
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-kernel target/release/rust-kernel \
|
-kernel kernel/target/x86_64-unknown-none/release/rust-kernel \
|
||||||
-s -S \
|
-s -S \
|
||||||
-m 128M \
|
-m 128M \
|
||||||
-serial stdio
|
-serial stdio
|
||||||
|
|
||||||
# In another terminal, connect GDB
|
# In another terminal, connect GDB
|
||||||
gdb target/release/rust-kernel
|
gdb kernel/target/x86_64-unknown-none/release/rust-kernel
|
||||||
(gdb) target remote localhost:1234
|
(gdb) target remote localhost:1234
|
||||||
(gdb) continue
|
(gdb) continue
|
||||||
```
|
```
|
||||||
@@ -315,8 +315,8 @@ perf counters # Show performance counters
|
|||||||
- **Memory Mapping**: Support for memory-mapped I/O and files
|
- **Memory Mapping**: Support for memory-mapped I/O and files
|
||||||
|
|
||||||
### Process & Task Management
|
### Process & Task Management
|
||||||
- **Preemptive Scheduling**: Priority-based with round-robin
|
- **Preemptive Scheduling**: Priority-based with round-robin and CFS (Completely Fair Scheduler)
|
||||||
- **Context Switching**: Full CPU context preservation
|
- **Context Switching**: Full CPU context preservation including GPRs, segment registers, and control registers
|
||||||
- **Kernel Threads**: Lightweight kernel task execution
|
- **Kernel Threads**: Lightweight kernel task execution
|
||||||
- **Process States**: Running, ready, waiting, zombie states
|
- **Process States**: Running, ready, waiting, zombie states
|
||||||
|
|
||||||
@@ -360,6 +360,7 @@ perf counters # Show performance counters
|
|||||||
|
|
||||||
### 📋 **Future Enhancements**
|
### 📋 **Future Enhancements**
|
||||||
- [ ] SMP (multi-processor) support
|
- [ ] SMP (multi-processor) support
|
||||||
|
- [ ] ACPI (Advanced Configuration and Power Interface) support
|
||||||
- [ ] Advanced file systems (ext2, FAT32)
|
- [ ] Advanced file systems (ext2, FAT32)
|
||||||
- [ ] Complete TCP/IP networking stack
|
- [ ] Complete TCP/IP networking stack
|
||||||
- [ ] Graphics and display support
|
- [ ] Graphics and display support
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ set -e # Exit on any error
|
|||||||
echo "=== Rust Kernel Build and Test Script ==="
|
echo "=== Rust Kernel Build and Test Script ==="
|
||||||
echo "Starting comprehensive build and validation..."
|
echo "Starting comprehensive build and validation..."
|
||||||
|
|
||||||
|
# Enable unstable features on stable compiler
|
||||||
|
export RUSTC_BOOTSTRAP=1
|
||||||
|
|
||||||
# Colors for output
|
# Colors for output
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -84,7 +87,7 @@ fi
|
|||||||
# Run Clippy lints (if available)
|
# Run Clippy lints (if available)
|
||||||
print_status "Running Clippy lints..."
|
print_status "Running Clippy lints..."
|
||||||
if command -v cargo-clippy &> /dev/null; then
|
if command -v cargo-clippy &> /dev/null; then
|
||||||
if RUSTFLAGS="-Awarnings" cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then
|
if cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then
|
||||||
print_success "Clippy lints passed"
|
print_success "Clippy lints passed"
|
||||||
else
|
else
|
||||||
print_warning "Clippy found issues (continuing with build)"
|
print_warning "Clippy found issues (continuing with build)"
|
||||||
@@ -97,18 +100,18 @@ fi
|
|||||||
|
|
||||||
# Build in debug mode
|
# Build in debug mode
|
||||||
print_status "Building kernel in debug mode..."
|
print_status "Building kernel in debug mode..."
|
||||||
run_with_status "RUSTFLAGS='-Awarnings' cargo check" "Debug build check"
|
run_with_status "cargo check" "Debug build check"
|
||||||
print_success "Debug build completed successfully"
|
print_success "Debug build completed successfully"
|
||||||
|
|
||||||
# Build in release mode
|
# Build in release mode
|
||||||
print_status "Building kernel in release mode..."
|
print_status "Building kernel in release mode..."
|
||||||
run_with_status "RUSTFLAGS='-Awarnings' cargo check --release" "Release build check"
|
run_with_status "cargo check --release" "Release build check"
|
||||||
print_success "Release build completed successfully"
|
print_success "Release build completed successfully"
|
||||||
|
|
||||||
# Build with make (if Makefile exists)
|
# Build with make (if Makefile exists)
|
||||||
if [ -f "Makefile" ]; then
|
if [ -f "Makefile" ]; then
|
||||||
print_status "Building with Makefile..."
|
print_status "Building with Makefile..."
|
||||||
run_with_status "RUSTFLAGS='-Awarnings' make kernel" "Makefile build"
|
run_with_status "make kernel" "Makefile build"
|
||||||
print_success "Makefile build completed successfully"
|
print_success "Makefile build completed successfully"
|
||||||
else
|
else
|
||||||
print_warning "Makefile not found, skipping make build"
|
print_warning "Makefile not found, skipping make build"
|
||||||
@@ -125,6 +128,15 @@ if [ -f "target/release/deps/kernel-"*.rlib ]; then
|
|||||||
print_status "Kernel library size: $KERNEL_SIZE"
|
print_status "Kernel library size: $KERNEL_SIZE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Create ISO
|
||||||
|
print_status "Creating bootable ISO..."
|
||||||
|
cp target/x86_64-unknown-none/release/rust-kernel iso/boot/rust-kernel
|
||||||
|
if grub-mkrescue -o rust-kernel.iso iso > /dev/null 2>&1; then
|
||||||
|
print_success "ISO created: rust-kernel.iso"
|
||||||
|
else
|
||||||
|
print_warning "Failed to create ISO (grub-mkrescue not found or failed)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create build report
|
# Create build report
|
||||||
BUILD_REPORT="build_report.txt"
|
BUILD_REPORT="build_report.txt"
|
||||||
print_status "Generating build report..."
|
print_status "Generating build report..."
|
||||||
@@ -198,7 +210,7 @@ print_status "Test suites available: 15+ test categories"
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "To test the kernel:"
|
echo "To test the kernel:"
|
||||||
echo " 1. Boot in QEMU: qemu-system-x86_64 -kernel target/release/..."
|
echo " 1. Boot in QEMU: qemu-system-x86_64 -cdrom rust-kernel.iso"
|
||||||
echo " 2. Use shell commands like: 'test run', 'sysinfo', 'health'"
|
echo " 2. Use shell commands like: 'test run', 'sysinfo', 'health'"
|
||||||
echo " 3. Monitor system status with: 'diag', 'perf', 'mem'"
|
echo " 3. Monitor system status with: 'diag', 'perf', 'mem'"
|
||||||
|
|
||||||
|
|||||||
28
clippy.toml
28
clippy.toml
@@ -1,21 +1,21 @@
|
|||||||
# Clippy configuration for kernel development
|
# Clippy configuration for kernel development
|
||||||
|
|
||||||
allow = [
|
# allow = [
|
||||||
"clippy::missing_errors_doc",
|
# "clippy::missing_errors_doc",
|
||||||
"clippy::missing_panics_doc",
|
# "clippy::missing_panics_doc",
|
||||||
"clippy::module_name_repetitions",
|
# "clippy::module_name_repetitions",
|
||||||
"clippy::inline_always",
|
# "clippy::inline_always",
|
||||||
"clippy::must_use_candidate",
|
# "clippy::must_use_candidate",
|
||||||
"clippy::cast_possible_truncation",
|
# "clippy::cast_possible_truncation",
|
||||||
"clippy::cast_sign_loss",
|
# "clippy::cast_sign_loss",
|
||||||
"clippy::cast_possible_wrap",
|
# "clippy::cast_possible_wrap",
|
||||||
"clippy::similar_names",
|
# "clippy::similar_names",
|
||||||
"clippy::too_many_lines",
|
# "clippy::too_many_lines",
|
||||||
"clippy::unused_self"
|
# "clippy::unused_self"
|
||||||
]
|
# ]
|
||||||
|
|
||||||
# Kernel-specific thresholds
|
# Kernel-specific thresholds
|
||||||
cognitive-complexity-threshold = 30
|
cognitive-complexity-threshold = 30
|
||||||
too-many-arguments-threshold = 8
|
too-many-arguments-threshold = 8
|
||||||
type-complexity-threshold = 250
|
type-complexity-threshold = 250
|
||||||
single-char-lifetime-names-threshold = 4
|
# single-char-lifetime-names-threshold = 4
|
||||||
|
|||||||
7
iso/boot/grub/grub.cfg
Archivo normal
7
iso/boot/grub/grub.cfg
Archivo normal
@@ -0,0 +1,7 @@
|
|||||||
|
set timeout=0
|
||||||
|
set default=0
|
||||||
|
|
||||||
|
menuentry "Rust Kernel" {
|
||||||
|
multiboot2 /boot/rust-kernel
|
||||||
|
boot
|
||||||
|
}
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
[target.x86_64-unknown-none]
|
[target.x86_64-unknown-none]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-arg=-Tlinker.ld",
|
|
||||||
"-C", "link-arg=--no-dynamic-linker",
|
"-C", "link-arg=--no-dynamic-linker",
|
||||||
"-C", "link-arg=-static",
|
"-C", "link-arg=-static",
|
||||||
|
"-C", "relocation-model=static",
|
||||||
|
"-C", "link-arg=-no-pie",
|
||||||
|
"-C", "link-arg=-z",
|
||||||
|
"-C", "link-arg=max-page-size=0x1000",
|
||||||
|
"-C", "link-arg=--oformat=elf64-x86-64",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-none"
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "alloc"]
|
||||||
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ crate-type = ["rlib"]
|
|||||||
spin = "0.9"
|
spin = "0.9"
|
||||||
bitflags = "2.4"
|
bitflags = "2.4"
|
||||||
linked_list_allocator = "0.10"
|
linked_list_allocator = "0.10"
|
||||||
once_cell = { version = "1.19", default-features = false, features = ["critical-section"] }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Build assembly files with rustc
|
|
||||||
println!("cargo:rerun-if-changed=src/arch/x86_64/boot.s");
|
|
||||||
println!("cargo:rerun-if-changed=src/arch/x86_64/exceptions.s");
|
|
||||||
println!("cargo:rerun-if-changed=linker.ld");
|
println!("cargo:rerun-if-changed=linker.ld");
|
||||||
|
|
||||||
// Tell Cargo to link against the linker script
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
println!("cargo:rustc-link-arg=-Tlinker.ld");
|
|
||||||
|
// Copy linker script to OUT_DIR so the linker can find it
|
||||||
|
let linker_script = out_dir.join("linker.ld");
|
||||||
|
fs::copy("linker.ld", &linker_script).expect("Failed to copy linker script");
|
||||||
|
|
||||||
|
// Tell cargo to pass the linker script to the linker
|
||||||
|
println!("cargo:rustc-link-arg=-T{}", linker_script.display());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,36 +3,51 @@
|
|||||||
|
|
||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
|
/* Program headers (segments) for ELF */
|
||||||
|
PHDRS
|
||||||
|
{
|
||||||
|
text PT_LOAD FLAGS(5); /* Read + Execute */
|
||||||
|
rodata PT_LOAD FLAGS(4); /* Read only */
|
||||||
|
data PT_LOAD FLAGS(6); /* Read + Write */
|
||||||
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* Start at 1MB (standard kernel load address) */
|
/* Start at 1MB (standard kernel load address) */
|
||||||
. = 1M;
|
. = 0x100000;
|
||||||
|
|
||||||
/* Multiboot header must be early */
|
/* Multiboot header MUST be first */
|
||||||
.multiboot_header : {
|
.multiboot_header : ALIGN(8) {
|
||||||
*(.multiboot_header)
|
KEEP(*(.multiboot_header))
|
||||||
}
|
} :text
|
||||||
|
|
||||||
|
/* Boot and text sections */
|
||||||
|
.boot : ALIGN(8) {
|
||||||
|
*(.text._start)
|
||||||
|
*(.boot)
|
||||||
|
} :text
|
||||||
|
|
||||||
|
/* Rest of code section */
|
||||||
|
.text : ALIGN(4K) {
|
||||||
|
*(.text .text.*)
|
||||||
|
} :text
|
||||||
|
|
||||||
/* Read-only sections */
|
/* Read-only sections */
|
||||||
.rodata : ALIGN(4K) {
|
.rodata : ALIGN(4K) {
|
||||||
*(.rodata .rodata.*)
|
*(.rodata .rodata.*)
|
||||||
}
|
} :rodata
|
||||||
|
|
||||||
/* Code section */
|
|
||||||
.text : ALIGN(4K) {
|
|
||||||
*(.text .text.*)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read-write data */
|
/* Read-write data */
|
||||||
.data : ALIGN(4K) {
|
.data : ALIGN(4K) {
|
||||||
*(.data .data.*)
|
*(.data .data.*)
|
||||||
}
|
*(.got .got.*)
|
||||||
|
} :data
|
||||||
|
|
||||||
/* BSS section (uninitialized data) */
|
/* BSS section (uninitialized data) */
|
||||||
.bss : ALIGN(4K) {
|
.bss : ALIGN(4K) {
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
}
|
} :data
|
||||||
|
|
||||||
/* Kernel end marker */
|
/* Kernel end marker */
|
||||||
__kernel_end = .;
|
__kernel_end = .;
|
||||||
|
|||||||
67
kernel/src/arch/x86_64/boot.S
Archivo normal
67
kernel/src/arch/x86_64/boot.S
Archivo normal
@@ -0,0 +1,67 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/* Multiboot header for the Rust kernel */
|
||||||
|
|
||||||
|
.section .multiboot_header
|
||||||
|
.align 8
|
||||||
|
|
||||||
|
/* Multiboot2 header */
|
||||||
|
multiboot_header_start:
|
||||||
|
.long 0xe85250d6 /* magic number */
|
||||||
|
.long 0 /* architecture: i386 */
|
||||||
|
.long multiboot_header_end - multiboot_header_start /* header length */
|
||||||
|
.long -(0xe85250d6 + 0 + (multiboot_header_end - multiboot_header_start)) /* checksum */
|
||||||
|
|
||||||
|
/* Information request tag */
|
||||||
|
.short 1 /* type */
|
||||||
|
.short 0 /* flags */
|
||||||
|
.long 12 /* size */
|
||||||
|
.long 6 /* memory map */
|
||||||
|
|
||||||
|
/* End tag */
|
||||||
|
.short 0 /* type */
|
||||||
|
.short 0 /* flags */
|
||||||
|
.long 8 /* size */
|
||||||
|
multiboot_header_end:
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.global _start
|
||||||
|
.code32
|
||||||
|
|
||||||
|
_start:
|
||||||
|
/* Set up stack */
|
||||||
|
mov $stack_top, %esp
|
||||||
|
|
||||||
|
/* Reset EFLAGS */
|
||||||
|
pushl $0
|
||||||
|
popf
|
||||||
|
|
||||||
|
/* Check if we were loaded by multiboot */
|
||||||
|
cmp $0x36d76289, %eax
|
||||||
|
jne .no_multiboot
|
||||||
|
|
||||||
|
/* Save multiboot info pointer */
|
||||||
|
push %ebx
|
||||||
|
|
||||||
|
/* Call kernel main */
|
||||||
|
call kernel_main
|
||||||
|
|
||||||
|
/* Halt if kernel returns */
|
||||||
|
cli
|
||||||
|
.hang:
|
||||||
|
hlt
|
||||||
|
jmp .hang
|
||||||
|
|
||||||
|
.no_multiboot:
|
||||||
|
/* No multiboot - just halt */
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
.align 16
|
||||||
|
stack_bottom:
|
||||||
|
.skip 16384 /* 16KB stack */
|
||||||
|
stack_top:
|
||||||
|
|
||||||
|
.section .data
|
||||||
|
multiboot_info_ptr:
|
||||||
|
.long 0
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# Rust Kernel boot entry point for x86_64
|
# Rust Kernel boot entry point for x86_64
|
||||||
|
|
||||||
.section .multiboot_header
|
.section .multiboot_header, "a"
|
||||||
|
# Multiboot 1 Header
|
||||||
|
.align 4
|
||||||
|
.long 0x1BADB002 # magic
|
||||||
|
.long 0x00000003 # flags (align + meminfo)
|
||||||
|
.long -(0x1BADB002 + 0x00000003) # checksum
|
||||||
|
|
||||||
|
# Multiboot 2 Header
|
||||||
|
.align 8
|
||||||
header_start:
|
header_start:
|
||||||
# Multiboot2 header
|
# Multiboot2 header
|
||||||
.long 0xe85250d6 # magic number
|
.long 0xe85250d6 # magic number
|
||||||
@@ -21,6 +29,8 @@ header_end:
|
|||||||
.section .bss
|
.section .bss
|
||||||
multiboot_magic_store:
|
multiboot_magic_store:
|
||||||
.skip 4
|
.skip 4
|
||||||
|
multiboot_info_store:
|
||||||
|
.skip 4
|
||||||
# Stack for the kernel
|
# Stack for the kernel
|
||||||
.global stack_bottom
|
.global stack_bottom
|
||||||
.global stack_top
|
.global stack_top
|
||||||
@@ -30,16 +40,16 @@ stack_top:
|
|||||||
|
|
||||||
# Bootstrap page tables
|
# Bootstrap page tables
|
||||||
.align 4096
|
.align 4096
|
||||||
.global boot_page_directory_ptr_table
|
.global boot_pml4
|
||||||
boot_page_directory_ptr_table:
|
boot_pml4:
|
||||||
.skip 4096
|
.skip 4096
|
||||||
|
|
||||||
.global boot_page_directory_table
|
.global boot_pdp
|
||||||
boot_page_directory_table:
|
boot_pdp:
|
||||||
.skip 4096
|
.skip 4096
|
||||||
|
|
||||||
.global boot_page_table
|
.global boot_pd
|
||||||
boot_page_table:
|
boot_pd:
|
||||||
.skip 4096
|
.skip 4096
|
||||||
|
|
||||||
.section .rodata
|
.section .rodata
|
||||||
@@ -49,7 +59,7 @@ gdt64:
|
|||||||
.quad (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) # code segment
|
.quad (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) # code segment
|
||||||
.set gdt64.data, . - gdt64
|
.set gdt64.data, . - gdt64
|
||||||
.quad (1<<44) | (1<<47) | (1<<41) # data segment
|
.quad (1<<44) | (1<<47) | (1<<41) # data segment
|
||||||
.set gdt64.pointer, . - gdt64
|
gdt64.pointer:
|
||||||
.word . - gdt64 - 1 # length
|
.word . - gdt64 - 1 # length
|
||||||
.quad gdt64 # address
|
.quad gdt64 # address
|
||||||
|
|
||||||
@@ -61,13 +71,21 @@ _start:
|
|||||||
movl $stack_top, %esp
|
movl $stack_top, %esp
|
||||||
movl %esp, %ebp
|
movl %esp, %ebp
|
||||||
|
|
||||||
# Save multiboot information before we lose it
|
# Save multiboot information before we lose it or clobber EAX
|
||||||
movl %eax, multiboot_magic_store
|
movl %eax, multiboot_magic_store
|
||||||
movl %ebx, multiboot_info_store
|
movl %ebx, multiboot_info_store
|
||||||
|
|
||||||
|
# Restore magic for check (or use stored value)
|
||||||
|
movl multiboot_magic_store, %eax
|
||||||
|
|
||||||
# Check for multiboot
|
# Check for multiboot
|
||||||
cmpl $0x36d76289, %eax
|
cmpl $0x36d76289, %eax
|
||||||
jne no_multiboot
|
je .multiboot_ok
|
||||||
|
cmpl $0x2BADB002, %eax
|
||||||
|
je .multiboot_ok
|
||||||
|
jmp no_multiboot
|
||||||
|
|
||||||
|
.multiboot_ok:
|
||||||
|
|
||||||
# Check for CPUID
|
# Check for CPUID
|
||||||
call check_cpuid
|
call check_cpuid
|
||||||
@@ -88,7 +106,7 @@ _start:
|
|||||||
movl %eax, %cr4
|
movl %eax, %cr4
|
||||||
|
|
||||||
# Load page table
|
# Load page table
|
||||||
movl $boot_page_directory_ptr_table, %eax
|
movl $boot_pml4, %eax
|
||||||
movl %eax, %cr3
|
movl %eax, %cr3
|
||||||
|
|
||||||
# Enable long mode
|
# Enable long mode
|
||||||
@@ -121,7 +139,7 @@ check_cpuid:
|
|||||||
pushl %ecx
|
pushl %ecx
|
||||||
popfl
|
popfl
|
||||||
cmpl %ecx, %eax
|
cmpl %ecx, %eax
|
||||||
sete %al
|
setne %al
|
||||||
movzbl %al, %eax
|
movzbl %al, %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@@ -136,7 +154,7 @@ check_long_mode:
|
|||||||
movl $0x80000001, %eax
|
movl $0x80000001, %eax
|
||||||
cpuid
|
cpuid
|
||||||
testl $1 << 29, %edx
|
testl $1 << 29, %edx
|
||||||
setz %al
|
setnz %al
|
||||||
movzbl %al, %eax
|
movzbl %al, %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@@ -145,30 +163,28 @@ check_long_mode:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
setup_page_tables:
|
setup_page_tables:
|
||||||
# Map first 2MB with 2MB pages
|
# Map PML4[0] -> PDP
|
||||||
# PDP table entry
|
movl $boot_pdp, %eax
|
||||||
movl $boot_page_directory_table, %eax
|
|
||||||
orl $0b11, %eax # present + writable
|
orl $0b11, %eax # present + writable
|
||||||
movl %eax, boot_page_directory_ptr_table
|
movl %eax, boot_pml4
|
||||||
|
|
||||||
# PD table entry
|
# Map PDP[0] -> PD
|
||||||
movl $boot_page_table, %eax
|
movl $boot_pd, %eax
|
||||||
orl $0b11, %eax # present + writable
|
orl $0b11, %eax # present + writable
|
||||||
movl %eax, boot_page_directory_table
|
movl %eax, boot_pdp
|
||||||
|
|
||||||
# Page table entries (identity map first 2MB)
|
# Map PD[0..511] -> 2MB Pages (Identity map 0-1GB)
|
||||||
movl $boot_page_table, %edi
|
movl $boot_pd, %edi
|
||||||
movl $0, %ebx
|
movl $0, %ebx # Physical address
|
||||||
movl $512, %ecx
|
movl $512, %ecx # 512 entries
|
||||||
|
|
||||||
.map_page_table:
|
.map_pd_loop:
|
||||||
movl %ebx, %eax
|
movl %ebx, %eax
|
||||||
shll $12, %eax # multiply by 4096 (page size)
|
orl $0b10000011, %eax # present + writable + huge (2MB)
|
||||||
orl $0b11, %eax # present + writable
|
|
||||||
movl %eax, (%edi)
|
movl %eax, (%edi)
|
||||||
addl $8, %edi
|
addl $8, %edi # Next entry
|
||||||
incl %ebx
|
addl $0x200000, %ebx # Next 2MB
|
||||||
loop .map_page_table
|
loop .map_pd_loop
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@@ -228,6 +244,10 @@ print_string:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
no_multiboot:
|
no_multiboot:
|
||||||
|
# DEBUG: Print 'M' to serial port COM1
|
||||||
|
mov $0x3f8, %dx
|
||||||
|
mov $'M', %al
|
||||||
|
out %al, %dx
|
||||||
movl $no_multiboot_msg, %esi
|
movl $no_multiboot_msg, %esi
|
||||||
call print_string_32
|
call print_string_32
|
||||||
jmp halt32
|
jmp halt32
|
||||||
@@ -272,3 +292,5 @@ no_cpuid_msg:
|
|||||||
|
|
||||||
no_long_mode_msg:
|
no_long_mode_msg:
|
||||||
.asciz "ERROR: Long mode not supported"
|
.asciz "ERROR: Long mode not supported"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
# Kernel entry point - multiboot compliant
|
|
||||||
|
|
||||||
.section .multiboot
|
|
||||||
.align 4
|
|
||||||
|
|
||||||
# Multiboot header
|
|
||||||
multiboot_header:
|
|
||||||
.long 0x1BADB002 # Magic number
|
|
||||||
.long 0x00000003 # Flags (align modules on page boundaries + memory info)
|
|
||||||
.long -(0x1BADB002 + 0x00000003) # Checksum
|
|
||||||
|
|
||||||
.section .bss
|
|
||||||
.align 16
|
|
||||||
stack_bottom:
|
|
||||||
.skip 16384 # 16 KB stack
|
|
||||||
stack_top:
|
|
||||||
|
|
||||||
.section .text
|
|
||||||
.global _start
|
|
||||||
.type _start, @function
|
|
||||||
|
|
||||||
_start:
|
|
||||||
# Set up the stack
|
|
||||||
mov $stack_top, %esp
|
|
||||||
|
|
||||||
# Reset EFLAGS
|
|
||||||
pushl $0
|
|
||||||
popf
|
|
||||||
|
|
||||||
# Push multiboot parameters
|
|
||||||
pushl %ebx # Multiboot info structure
|
|
||||||
pushl %eax # Multiboot magic number
|
|
||||||
|
|
||||||
# Call the kernel main function
|
|
||||||
call kernel_main_multiboot
|
|
||||||
|
|
||||||
# If kernel returns (shouldn't happen), hang
|
|
||||||
cli
|
|
||||||
hang:
|
|
||||||
hlt
|
|
||||||
jmp hang
|
|
||||||
|
|
||||||
.size _start, . - _start
|
|
||||||
@@ -166,31 +166,87 @@ impl Context {
|
|||||||
|
|
||||||
/// Restore CPU context and switch to it
|
/// Restore CPU context and switch to it
|
||||||
pub unsafe fn restore(&self) -> ! {
|
pub unsafe fn restore(&self) -> ! {
|
||||||
// For now, implement a simplified version that doesn't cause register pressure
|
// Restore context using the pointer to self (passed in rdi)
|
||||||
// TODO: Implement full context switching with proper register restoration
|
|
||||||
|
|
||||||
// Restore page table
|
|
||||||
asm!("mov cr3, {}", in(reg) self.cr3);
|
|
||||||
|
|
||||||
// Set up a minimal context switch by jumping to the target RIP
|
|
||||||
// This is a simplified version - a full implementation would restore all
|
|
||||||
// registers
|
|
||||||
asm!(
|
asm!(
|
||||||
"mov rsp, {}",
|
// Restore CR3 (Page Table)
|
||||||
"push {}", // CS for iretq
|
"mov rax, [rdi + 144]",
|
||||||
"push {}", // RIP for iretq
|
"mov cr3, rax",
|
||||||
"pushfq", // Push current flags
|
|
||||||
|
// Switch stack to the target stack
|
||||||
|
"mov rsp, [rdi + 56]",
|
||||||
|
|
||||||
|
// Construct interrupt stack frame for iretq
|
||||||
|
// Stack layout: SS, RSP, RFLAGS, CS, RIP
|
||||||
|
|
||||||
|
// SS
|
||||||
|
"movzx rax, word ptr [rdi + 162]",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// RSP (target stack pointer)
|
||||||
|
"mov rax, [rdi + 56]",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// RFLAGS
|
||||||
|
"mov rax, [rdi + 136]",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// CS
|
||||||
|
"movzx rax, word ptr [rdi + 152]",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// RIP
|
||||||
|
"mov rax, [rdi + 128]",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// Push General Purpose Registers onto the new stack
|
||||||
|
// We push them in reverse order of popping
|
||||||
|
"push qword ptr [rdi + 0]", // rax
|
||||||
|
"push qword ptr [rdi + 8]", // rbx
|
||||||
|
"push qword ptr [rdi + 16]", // rcx
|
||||||
|
"push qword ptr [rdi + 24]", // rdx
|
||||||
|
"push qword ptr [rdi + 32]", // rsi
|
||||||
|
"push qword ptr [rdi + 40]", // rdi
|
||||||
|
"push qword ptr [rdi + 48]", // rbp
|
||||||
|
// rsp is handled by stack switch
|
||||||
|
"push qword ptr [rdi + 64]", // r8
|
||||||
|
"push qword ptr [rdi + 72]", // r9
|
||||||
|
"push qword ptr [rdi + 80]", // r10
|
||||||
|
"push qword ptr [rdi + 88]", // r11
|
||||||
|
"push qword ptr [rdi + 96]", // r12
|
||||||
|
"push qword ptr [rdi + 104]", // r13
|
||||||
|
"push qword ptr [rdi + 112]", // r14
|
||||||
|
"push qword ptr [rdi + 120]", // r15
|
||||||
|
|
||||||
|
// Restore Segment Registers
|
||||||
|
"mov ax, [rdi + 154]", // ds
|
||||||
|
"mov ds, ax",
|
||||||
|
"mov ax, [rdi + 156]", // es
|
||||||
|
"mov es, ax",
|
||||||
|
"mov ax, [rdi + 158]", // fs
|
||||||
|
"mov fs, ax",
|
||||||
|
"mov ax, [rdi + 160]", // gs
|
||||||
|
"mov gs, ax",
|
||||||
|
|
||||||
|
// Pop General Purpose Registers
|
||||||
|
"pop r15",
|
||||||
|
"pop r14",
|
||||||
|
"pop r13",
|
||||||
|
"pop r12",
|
||||||
|
"pop r11",
|
||||||
|
"pop r10",
|
||||||
|
"pop r9",
|
||||||
|
"pop r8",
|
||||||
|
"pop rbp",
|
||||||
|
"pop rdi", // This restores the target rdi
|
||||||
|
"pop rsi",
|
||||||
|
"pop rdx",
|
||||||
|
"pop rcx",
|
||||||
|
"pop rbx",
|
||||||
"pop rax",
|
"pop rax",
|
||||||
"or rax, 0x200", // Enable interrupts
|
|
||||||
"push rax", // RFLAGS for iretq
|
// Return from interrupt (restores RIP, CS, RFLAGS, RSP, SS)
|
||||||
"push {}", // CS again
|
|
||||||
"push {}", // RIP again
|
|
||||||
"iretq",
|
"iretq",
|
||||||
in(reg) self.rsp,
|
in("rdi") self,
|
||||||
in(reg) self.cs as u64,
|
|
||||||
in(reg) self.rip,
|
|
||||||
in(reg) self.cs as u64,
|
|
||||||
in(reg) self.rip,
|
|
||||||
options(noreturn)
|
options(noreturn)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ pub fn get_boot_info() -> &'static BootInfo {
|
|||||||
|
|
||||||
/// Update boot information
|
/// Update boot information
|
||||||
pub unsafe fn update_boot_info<F>(f: F)
|
pub unsafe fn update_boot_info<F>(f: F)
|
||||||
where F: FnOnce(&mut BootInfo) {
|
where
|
||||||
|
F: FnOnce(&mut BootInfo),
|
||||||
|
{
|
||||||
f(&mut BOOT_INFO);
|
f(&mut BOOT_INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +116,8 @@ pub mod multiboot {
|
|||||||
pub struct BootMemoryInfo {
|
pub struct BootMemoryInfo {
|
||||||
pub total_memory: u64,
|
pub total_memory: u64,
|
||||||
pub available_memory: u64,
|
pub available_memory: u64,
|
||||||
pub memory_regions: alloc::vec::Vec<MemoryMapEntry>,
|
pub memory_regions: [MemoryMapEntry; 32],
|
||||||
|
pub region_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootMemoryInfo {
|
impl BootMemoryInfo {
|
||||||
@@ -122,7 +125,13 @@ pub mod multiboot {
|
|||||||
Self {
|
Self {
|
||||||
total_memory: 0,
|
total_memory: 0,
|
||||||
available_memory: 0,
|
available_memory: 0,
|
||||||
memory_regions: alloc::vec::Vec::new(),
|
memory_regions: [MemoryMapEntry {
|
||||||
|
base_addr: 0,
|
||||||
|
length: 0,
|
||||||
|
type_: 0,
|
||||||
|
reserved: 0,
|
||||||
|
}; 32],
|
||||||
|
region_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,17 +140,20 @@ pub mod multiboot {
|
|||||||
self.available_memory += entry.length;
|
self.available_memory += entry.length;
|
||||||
}
|
}
|
||||||
self.total_memory += entry.length;
|
self.total_memory += entry.length;
|
||||||
self.memory_regions.push(entry);
|
if self.region_count < 32 {
|
||||||
|
self.memory_regions[self.region_count] = entry;
|
||||||
|
self.region_count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse multiboot2 information and initialize memory management
|
/// Parse multiboot2 information and initialize memory management
|
||||||
pub fn init_memory_from_multiboot(multiboot_addr: usize) -> Result<()> {
|
pub fn init_memory_from_multiboot(multiboot_addr: usize) -> Result<()> {
|
||||||
info!("Parsing multiboot information at 0x{:x}", multiboot_addr);
|
crate::println!("Parsing multiboot information at 0x{:x}", multiboot_addr);
|
||||||
|
|
||||||
let multiboot_info = unsafe { &*(multiboot_addr as *const MultibootInfo) };
|
let multiboot_info = unsafe { &*(multiboot_addr as *const MultibootInfo) };
|
||||||
|
|
||||||
info!("Multiboot info size: {} bytes", multiboot_info.total_size);
|
crate::println!("Multiboot info size: {} bytes", multiboot_info.total_size);
|
||||||
|
|
||||||
// Parse memory map from multiboot info
|
// Parse memory map from multiboot info
|
||||||
let mut memory_info = BootMemoryInfo::new();
|
let mut memory_info = BootMemoryInfo::new();
|
||||||
@@ -166,12 +178,13 @@ pub mod multiboot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize page allocator with available memory
|
// Initialize page allocator with available memory
|
||||||
for region in &memory_info.memory_regions {
|
for i in 0..memory_info.region_count {
|
||||||
|
let region = &memory_info.memory_regions[i];
|
||||||
if region.type_ == memory_type::AVAILABLE {
|
if region.type_ == memory_type::AVAILABLE {
|
||||||
let start_pfn = region.base_addr / 4096;
|
let start_pfn = region.base_addr / 4096;
|
||||||
let end_pfn = (region.base_addr + region.length) / 4096;
|
let end_pfn = (region.base_addr + region.length) / 4096;
|
||||||
|
|
||||||
info!(
|
crate::println!(
|
||||||
"Adding memory region: 0x{:x}-0x{:x}",
|
"Adding memory region: 0x{:x}-0x{:x}",
|
||||||
region.base_addr,
|
region.base_addr,
|
||||||
region.base_addr + region.length
|
region.base_addr + region.length
|
||||||
@@ -185,9 +198,9 @@ pub mod multiboot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Memory initialization from multiboot completed");
|
crate::println!("Memory initialization from multiboot completed");
|
||||||
info!("Total memory: {} bytes", memory_info.total_memory);
|
crate::println!("Total memory: {} bytes", memory_info.total_memory);
|
||||||
info!("Available memory: {} bytes", memory_info.available_memory);
|
crate::println!("Available memory: {} bytes", memory_info.available_memory);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -462,7 +462,9 @@ static ENHANCED_SCHEDULER: Spinlock<Option<EnhancedScheduler>> = Spinlock::new(N
|
|||||||
|
|
||||||
/// Helper to get scheduler reference safely
|
/// Helper to get scheduler reference safely
|
||||||
fn with_scheduler<T, F>(f: F) -> Option<T>
|
fn with_scheduler<T, F>(f: F) -> Option<T>
|
||||||
where F: FnOnce(&mut EnhancedScheduler) -> T {
|
where
|
||||||
|
F: FnOnce(&mut EnhancedScheduler) -> T,
|
||||||
|
{
|
||||||
let mut scheduler_option = ENHANCED_SCHEDULER.lock();
|
let mut scheduler_option = ENHANCED_SCHEDULER.lock();
|
||||||
if let Some(ref mut scheduler) = *scheduler_option {
|
if let Some(ref mut scheduler) = *scheduler_option {
|
||||||
Some(f(scheduler))
|
Some(f(scheduler))
|
||||||
@@ -473,7 +475,9 @@ where F: FnOnce(&mut EnhancedScheduler) -> T {
|
|||||||
|
|
||||||
/// Helper to get read-only scheduler reference safely
|
/// Helper to get read-only scheduler reference safely
|
||||||
fn with_scheduler_read<T, F>(f: F) -> Option<T>
|
fn with_scheduler_read<T, F>(f: F) -> Option<T>
|
||||||
where F: FnOnce(&EnhancedScheduler) -> T {
|
where
|
||||||
|
F: FnOnce(&EnhancedScheduler) -> T,
|
||||||
|
{
|
||||||
let scheduler_option = ENHANCED_SCHEDULER.lock();
|
let scheduler_option = ENHANCED_SCHEDULER.lock();
|
||||||
if let Some(ref scheduler) = *scheduler_option {
|
if let Some(ref scheduler) = *scheduler_option {
|
||||||
Some(f(scheduler))
|
Some(f(scheduler))
|
||||||
|
|||||||
@@ -257,25 +257,28 @@ impl DentryCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Global dentry cache
|
/// Global dentry cache
|
||||||
static DCACHE: once_cell::sync::Lazy<DentryCache> =
|
static DCACHE: spin::once::Once<DentryCache> = spin::once::Once::new();
|
||||||
once_cell::sync::Lazy::new(|| DentryCache::new());
|
|
||||||
|
fn get_dcache() -> &'static DentryCache {
|
||||||
|
DCACHE.call_once(|| DentryCache::new())
|
||||||
|
}
|
||||||
|
|
||||||
/// Look up dentry in cache
|
/// Look up dentry in cache
|
||||||
pub fn dcache_lookup(path: &str) -> Option<Arc<Dentry>> {
|
pub fn dcache_lookup(path: &str) -> Option<Arc<Dentry>> {
|
||||||
DCACHE.lookup(path)
|
get_dcache().lookup(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert dentry into cache
|
/// Insert dentry into cache
|
||||||
pub fn dcache_insert(path: String, dentry: Arc<Dentry>) {
|
pub fn dcache_insert(path: String, dentry: Arc<Dentry>) {
|
||||||
DCACHE.insert(path, dentry);
|
get_dcache().insert(path, dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove dentry from cache
|
/// Remove dentry from cache
|
||||||
pub fn dcache_remove(path: &str) -> Option<Arc<Dentry>> {
|
pub fn dcache_remove(path: &str) -> Option<Arc<Dentry>> {
|
||||||
DCACHE.remove(path)
|
get_dcache().remove(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prune dentry cache
|
/// Prune dentry cache
|
||||||
pub fn dcache_prune() {
|
pub fn dcache_prune() {
|
||||||
DCACHE.prune();
|
get_dcache().prune();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,12 +194,15 @@ impl MountNamespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Global mount namespace
|
/// Global mount namespace
|
||||||
static INIT_MNT_NS: once_cell::sync::Lazy<Mutex<MountNamespace>> =
|
static INIT_MNT_NS: spin::once::Once<Mutex<MountNamespace>> = spin::once::Once::new();
|
||||||
once_cell::sync::Lazy::new(|| Mutex::new(MountNamespace::new(1)));
|
|
||||||
|
fn get_init_mnt_ns() -> &'static Mutex<MountNamespace> {
|
||||||
|
INIT_MNT_NS.call_once(|| Mutex::new(MountNamespace::new(1)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the init mount namespace
|
/// Get the init mount namespace
|
||||||
pub fn get_init_ns() -> &'static Mutex<MountNamespace> {
|
pub fn get_init_ns() -> &'static Mutex<MountNamespace> {
|
||||||
&INIT_MNT_NS
|
get_init_mnt_ns()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mount a filesystem
|
/// Mount a filesystem
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
// Include boot assembly
|
||||||
|
// #[cfg(target_arch = "x86_64")]
|
||||||
|
// global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax));
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
pub mod benchmark; // Performance benchmarking
|
pub mod benchmark; // Performance benchmarking
|
||||||
pub mod boot;
|
pub mod boot;
|
||||||
@@ -94,7 +98,7 @@ pub extern "C" fn kernel_main() -> ! {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn kernel_main_multiboot(multiboot_magic: u32, multiboot_addr: u32) -> ! {
|
pub extern "C" fn kernel_main_multiboot(multiboot_magic: u32, multiboot_addr: u32) -> ! {
|
||||||
// Verify multiboot magic number
|
// Verify multiboot magic number
|
||||||
if multiboot_magic != 0x36d76289 {
|
if multiboot_magic != 0x36d76289 && multiboot_magic != 0x2BADB002 {
|
||||||
panic!("Invalid multiboot magic: 0x{:x}", multiboot_magic);
|
panic!("Invalid multiboot magic: 0x{:x}", multiboot_magic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +117,8 @@ fn early_kernel_init() {
|
|||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Rust Kernel v{} starting...", VERSION);
|
crate::println!("Rust Kernel v{} starting...", VERSION);
|
||||||
info!("Early kernel initialization");
|
crate::println!("Early kernel initialization");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize memory management using multiboot information
|
/// Initialize memory management using multiboot information
|
||||||
|
|||||||
55
kernel/src/main.rs
Archivo normal
55
kernel/src/main.rs
Archivo normal
@@ -0,0 +1,55 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
//! Kernel main entry point
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate kernel;
|
||||||
|
|
||||||
|
use core::arch::global_asm;
|
||||||
|
|
||||||
|
// Include boot assembly
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax));
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
/// Multiboot1 header - placed at the very beginning
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(packed)]
|
||||||
|
struct MultibootHeader {
|
||||||
|
magic: u32,
|
||||||
|
flags: u32,
|
||||||
|
checksum: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiboot header must be in the first 8KB and be 4-byte aligned
|
||||||
|
// #[link_section = ".multiboot_header"]
|
||||||
|
// #[no_mangle]
|
||||||
|
// #[used]
|
||||||
|
// static MULTIBOOT_HEADER: MultibootHeader = MultibootHeader {
|
||||||
|
// magic: 0x1BADB002,
|
||||||
|
// flags: 0x00000000,
|
||||||
|
// checksum: 0u32.wrapping_sub(0x1BADB002u32.wrapping_add(0x00000000)),
|
||||||
|
// };
|
||||||
|
|
||||||
|
/// Entry point called by boot.s assembly code
|
||||||
|
/// This is just a wrapper to ensure the kernel crate is linked
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rust_main() -> ! {
|
||||||
|
// This function shouldn't be called directly if boot.s calls kernel_main_multiboot
|
||||||
|
// But if it is called, we redirect to the kernel library
|
||||||
|
loop {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic handler is defined in the kernel library
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn _start() -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
//! Page frame allocator
|
//! Page frame allocator
|
||||||
|
|
||||||
use alloc::collections::BTreeSet;
|
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
@@ -98,34 +97,57 @@ pub static PAGE_ALLOCATOR: Spinlock<PageAllocator> = Spinlock::new(PageAllocator
|
|||||||
|
|
||||||
/// Page allocator implementation
|
/// Page allocator implementation
|
||||||
pub struct PageAllocator {
|
pub struct PageAllocator {
|
||||||
free_pages: BTreeSet<Pfn>,
|
free_list_head: Option<PhysAddr>,
|
||||||
total_pages: usize,
|
total_pages: usize,
|
||||||
allocated_pages: usize,
|
allocated_pages: usize,
|
||||||
|
free_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PageAllocator {
|
impl PageAllocator {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
free_pages: BTreeSet::new(),
|
free_list_head: None,
|
||||||
total_pages: 0,
|
total_pages: 0,
|
||||||
allocated_pages: 0,
|
allocated_pages: 0,
|
||||||
|
free_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a range of pages to the free list
|
/// Add a range of pages to the free list
|
||||||
pub fn add_free_range(&mut self, start: Pfn, count: usize) {
|
pub fn add_free_range(&mut self, start: Pfn, count: usize) {
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
self.free_pages.insert(Pfn(start.0 + i));
|
let pfn = Pfn(start.0 + i);
|
||||||
|
let phys_addr = PhysAddr(pfn.0 * 4096);
|
||||||
|
|
||||||
|
// Store current head in the new page
|
||||||
|
// We can write to phys_addr because it's identity mapped
|
||||||
|
unsafe {
|
||||||
|
let ptr = phys_addr.0 as *mut u64;
|
||||||
|
*ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update head
|
||||||
|
self.free_list_head = Some(phys_addr);
|
||||||
}
|
}
|
||||||
self.total_pages += count;
|
self.total_pages += count;
|
||||||
|
self.free_count += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a single page
|
/// Allocate a single page
|
||||||
fn alloc_page(&mut self) -> Result<Pfn> {
|
fn alloc_page(&mut self) -> Result<Pfn> {
|
||||||
if let Some(pfn) = self.free_pages.iter().next().copied() {
|
if let Some(head_addr) = self.free_list_head {
|
||||||
self.free_pages.remove(&pfn);
|
// Read next ptr from head
|
||||||
|
let next_addr_u64 = unsafe { *(head_addr.0 as *const u64) };
|
||||||
|
|
||||||
|
self.free_list_head = if next_addr_u64 == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(PhysAddr(next_addr_u64 as usize))
|
||||||
|
};
|
||||||
|
|
||||||
self.allocated_pages += 1;
|
self.allocated_pages += 1;
|
||||||
Ok(pfn)
|
self.free_count -= 1;
|
||||||
|
Ok(Pfn(head_addr.0 / 4096))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::OutOfMemory)
|
Err(Error::OutOfMemory)
|
||||||
}
|
}
|
||||||
@@ -133,18 +155,19 @@ impl PageAllocator {
|
|||||||
|
|
||||||
/// Free a single page
|
/// Free a single page
|
||||||
fn free_page(&mut self, pfn: Pfn) {
|
fn free_page(&mut self, pfn: Pfn) {
|
||||||
if self.free_pages.insert(pfn) {
|
let phys_addr = PhysAddr(pfn.0 * 4096);
|
||||||
self.allocated_pages -= 1;
|
unsafe {
|
||||||
|
let ptr = phys_addr.0 as *mut u64;
|
||||||
|
*ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0);
|
||||||
}
|
}
|
||||||
|
self.free_list_head = Some(phys_addr);
|
||||||
|
self.allocated_pages -= 1;
|
||||||
|
self.free_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get statistics
|
/// Get statistics
|
||||||
fn stats(&self) -> (usize, usize, usize) {
|
fn stats(&self) -> (usize, usize, usize) {
|
||||||
(
|
(self.total_pages, self.allocated_pages, self.free_count)
|
||||||
self.total_pages,
|
|
||||||
self.allocated_pages,
|
|
||||||
self.free_pages.len(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,29 @@ impl ProcessTable {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_two_threads_mut(
|
||||||
|
&mut self,
|
||||||
|
tid1: Tid,
|
||||||
|
tid2: Tid,
|
||||||
|
) -> (Option<&mut Thread>, Option<&mut Thread>) {
|
||||||
|
if tid1 == tid2 {
|
||||||
|
let t = self.find_thread_mut(tid1);
|
||||||
|
return (t, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a bit inefficient but safe
|
||||||
|
// We can't easily return two mutable references to the same structure
|
||||||
|
// But since they are in different processes or different threads, they are distinct memory locations.
|
||||||
|
// We can use unsafe to cheat the borrow checker, knowing that tid1 != tid2.
|
||||||
|
|
||||||
|
let ptr = self as *mut ProcessTable;
|
||||||
|
unsafe {
|
||||||
|
let t1 = (*ptr).find_thread_mut(tid1);
|
||||||
|
let t2 = (*ptr).find_thread_mut(tid2);
|
||||||
|
(t1, t2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a new PID
|
/// Allocate a new PID
|
||||||
@@ -279,6 +302,20 @@ pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result<Pid> {
|
|||||||
Ok(pid)
|
Ok(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a thread to the kernel process (PID 0)
|
||||||
|
pub fn add_kernel_thread(tid: Tid, context: Context, stack_pointer: VirtAddr) -> Result<()> {
|
||||||
|
let mut table = PROCESS_TABLE.lock();
|
||||||
|
if let Some(process) = table.get_process_mut(Pid(0)) {
|
||||||
|
let mut thread = Thread::new(tid, Pid(0), 0);
|
||||||
|
thread.context = context;
|
||||||
|
thread.stack_pointer = stack_pointer;
|
||||||
|
process.add_thread(thread);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get current process PID
|
/// Get current process PID
|
||||||
pub fn current_process_pid() -> Option<Pid> {
|
pub fn current_process_pid() -> Option<Pid> {
|
||||||
let table = PROCESS_TABLE.lock();
|
let table = PROCESS_TABLE.lock();
|
||||||
|
|||||||
@@ -458,32 +458,39 @@ impl Scheduler {
|
|||||||
if let Some(current_tid) = self.current {
|
if let Some(current_tid) = self.current {
|
||||||
if current_tid != tid {
|
if current_tid != tid {
|
||||||
// Look up current and next threads
|
// Look up current and next threads
|
||||||
let process_table = PROCESS_TABLE.lock();
|
// We need to use a scope to ensure the lock is dropped before switching
|
||||||
if let (Some(current_thread), Some(next_thread)) = (
|
let (current_ctx_ptr, next_ctx_ptr) = {
|
||||||
process_table.find_thread(current_tid),
|
let mut process_table = PROCESS_TABLE.lock();
|
||||||
process_table.find_thread(tid),
|
|
||||||
) {
|
let (current_thread, next_thread) = process_table
|
||||||
|
.find_two_threads_mut(current_tid, tid);
|
||||||
|
|
||||||
|
let current_ptr = if let Some(t) = current_thread {
|
||||||
|
&mut t.context as *mut Context
|
||||||
|
} else {
|
||||||
|
return; // Current thread not found?
|
||||||
|
};
|
||||||
|
|
||||||
|
let next_ptr = if let Some(t) = next_thread {
|
||||||
|
&t.context as *const Context
|
||||||
|
} else {
|
||||||
|
return; // Next thread not found
|
||||||
|
};
|
||||||
|
|
||||||
|
(current_ptr, next_ptr)
|
||||||
|
};
|
||||||
|
|
||||||
// Update scheduler state
|
// Update scheduler state
|
||||||
self.current = Some(tid);
|
self.current = Some(tid);
|
||||||
self.nr_switches += 1;
|
self.nr_switches += 1;
|
||||||
|
|
||||||
// Drop the lock before context switch to avoid deadlock
|
// Perform the context switch
|
||||||
drop(process_table);
|
// SAFETY: We have valid pointers to the contexts and we've dropped the lock
|
||||||
|
unsafe {
|
||||||
// TODO: Implement actual context switch
|
switch_context(&mut *current_ctx_ptr, &*next_ctx_ptr);
|
||||||
// This would involve:
|
|
||||||
// 1. Saving current thread's context
|
|
||||||
// 2. Loading next thread's context
|
|
||||||
// 3. Switching page tables if different processes
|
|
||||||
// 4. Updating stack pointer and instruction pointer
|
|
||||||
|
|
||||||
crate::info!(
|
|
||||||
"Context switch from TID {} to TID {}",
|
|
||||||
current_tid.0,
|
|
||||||
tid.0
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,3 +661,10 @@ pub fn scheduler_tick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a manual context switch to a specific task
|
||||||
|
/// This is used by the enhanced scheduler to execute its scheduling decisions
|
||||||
|
pub fn context_switch_to(tid: Tid) {
|
||||||
|
let mut scheduler = SCHEDULER.lock();
|
||||||
|
scheduler.switch_to(tid);
|
||||||
|
}
|
||||||
|
|||||||
41
kernel/src/simple_boot.s
Archivo normal
41
kernel/src/simple_boot.s
Archivo normal
@@ -0,0 +1,41 @@
|
|||||||
|
# Simple multiboot2 header and boot code
|
||||||
|
.section .multiboot_header, "a", @progbits
|
||||||
|
.align 8
|
||||||
|
|
||||||
|
multiboot2_header_start:
|
||||||
|
.long 0xe85250d6 # multiboot2 magic
|
||||||
|
.long 0 # architecture (i386)
|
||||||
|
.long multiboot2_header_end - multiboot2_header_start # header length
|
||||||
|
.long -(0xe85250d6 + 0 + (multiboot2_header_end - multiboot2_header_start)) # checksum
|
||||||
|
|
||||||
|
# End tag
|
||||||
|
.word 0 # type
|
||||||
|
.word 0 # flags
|
||||||
|
.long 8 # size
|
||||||
|
multiboot2_header_end:
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.global _start
|
||||||
|
.code32
|
||||||
|
|
||||||
|
_start:
|
||||||
|
# Disable interrupts
|
||||||
|
cli
|
||||||
|
|
||||||
|
# Set up a simple stack
|
||||||
|
mov $stack_top, %esp
|
||||||
|
mov $stack_top, %ebp
|
||||||
|
|
||||||
|
# Call main Rust function
|
||||||
|
call main
|
||||||
|
|
||||||
|
# If main returns, halt
|
||||||
|
halt:
|
||||||
|
hlt
|
||||||
|
jmp halt
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
.align 16
|
||||||
|
stack_bottom:
|
||||||
|
.skip 16384 # 16KB stack
|
||||||
|
stack_top:
|
||||||
@@ -69,7 +69,7 @@ impl CpuInfo {
|
|||||||
unsafe {
|
unsafe {
|
||||||
asm!("mov {ebx_save}, rbx",
|
asm!("mov {ebx_save}, rbx",
|
||||||
"cpuid",
|
"cpuid",
|
||||||
"mov {ebx_out}, ebx",
|
"mov {ebx_out:e}, ebx",
|
||||||
"mov rbx, {ebx_save}",
|
"mov rbx, {ebx_save}",
|
||||||
ebx_save = out(reg) _,
|
ebx_save = out(reg) _,
|
||||||
ebx_out = out(reg) ebx,
|
ebx_out = out(reg) ebx,
|
||||||
@@ -144,7 +144,7 @@ impl CpuInfo {
|
|||||||
|
|
||||||
asm!("mov {ebx_save}, rbx",
|
asm!("mov {ebx_save}, rbx",
|
||||||
"cpuid",
|
"cpuid",
|
||||||
"mov {ebx_out}, ebx",
|
"mov {ebx_out:e}, ebx",
|
||||||
"mov rbx, {ebx_save}",
|
"mov rbx, {ebx_save}",
|
||||||
ebx_save = out(reg) _,
|
ebx_save = out(reg) _,
|
||||||
ebx_out = out(reg) vendor_ebx,
|
ebx_out = out(reg) vendor_ebx,
|
||||||
|
|||||||
@@ -403,8 +403,37 @@ impl TimerWheel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_timer(&mut self, timer: HrTimer) {
|
pub fn add_timer(&mut self, timer: HrTimer) {
|
||||||
// TODO: Add timer to appropriate level based on expiry time
|
let now_ns = get_time_ns();
|
||||||
let level = 0; // Simplified
|
let expires_ns = timer.expires.to_ns();
|
||||||
|
|
||||||
|
// If already expired or expires very soon, put in level 0
|
||||||
|
if expires_ns <= now_ns {
|
||||||
|
self.levels[0].push(timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let delta_ns = expires_ns - now_ns;
|
||||||
|
let delta_jiffies = delta_ns / NSEC_PER_JIFFY;
|
||||||
|
|
||||||
|
// Determine level based on delta
|
||||||
|
// Level 0: Immediate to ~256ms
|
||||||
|
// Level 1: ~256ms to ~16s
|
||||||
|
// Level 2: ~16s to ~17m
|
||||||
|
// Level 3: ~17m to ~18h
|
||||||
|
// Level 4+: Far future
|
||||||
|
let level = if delta_jiffies < 256 {
|
||||||
|
0
|
||||||
|
} else if delta_jiffies < 256 * 64 {
|
||||||
|
1
|
||||||
|
} else if delta_jiffies < 256 * 64 * 64 {
|
||||||
|
2
|
||||||
|
} else if delta_jiffies < 256 * 64 * 64 * 64 {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
};
|
||||||
|
|
||||||
|
let level = core::cmp::min(level, 7);
|
||||||
self.levels[level].push(timer);
|
self.levels[level].push(timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,10 @@ impl TimerState {
|
|||||||
let mut stats = self.stats.lock();
|
let mut stats = self.stats.lock();
|
||||||
stats.context_switches += 1;
|
stats.context_switches += 1;
|
||||||
|
|
||||||
// TODO: Actual context switching would happen here
|
// Perform actual context switch
|
||||||
// This would involve saving current CPU state and
|
// This will save current CPU state and restore the state of the next task
|
||||||
// restoring the state of the next task
|
crate::scheduler::context_switch_to(next_tid);
|
||||||
|
|
||||||
crate::info!(
|
crate::info!(
|
||||||
"Context switch: {:?} -> {:?}",
|
"Context switch: {:?} -> {:?}",
|
||||||
current_tid,
|
current_tid,
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ impl Task {
|
|||||||
function: TaskFunction,
|
function: TaskFunction,
|
||||||
stack_size: usize,
|
stack_size: usize,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
static NEXT_TID: AtomicU32 = AtomicU32::new(1);
|
// Use process subsystem to allocate TID to ensure uniqueness
|
||||||
let tid = Tid(NEXT_TID.fetch_add(1, Ordering::Relaxed));
|
let tid = crate::process::allocate_tid();
|
||||||
|
|
||||||
// Allocate stack
|
// Allocate stack
|
||||||
let stack_ptr = kmalloc::kmalloc(stack_size)?;
|
let stack_ptr = kmalloc::kmalloc(stack_size)?;
|
||||||
@@ -156,10 +156,18 @@ impl TaskManager {
|
|||||||
function: TaskFunction,
|
function: TaskFunction,
|
||||||
stack_size: usize,
|
stack_size: usize,
|
||||||
) -> Result<Tid> {
|
) -> Result<Tid> {
|
||||||
let task = Task::new_kernel_task(name, function, stack_size)?;
|
let task = Task::new_kernel_task(name.clone(), function, stack_size)?;
|
||||||
let tid = task.tid;
|
let tid = task.tid;
|
||||||
|
|
||||||
self.tasks.lock().push(task);
|
// Add to local task list
|
||||||
|
self.tasks.lock().push(task.clone());
|
||||||
|
|
||||||
|
// Add to PROCESS_TABLE so the low-level scheduler can find it
|
||||||
|
crate::process::add_kernel_thread(
|
||||||
|
tid,
|
||||||
|
task.context,
|
||||||
|
crate::memory::VirtAddr::new(task.context.rsp as usize),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Add to enhanced scheduler
|
// Add to enhanced scheduler
|
||||||
crate::enhanced_scheduler::add_task(
|
crate::enhanced_scheduler::add_task(
|
||||||
|
|||||||
@@ -1,24 +1,17 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
//! Rust Kernel
|
//! Rust Kernel Library
|
||||||
//!
|
//!
|
||||||
//! A modern kernel implementation in Rust, inspired by the Linux kernel
|
//! A modern kernel implementation in Rust, inspired by the Linux kernel
|
||||||
//! and utilizing the Rust for Linux infrastructure.
|
//! and utilizing the Rust for Linux infrastructure.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
extern crate kernel;
|
extern crate kernel;
|
||||||
|
|
||||||
// Re-export the kernel crate
|
// Re-export the kernel crate
|
||||||
pub use kernel::*;
|
pub use kernel::*;
|
||||||
|
|
||||||
/// Main kernel entry point
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn _start() -> ! {
|
|
||||||
kernel::kernel_main()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
19
src/main.rs
Archivo normal
19
src/main.rs
Archivo normal
@@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
//! Rust Kernel Binary
|
||||||
|
//!
|
||||||
|
//! Main binary entry point for the Rust kernel
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate kernel;
|
||||||
|
|
||||||
|
use core::arch::global_asm;
|
||||||
|
|
||||||
|
// Include boot assembly
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
global_asm!(
|
||||||
|
include_str!("../kernel/src/arch/x86_64/boot.s"),
|
||||||
|
options(att_syntax)
|
||||||
|
);
|
||||||
Referencia en una nueva incidencia
Block a user