This commit is contained in:
Luc Lenôtre 2024-10-01 21:12:00 +00:00 committed by GitHub
commit bb33890d6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 402 additions and 581 deletions

View File

@ -35,42 +35,51 @@ jobs:
- name: Integration tests - name: Integration tests
working-directory: inttest working-directory: inttest
run: cargo fmt --check run: cargo fmt --check
documentation: book:
runs-on: [self-hosted, linux] runs-on: [self-hosted, linux]
needs: clippy needs: clippy
steps: steps:
- name: Build book - name: Build book
run: mdbook build doc/ run: mdbook build doc/
documentation:
runs-on: [self-hosted, linux]
needs: clippy
strategy:
matrix:
arch: ["x86", "x86_64"]
steps:
- name: Build references - name: Build references
working-directory: kernel working-directory: kernel
run: ci/doc.sh run: cargo doc --target target/${{ matrix.arch }}/${{ matrix.arch }}.json
build: build:
runs-on: [self-hosted, linux] runs-on: [self-hosted, linux]
needs: clippy needs: clippy
strategy:
matrix:
arch: ["x86", "x86_64"]
steps: steps:
- name: Debug - name: Debug
working-directory: kernel working-directory: kernel
run: ci/build.sh run: cargo build --target target/${{ matrix.arch }}/${{ matrix.arch }}.json
- name: Check Multiboot2 for x86 (debug) - name: Check Multiboot2 for x86 (debug)
working-directory: kernel working-directory: kernel
run: grub-file --is-x86-multiboot2 target/x86/debug/maestro run: grub-file --is-x86-multiboot2 target/x86/debug/maestro
- name: Release - name: Release
working-directory: kernel working-directory: kernel
env: run: cargo build --target target/${{ matrix.arch }}/${{ matrix.arch }}.json --release
CARGOFLAGS: --release
run: ci/build.sh
- name: Check Multiboot2 for x86 (release) - name: Check Multiboot2 for x86 (release)
working-directory: kernel working-directory: kernel
run: grub-file --is-x86-multiboot2 target/x86/release/maestro run: grub-file --is-x86-multiboot2 target/x86/release/maestro
strace: strace:
runs-on: [self-hosted, linux] runs-on: [self-hosted, linux]
needs: build needs: build
strategy:
matrix:
arch: ["x86", "x86_64"]
steps: steps:
- name: Build - name: Build
working-directory: kernel working-directory: kernel
env: run: cargo build --target target/${{ matrix.arch }}/${{ matrix.arch }}.json --features strace
CARGOFLAGS: --features strace
run: ci/build.sh
- name: Check Multiboot2 for x86 - name: Check Multiboot2 for x86
working-directory: kernel working-directory: kernel
run: grub-file --is-x86-multiboot2 target/x86/debug/maestro run: grub-file --is-x86-multiboot2 target/x86/debug/maestro
@ -87,6 +96,9 @@ jobs:
selftest: selftest:
runs-on: [self-hosted, linux] runs-on: [self-hosted, linux]
needs: build needs: build
strategy:
matrix:
arch: ["x86", "x86_64"]
steps: steps:
- name: Run utils tests - name: Run utils tests
working-directory: utils working-directory: utils
@ -94,18 +106,31 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
- name: Run kernel tests - name: Run kernel tests
working-directory: kernel working-directory: kernel
env:
CARGOFLAGS: --target arch/${{ matrix.arch }}/${{ matrix.arch }}.json
run: ci/test.sh self run: ci/test.sh self
timeout-minutes: 10 timeout-minutes: 10
inttest: inttest:
runs-on: [self-hosted, linux] runs-on: [self-hosted, linux]
needs: build needs: build
strategy:
matrix:
arch:
- kernel: "x86"
user: "i686-unknown-linux-musl"
- kernel: "x86_64"
user: "x86_64-unknown-linux-musl"
steps: steps:
- name: Build tests - name: Build tests
working-directory: inttest working-directory: inttest
env:
TARGET: ${{ matrix.arch.user }}
run: | run: |
./build.sh ./build.sh
mv disk ../kernel/qemu_disk mv disk ../kernel/qemu_disk
- name: Run - name: Run
working-directory: kernel working-directory: kernel
env:
CARGOFLAGS: --target arch/${{ matrix.arch.kernel }}/${{ matrix.arch.kernel }}.json
run: ci/test.sh int run: ci/test.sh int
timeout-minutes: 10 timeout-minutes: 10

View File

@ -3,7 +3,7 @@
set -e set -e
if [ -z "$TARGET" ]; then if [ -z "$TARGET" ]; then
export TARGET=i686-unknown-linux-musl export TARGET=x86_64-unknown-linux-musl
fi fi
# Build # Build

View File

@ -4,6 +4,9 @@ build-std = ["core", "alloc"]
[target.x86] [target.x86]
runner = "scripts/qemu.sh" runner = "scripts/qemu.sh"
[target.x86_64]
runner = "scripts/qemu.sh"
[build] [build]
# Set default target # Set default target
target = "arch/x86/x86.json" target = "arch/x86_64/x86_64.json"

View File

@ -17,7 +17,7 @@
*/ */
/* /*
* This file is the linker script for the x86 architecture. * The linker script for the x86 architecture.
* *
* The kernel image is split into two parts: * The kernel image is split into two parts:
* - The boot part, in lower memory (sections with the `.boot` prefix) * - The boot part, in lower memory (sections with the `.boot` prefix)
@ -27,10 +27,11 @@
* the kernel image is relocated to higher memory. * the kernel image is relocated to higher memory.
* After running the kernel code, the booting code isn't useful anymore. * After running the kernel code, the booting code isn't useful anymore.
* *
* Sections need to be aligned on the page boundary to be protected against writing (for those * Sections need to be aligned on the page boundary to be protected against
* where it applies). * writing (for those where it applies).
* *
* BSS sections are located right after read-only sections to limit damages if the stack(s) they contain overflows. * BSS sections are located right after read-only sections to limit damages if
* the stack(s) they contain overflows.
*/ */
ENTRY(multiboot_entry) ENTRY(multiboot_entry)

View File

@ -0,0 +1,74 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* The linker script for the x86_64 architecture.
*
* For more information about the organization of this particular file, check
* the documentation in the linker script for the x86 architecture.
*/
ENTRY(multiboot_entry)
SECTIONS
{
. = 0x100000;
.boot.text : ALIGN(4K)
{
*(.boot.text)
}
.boot.stack : ALIGN(4K)
{
*(.boot.stack)
}
.boot.data : ALIGN(4K)
{
*(.boot.data)
}
. = 0xc0200000;
.text : AT (ADDR (.text) - 0xc0000000) ALIGN(4K)
{
*(.text*)
}
.rodata : AT (ADDR (.rodata) - 0xc0000000) ALIGN(4K)
{
*(.rodata*)
}
.user : AT (ADDR (.user) - 0xc0000000) ALIGN(4K)
{
*(.user*)
}
.bss : AT (ADDR (.bss) - 0xc0000000) ALIGN(4K)
{
*(COMMON)
*(.bss*)
}
.data : AT (ADDR (.data) - 0xc0000000) ALIGN(4K)
{
*(.data*)
}
}

View File

@ -0,0 +1,16 @@
{
"arch": "x86_64",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"disable-redzone": true,
"dynamic-linking": true,
"executables": true,
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
"linker": "x86_64-elf-ld",
"linker-flavor": "ld",
"llvm-target": "x86_64-unknown-none",
"os": "none",
"panic-strategy": "abort",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64"
}

View File

@ -1,11 +0,0 @@
#!/bin/sh
EXIT_CODE=0
for arch in $(ls -1 arch/); do
echo "Build for architecture $arch..."
cargo build --target arch/$arch/$arch.json $CARGOFLAGS
EXIT_CODE=$(($EXIT_CODE + $?))
done
exit $EXIT_CODE

View File

@ -1,11 +0,0 @@
#!/bin/sh
EXIT_CODE=0
for arch in $(ls -1 arch/); do
echo "Build documentation for architecture $arch..."
cargo doc --target arch/$arch/$arch.json $CARGOFLAGS
EXIT_CODE=$(($EXIT_CODE + $?))
done
exit $EXIT_CODE

View File

@ -11,7 +11,7 @@ setsid cargo run $CARGOFLAGS >qemu.log 2>&1 &
QEMU_PID=$! QEMU_PID=$!
if [ -z "$ARCH" ]; then if [ -z "$ARCH" ]; then
ARCH="x86" ARCH="x86_64"
fi fi
KERN_PATH="target/$ARCH/debug/maestro" KERN_PATH="target/$ARCH/debug/maestro"

238
kernel/src/boot.rs Normal file
View File

@ -0,0 +1,238 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{
gdt,
memory::{
vmem::x86::{FLAG_PAGE_SIZE, FLAG_PRESENT, FLAG_WRITE},
PhysAddr,
},
};
use core::arch::global_asm;
use utils::limits::PAGE_SIZE;
/// The value of the Multiboot2 magic.
const MULTIBOOT_MAGIC: u32 = 0xe85250d6;
/// Multiboot header tag: End
const MULTIBOOT_HEADER_TAG_END: u16 = 0;
/// Multiboot header tag: The kernel's entry point address
const MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: u16 = 3;
/// The physical address of the GDT.
const GDT_PHYS_ADDR: PhysAddr = PhysAddr(0x800);
/// The header of a multiboot2 tag.
#[repr(C, align(8))]
struct MultibootTagHdr {
/// The tag's type.
r#type: u16,
/// The tag's flags.
///
/// Currently, has only one flag:
/// - `0`: if set, the tag may be considered as optional by the bootloader.
flags: u16,
/// The size of the tag in bytes.
size: u32,
}
/// Layout of the multiboot2 tag indicating the entry point of the kernel.
#[repr(C, align(8))]
struct MultibootEntryAddrTag {
/// The tag's header.
hdr: MultibootTagHdr,
/// The entry point's physical address.
entry_addr: u32,
}
/// Layout of the multiboot2 header.
#[repr(C, align(8))]
struct MultibootHeader {
// Mandatory fields
/// Multiboot magic number.
magic: u32,
/// The CPU architecture to boot for.
architecture: u32,
/// The size of this header in bytes.
header_length: u32,
/// The checksum of the previous fields.
checksum: u32,
/// The entry point tag.
entry_addr_tag: MultibootEntryAddrTag,
/// The end tag.
end_tag: MultibootTagHdr,
}
/// The header used to provide multiboot2 with the necessary information to boot the kernel.
#[no_mangle]
#[link_section = ".boot.rodata"]
pub static MULTIBOOT_HEADER: MultibootHeader = MultibootHeader {
magic: MULTIBOOT_MAGIC,
// x86
architecture: 0,
header_length: size_of::<MultibootHeader>() as _,
// Compute checksum of the previous values
checksum: 0.wrapping_sub(MULTIBOOT_MAGIC + 0 + size_of::<MultibootHeader>()),
entry_addr_tag: MultibootEntryAddrTag {
hdr: MultibootTagHdr {
r#type: MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS,
flags: 0,
size: size_of::<MultibootEntryAddrTag>(),
},
entry_addr: multiboot_entry as _,
},
end_tag: MultibootTagHdr {
r#type: MULTIBOOT_HEADER_TAG_END,
flags: 0,
size: size_of::<MultibootTagHdr>(),
},
};
/// The initial Global Descriptor Table.
#[no_mangle]
#[link_section = ".boot.rodata"]
pub static INIT_GDT: [gdt::Entry; 9] = [
// First entry, empty
gdt::Entry::default(),
// Kernel code segment
gdt::Entry::new(0, !0, 0b10011010, 0b1100),
// Kernel data segment
gdt::Entry::new(0, !0, 0b10010010, 0b1100),
// User code segment
gdt::Entry::new(0, !0, 0b11111010, 0b1100),
// User data segment
gdt::Entry::new(0, !0, 0b11110010, 0b1100),
// TSS
gdt::Entry::default(),
// TLS entries
gdt::Entry::default(),
gdt::Entry::default(),
gdt::Entry::default(),
];
/// A page directory.
#[repr(C, align(8))]
struct PageDir([u32; 1024]);
impl PageDir {
/// Initializes a page directory to remap the kernel to the higher half of the memory.
pub const fn higher_half() -> Self {
let mut dir = [0; 1024];
for i in 0..256 {
let addr = (i * PAGE_SIZE * 1024) as u32;
let ent = addr | FLAG_PAGE_SIZE | FLAG_WRITE | FLAG_PRESENT;
dir[i] = ent;
dir[i + 768] = ent;
}
Self(dir)
}
}
/// The page directory used to remap the kernel to higher memory.
#[no_mangle]
#[link_section = ".boot.rodata"]
pub static REMAP_DIR: PageDir = PageDir::higher_half();
extern "C" {
/// The kernel's entry point.
fn multiboot_entry();
}
global_asm!(
r"
.global multiboot_entry
.type multiboot_entry, @function
.section .boot.text
multiboot_entry:
mov esp, boot_stack_begin
xor ebp, ebp
pushl 0
popf
push ebx
push eax
call setup_gdt
call remap
call kernel_main
# `kernel_main` cannot return
ud2
setup_gdt:
# Copy GDT to its physical address
mov esi, INIT_GDT
mov edi, {GDT_PHYS_ADDR}
mov ecx, {GDT_SIZE}
rep movsb
# Load GDT
pushl {GDT_PHYS_ADDR}
pushw ({GDT_SIZE} - 1)
lgdt [esp]
add esp, 6
jmp 8, complete_flush
complete_flush:
mov ax, GDT_KERNEL_DS
mov ds, ax
mov es, ax
mov ss, ax
mov ax, 0
mov fs, ax
mov gs, ax
ret
/*
* Remaps the first gigabyte of memory to the last one, enabling paging and PSE.
*/
remap:
# Set page directory
mov cr3, {REMAP_DIR_ADDR}
# Enable PSE
mov eax, cr4
or eax, 0x00000010
mov cr4, eax
# Enable paging
mov eax, cr0
or eax, 0x80010000
mov cr0, eax
# Update stack
add esp, 0xc0000000
ret
.section .boot.stack
.align 8
boot_stack:
.size boot_stack, STACK_SIZE
.skip STACK_SIZE
boot_stack_begin:
",
GDT_SIZE = size_of_val(&INIT_GDT),
REMAP_DIR_ADDR = &REMAP_DIR
);

View File

@ -1,107 +0,0 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
.global a20_handle
.type a20_handle, @function
.type a20_check, @function
.type a20_wait_read, @function
.type a20_wait_write, @function
.section .boot.text, "ax"
/*
* Ensures that the A20 line is enabled.
*/
a20_handle:
call a20_check
test $0, %eax
je a20_handle_
ret
a20_handle_:
call a20_enable
ret
/*
* Checks whether the a20 line is enabled or not.
*/
a20_check:
pusha
mov $0x888888, %edi
mov $0x088888, %esi
mov %edi, (%edi)
mov %esi, (%esi)
cmpsl
popa
jne a20_enabled
xor %eax, %eax
ret
a20_enabled:
mov $1, %eax
ret
/*
* Enables the a20 line using the PS2 controller.
* Note: Interrupts must be disabled for this function.
*/
a20_enable:
pushf
cli
call a20_wait_write
mov $0xad, %al
outb %al, $0x64
call a20_wait_write
mov $0xd0, %al
outb %al, $0x64
call a20_wait_read
inb $0x60, %al
push %eax
call a20_wait_write
mov $0xd1, %al
outb %al, $0x64
pop %eax
or $2, %al
outb %al, $0x60
call a20_wait_write
popf
ret
/*
* Waits for the PS2 controller to be available for reading.
*/
a20_wait_read:
in $0x64, %al
test $1, %al
jz a20_wait_read
ret
/*
* Waits for the PS2 controller to be available for writing.
*/
a20_wait_write:
in $0x64, %al
test $2, %al
jnz a20_wait_write
ret

View File

@ -1,114 +0,0 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
.section .boot.text, "ax"
/*
* Constants used by Multiboot2 to detect the kernel.
*/
.set MULTIBOOT_MAGIC, 0xe85250d6
.set MULTIBOOT_ARCHITECTURE, 0
.set HEADER_LENGTH, (header_end - header)
.set CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_ARCHITECTURE + HEADER_LENGTH)
/*
* Multiboot header tags constants.
*/
.set MULTIBOOT_HEADER_TAG_END, 0
.set MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS, 3
/*
* The Multiboot2 kernel header.
*/
.align 8
header:
.long MULTIBOOT_MAGIC
.long MULTIBOOT_ARCHITECTURE
.long HEADER_LENGTH
.long CHECKSUM
/*
* The entry tag, setting the entry point of the kernel.
*/
.align 8
entry_address_tag:
.short MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS
.short 1
.long (entry_address_tag_end - entry_address_tag)
.long multiboot_entry
entry_address_tag_end:
.align 8
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long 8
header_end:
/*
* The size of the kernel stack.
*/
.set STACK_SIZE, 32768
.global boot_stack
.global boot_stack_begin
.global multiboot_entry
.type multiboot_entry, @function
.extern setup_gdt
.extern _init
.extern _fini
/*
* The entry point of the kernel.
*/
multiboot_entry:
mov $boot_stack_begin, %esp
xor %ebp, %ebp
pushl $0
popf
cli
push %eax
push %ebx
call a20_handle
call setup_gdt
call kernel_remap
pop %ebx
pop %eax
mov $(0xc0000000 + boot_stack_begin), %esp
push %ebx
push %eax
call kernel_main
# `kernel_main` cannot return
ud2
.section .boot.stack, "aw"
.align 8
/*
* The kernel stack.
*/
boot_stack:
.size boot_stack, STACK_SIZE
.skip STACK_SIZE
boot_stack_begin:

View File

@ -1,191 +0,0 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
.global GDT_KERNEL_CS
.global GDT_KERNEL_DS
.global GDT_USER_CS
.global GDT_USER_DS
.global GDT_TSS
.global GDT_DESC_VIRT_PTR
.global setup_gdt
.global gdt_move
.type setup_gdt, @function
.type gdt_copy, @function
.type gdt_move, @function
.section .boot.data, "aw"
.align 8
/*
* The beginning of the GDT.
* Every segment covers the whole memory space.
*/
gdt_start:
.quad 0
/*
* Segment for the kernel code.
*/
gdt_kernel_code:
.word 0xffff
.word 0
.byte 0
.byte 0b10011010
.byte 0b11001111
.byte 0
/*
* Segment for the kernel data.
*/
gdt_kernel_data:
.word 0xffff
.word 0
.byte 0
.byte 0b10010010
.byte 0b11001111
.byte 0
/*
* Segment for the user code.
*/
gdt_user_code:
.word 0xffff
.word 0
.byte 0
.byte 0b11111010
.byte 0b11001111
.byte 0
/*
* Segment for the user data.
*/
gdt_user_data:
.word 0xffff
.word 0
.byte 0
.byte 0b11110010
.byte 0b11001111
.byte 0
/*
* Reserved space for the Task State Segment.
*/
gdt_tss:
.quad 0
/*
* TLS GDT entries.
*/
gdt_tls:
.quad 0
.quad 0
.quad 0
/*
* The GDT descriptor.
*/
gdt:
.word gdt - gdt_start - 1
.long gdt_start
/*
* Offsets into the GDT for each segment.
*/
.set GDT_KERNEL_CS, (gdt_kernel_code - gdt_start)
.set GDT_KERNEL_DS, (gdt_kernel_data - gdt_start)
.set GDT_USER_CS, (gdt_user_code - gdt_start)
.set GDT_USER_DS, (gdt_user_data - gdt_start)
.set GDT_TSS, (gdt_tss - gdt_start)
/*
* Physical address to the GDT.
*/
.set GDT_PHYS_PTR, 0x800
/*
* The size of the GDT in bytes.
*/
.set GDT_SIZE, (gdt - gdt_start)
/*
* Physical address to the GDT descriptor.
*/
.set GDT_DESC_PHYS_PTR, (GDT_PHYS_PTR + (gdt - gdt_start))
/*
* Virtual address to the GDT.
*/
.set GDT_VIRT_PTR, (0xc0000000 + GDT_PHYS_PTR)
/*
* Virtual address to the GDT descriptor.
*/
.set GDT_DESC_VIRT_PTR, (GDT_VIRT_PTR + (gdt - gdt_start))
.section .boot.text, "ax"
/*
* Switches the CPU to protected mode.
*/
setup_gdt:
cli
call gdt_copy
mov $GDT_DESC_PHYS_PTR, %eax
movl $GDT_PHYS_PTR, 2(%eax)
lgdt GDT_DESC_PHYS_PTR
mov %cr0, %eax
or $1, %al
mov %eax, %cr0
jmp $GDT_KERNEL_CS, $complete_flush
complete_flush:
mov $GDT_KERNEL_DS, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $0, %ax
mov %ax, %fs
mov %ax, %gs
ret
/*
* Copies the GDT to its physical address.
*/
gdt_copy:
mov $gdt_start, %esi
mov $GDT_PHYS_PTR, %edi
mov $(GDT_SIZE + 6), %ecx
rep movsb
ret
/*
* Moves the GDT to the new virtual address after kernel relocation.
*/
gdt_move:
mov $GDT_DESC_VIRT_PTR, %eax
movl $GDT_VIRT_PTR, 2(%eax)
lgdt GDT_DESC_VIRT_PTR
ret

View File

@ -1,113 +0,0 @@
/*
* Copyright 2024 Luc Lenôtre
*
* This file is part of Maestro.
*
* Maestro is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Maestro is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Maestro. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* This file handles kernel remapping in order to place it in High Memory.
* To do so, paging is enabled using a page directory that remaps the whole
* kernel.
*
* The created page directory has to be replaced when kernel memory management
* is ready.
*/
.section .boot.text, "ax"
.global kernel_remap
.type kernel_remap, @function
.type pse_enable, @function
.extern gdt_move
/*
* Remaps the first gigabyte of memory to the last one.
*
* This function enables PSE.
*/
kernel_remap:
push %ebx
// Zero page directory
xor %eax, %eax
mov $remap_dir, %esi
L1:
movl $0, (%esi)
add $4, %esi
add $1, %eax
cmp $768, %eax
jne L1
// Fill entries
xor %eax, %eax
mov $remap_dir, %esi
L2:
// (i * PAGE_SIZE * 1024)
mov %eax, %ebx
mov $22, %cl
shl %cl, %ebx
// PAGE_SIZE | WRITE | PRESENT
or $(128 + 2 + 1), %ebx
movl %ebx, (%esi)
movl %ebx, (4 * 768)(%esi)
add $4, %esi
add $1, %eax
cmp $256, %eax
jne L2
push $remap_dir
call pse_enable
add $4, %esp
call gdt_move
pop %ebx
ret
/*
* Enables Page Size Extension (PSE) and paging using the given page directory.
*/
pse_enable:
push %ebp
mov %esp, %ebp
push %eax
mov 8(%ebp), %eax
mov %eax, %cr3
mov %cr4, %eax
or $0x00000010, %eax
mov %eax, %cr4
mov %cr0, %eax
or $0x80010000, %eax
mov %eax, %cr0
pop %eax
mov %ebp, %esp
pop %ebp
ret
.section .boot.data, "aw"
/*
* The page directory used for kernel remapping.
*/
.align 4096
remap_dir:
.size remap_dir, 4096
.skip 4096

View File

@ -41,21 +41,32 @@ pub const TSS_OFFSET: usize = 40;
/// The offset of Thread Local Storage (TLS) entries. /// The offset of Thread Local Storage (TLS) entries.
pub const TLS_OFFSET: usize = 48; pub const TLS_OFFSET: usize = 48;
/// Structure representing a GDT entry. /// A GDT entry.
#[repr(transparent)] #[repr(C, align(8))]
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
pub struct Entry(pub u64); pub struct Entry(pub u64);
impl Entry { impl Entry {
/// Creates a new entry with the give information.
#[inline(always)]
pub const fn new(base: u32, limit: u32, access_byte: u8, flags: u8) -> Self {
let mut ent = Self(0);
ent.set_base(base);
ent.set_limit(limit);
ent.set_access_byte(access_byte);
ent.set_flags(flags);
ent
}
/// Returns the entry's base address. /// Returns the entry's base address.
#[inline(always)] #[inline(always)]
pub fn get_base(&self) -> u32 { pub const fn get_base(&self) -> u32 {
(((self.0 >> 16) & 0xffffff) | ((self.0 >> 32) & 0xff000000)) as _ (((self.0 >> 16) & 0xffffff) | ((self.0 >> 32) & 0xff000000)) as _
} }
/// Sets the entry's base address. /// Sets the entry's base address.
#[inline(always)] #[inline(always)]
pub fn set_base(&mut self, base: u32) { pub const fn set_base(&mut self, base: u32) {
self.0 &= !(0xffffff << 16); self.0 &= !(0xffffff << 16);
self.0 &= !(0xff << 56); self.0 &= !(0xff << 56);
@ -65,7 +76,7 @@ impl Entry {
/// Returns the entry's limit. /// Returns the entry's limit.
#[inline(always)] #[inline(always)]
pub fn get_limit(&self) -> u32 { pub const fn get_limit(&self) -> u32 {
((self.0 & 0xffff) | (((self.0 >> 48) & 0xf) << 16)) as _ ((self.0 & 0xffff) | (((self.0 >> 48) & 0xf) << 16)) as _
} }
@ -73,7 +84,7 @@ impl Entry {
/// ///
/// If the given limit is more than `pow(2, 20) - 1`, the value is truncated. /// If the given limit is more than `pow(2, 20) - 1`, the value is truncated.
#[inline(always)] #[inline(always)]
pub fn set_limit(&mut self, limit: u32) { pub const fn set_limit(&mut self, limit: u32) {
self.0 &= !0xffff; self.0 &= !0xffff;
self.0 &= !(0xf << 48); self.0 &= !(0xf << 48);
@ -83,39 +94,39 @@ impl Entry {
/// Returns the value of the access byte. /// Returns the value of the access byte.
#[inline(always)] #[inline(always)]
pub fn get_access_byte(&self) -> u8 { pub const fn get_access_byte(&self) -> u8 {
((self.0 >> 40) & 0xff) as _ ((self.0 >> 40) & 0xff) as _
} }
/// Sets the value of the access byte. /// Sets the value of the access byte.
#[inline(always)] #[inline(always)]
pub fn set_access_byte(&mut self, byte: u8) { pub const fn set_access_byte(&mut self, byte: u8) {
self.0 &= !(0xff << 40); self.0 &= !(0xff << 40);
self.0 |= (byte as u64) << 40; self.0 |= (byte as u64) << 40;
} }
/// Returns the flags. /// Returns the flags.
#[inline(always)] #[inline(always)]
pub fn get_flags(&self) -> u8 { pub const fn get_flags(&self) -> u8 {
((self.0 >> 52) & 0x0f) as _ ((self.0 >> 52) & 0x0f) as _
} }
/// Sets the flags. /// Sets the flags.
#[inline(always)] #[inline(always)]
pub fn set_flags(&mut self, flags: u8) { pub const fn et_flags(&mut self, flags: u8) {
self.0 &= !(0x0f << 52); self.0 &= !(0x0f << 52);
self.0 |= ((flags as u64) & 0x0f) << 52; self.0 |= ((flags as u64) & 0x0f) << 52;
} }
/// Tells whether the entry is present. /// Tells whether the entry is present.
#[inline(always)] #[inline(always)]
pub fn is_present(&self) -> bool { pub const fn is_present(&self) -> bool {
(self.0 >> 47 & 1) != 0 (self.0 >> 47 & 1) != 0
} }
/// Sets the entry present or not. /// Sets the entry present or not.
#[inline(always)] #[inline(always)]
pub fn set_present(&mut self, present: bool) { pub const fn set_present(&mut self, present: bool) {
if present { if present {
self.0 |= 1 << 47; self.0 |= 1 << 47;
} else { } else {

View File

@ -52,6 +52,7 @@
extern crate alloc; extern crate alloc;
pub mod acpi; pub mod acpi;
mod boot;
pub mod cmdline; pub mod cmdline;
pub mod cpu; pub mod cpu;
pub mod crypto; pub mod crypto;
@ -60,7 +61,7 @@ pub mod device;
pub mod elf; pub mod elf;
pub mod event; pub mod event;
pub mod file; pub mod file;
#[cfg(target_arch = "x86")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod gdt; pub mod gdt;
#[macro_use] #[macro_use]
pub mod idt; pub mod idt;

View File

@ -19,7 +19,7 @@
//! The virtual memory makes the kernel able to isolate processes, which is //! The virtual memory makes the kernel able to isolate processes, which is
//! essential for modern systems. //! essential for modern systems.
#[cfg(target_arch = "x86")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod x86; pub mod x86;
use crate::{ use crate::{

View File

@ -4,14 +4,14 @@
KERN_SRC=$(realpath $(dirname $0)/..) KERN_SRC=$(realpath $(dirname $0)/..)
if [ -z "$ARCH" ]; then if [ -z "$ARCH" ]; then
ARCH="x86" ARCH="x86_64"
fi fi
CARGOFLAGS="--target $KERN_SRC/kernel/arch/$ARCH/$ARCH.json $CARGOFLAGS" export CARGOFLAGS="--target $KERN_SRC/kernel/arch/$ARCH/$ARCH.json $CARGOFLAGS"
if [ ! -z "$PROFILE" ] && [ "$PROFILE" != "debug" ]; then if [ ! -z "$PROFILE" ] && [ "$PROFILE" != "debug" ]; then
CARGOFLAGS="$CARGOFLAGS --profile $PROFILE" CARGOFLAGS="$CARGOFLAGS --profile $PROFILE"
else else
PROFILE="debug" export PROFILE="debug"
fi fi
export RUSTFLAGS="--extern kernel=$KERN_SRC/kernel/target/$ARCH/$PROFILE/libkernel.so -L $KERN_SRC/kernel/target/$ARCH/$PROFILE/deps -L $KERN_SRC/kernel/target/$PROFILE/deps $RUSTFLAGS" export RUSTFLAGS="--extern kernel=$KERN_SRC/kernel/target/$ARCH/$PROFILE/libkernel.so -L $KERN_SRC/kernel/target/$ARCH/$PROFILE/deps -L $KERN_SRC/kernel/target/$PROFILE/deps $RUSTFLAGS"

View File

@ -1,5 +1,4 @@
[toolchain] [toolchain]
channel = "nightly-2024-09-18" channel = "nightly-2024-09-18"
components = ["rustfmt", "rustc-dev", "rust-src", "clippy", "miri"] components = ["rustfmt", "rustc-dev", "rust-src", "clippy", "miri"]
targets = ["i686-unknown-linux-musl"]
profile = "minimal" profile = "minimal"