mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:37:35 +00:00
Kernel: Introduce basic pre-kernel environment
This implements a simple bootloader that is capable of loading ELF64 kernel images. It does this by using QEMU/GRUB to load the kernel image from disk and pass it to our bootloader as a Multiboot module. The bootloader then parses the ELF image and sets it up appropriately. The kernel's entry point is a C++ function with architecture-native code. Co-authored-by: Liav A <liavalb@gmail.com>
This commit is contained in:
parent
357ddd393e
commit
7e94b090fe
30 changed files with 1207 additions and 181 deletions
30
Kernel/Prekernel/BootInfo.h
Normal file
30
Kernel/Prekernel/BootInfo.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Platform.h>
|
||||
#include <Kernel/Multiboot.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
struct [[gnu::packed]] BootInfo {
|
||||
u8 const* start_of_prekernel_image;
|
||||
u8 const* end_of_prekernel_image;
|
||||
FlatPtr kernel_base;
|
||||
multiboot_info* multiboot_info_ptr;
|
||||
#if ARCH(X86_64)
|
||||
u32 gdt64ptr;
|
||||
u16 code64_sel;
|
||||
FlatPtr boot_pml4t;
|
||||
#endif
|
||||
FlatPtr boot_pdpt;
|
||||
FlatPtr boot_pd0;
|
||||
FlatPtr boot_pd_kernel;
|
||||
FlatPtr boot_pd_kernel_pt1023;
|
||||
char const* kernel_cmdline;
|
||||
};
|
||||
}
|
28
Kernel/Prekernel/CMakeLists.txt
Normal file
28
Kernel/Prekernel/CMakeLists.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
set(SOURCES
|
||||
boot.S
|
||||
multiboot.S
|
||||
init.cpp
|
||||
UBSanitizer.cpp
|
||||
../MiniStdLib.cpp
|
||||
)
|
||||
|
||||
if ("${SERENITY_ARCH}" STREQUAL "i686")
|
||||
set(PREKERNEL_TARGET Prekernel32)
|
||||
else()
|
||||
set(PREKERNEL_TARGET Prekernel64)
|
||||
endif()
|
||||
|
||||
add_executable(${PREKERNEL_TARGET} ${SOURCES})
|
||||
|
||||
target_link_options(${PREKERNEL_TARGET} PRIVATE LINKER:-T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld -nostdlib)
|
||||
set_target_properties(${PREKERNEL_TARGET} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||
|
||||
target_link_libraries(${PREKERNEL_TARGET} gcc supc++)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${PREKERNEL_TARGET} POST_BUILD
|
||||
COMMAND ${TOOLCHAIN_PREFIX}objcopy -O elf32-i386 ${CMAKE_CURRENT_BINARY_DIR}/${PREKERNEL_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/Prekernel
|
||||
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/Prekernel
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Prekernel" DESTINATION boot)
|
9
Kernel/Prekernel/Prekernel.h
Normal file
9
Kernel/Prekernel/Prekernel.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MAX_KERNEL_SIZE 0x3000000
|
142
Kernel/Prekernel/UBSanitizer.cpp
Normal file
142
Kernel/Prekernel/UBSanitizer.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/UBSanitizer.h>
|
||||
#include <Kernel/Arch/x86/Processor.h>
|
||||
#include <Kernel/KSyms.h>
|
||||
|
||||
using namespace AK::UBSanitizer;
|
||||
|
||||
bool AK::UBSanitizer::g_ubsan_is_deadly { true };
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void print_location(const SourceLocation&)
|
||||
{
|
||||
asm volatile("cli; hlt");
|
||||
}
|
||||
|
||||
void __ubsan_handle_load_invalid_value(const InvalidValueData&, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_load_invalid_value(const InvalidValueData& data, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_nonnull_arg(const NonnullArgData&) __attribute__((used));
|
||||
void __ubsan_handle_nonnull_arg(const NonnullArgData& data)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_nullability_arg(const NonnullArgData&) __attribute__((used));
|
||||
void __ubsan_handle_nullability_arg(const NonnullArgData& data)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_nonnull_return_v1(const NonnullReturnData&, const SourceLocation&) __attribute__((used));
|
||||
void __ubsan_handle_nonnull_return_v1(const NonnullReturnData&, const SourceLocation& location)
|
||||
{
|
||||
print_location(location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_nullability_return_v1(const NonnullReturnData& data, const SourceLocation& location) __attribute__((used));
|
||||
void __ubsan_handle_nullability_return_v1(const NonnullReturnData&, const SourceLocation& location)
|
||||
{
|
||||
print_location(location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_vla_bound_not_positive(const VLABoundData&, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_vla_bound_not_positive(const VLABoundData& data, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_add_overflow(const OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used));
|
||||
void __ubsan_handle_add_overflow(const OverflowData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_sub_overflow(const OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used));
|
||||
void __ubsan_handle_sub_overflow(const OverflowData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_negate_overflow(const OverflowData&, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_negate_overflow(const OverflowData& data, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_mul_overflow(const OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used));
|
||||
void __ubsan_handle_mul_overflow(const OverflowData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_shift_out_of_bounds(const ShiftOutOfBoundsData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used));
|
||||
void __ubsan_handle_shift_out_of_bounds(const ShiftOutOfBoundsData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_divrem_overflow(const OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used));
|
||||
void __ubsan_handle_divrem_overflow(const OverflowData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_out_of_bounds(const OutOfBoundsData&, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_out_of_bounds(const OutOfBoundsData& data, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_type_mismatch_v1(const TypeMismatchData&, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_type_mismatch_v1(const TypeMismatchData& data, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_alignment_assumption(const AlignmentAssumptionData&, ValueHandle, ValueHandle, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_alignment_assumption(const AlignmentAssumptionData& data, ValueHandle, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_builtin_unreachable(const UnreachableData&) __attribute__((used));
|
||||
void __ubsan_handle_builtin_unreachable(const UnreachableData& data)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_missing_return(const UnreachableData&) __attribute__((used));
|
||||
void __ubsan_handle_missing_return(const UnreachableData& data)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_implicit_conversion(const ImplicitConversionData&, ValueHandle, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_implicit_conversion(const ImplicitConversionData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_invalid_builtin(const InvalidBuiltinData) __attribute__((used));
|
||||
void __ubsan_handle_invalid_builtin(const InvalidBuiltinData data)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
|
||||
void __ubsan_handle_pointer_overflow(const PointerOverflowData&, ValueHandle, ValueHandle) __attribute__((used));
|
||||
void __ubsan_handle_pointer_overflow(const PointerOverflowData& data, ValueHandle, ValueHandle)
|
||||
{
|
||||
print_location(data.location);
|
||||
}
|
||||
}
|
509
Kernel/Prekernel/boot.S
Normal file
509
Kernel/Prekernel/boot.S
Normal file
|
@ -0,0 +1,509 @@
|
|||
#include <AK/Platform.h>
|
||||
#include <Kernel/Prekernel/Prekernel.h>
|
||||
|
||||
.code32
|
||||
|
||||
.section .stack, "aw", @nobits
|
||||
stack_bottom:
|
||||
.skip 32768
|
||||
stack_top:
|
||||
|
||||
.global kernel_cmdline
|
||||
kernel_cmdline:
|
||||
.skip 4096
|
||||
|
||||
.section .page_tables, "aw", @nobits
|
||||
.align 4096
|
||||
#if ARCH(X86_64)
|
||||
.global boot_pml4t
|
||||
boot_pml4t:
|
||||
.skip 4096
|
||||
#endif
|
||||
.global boot_pdpt
|
||||
boot_pdpt:
|
||||
.skip 4096
|
||||
.global boot_pd0
|
||||
boot_pd0:
|
||||
.skip 4096
|
||||
.global boot_pd0_pts
|
||||
boot_pd0_pts:
|
||||
.skip 4096 * (MAX_KERNEL_SIZE >> 21)
|
||||
.global boot_pd_kernel
|
||||
boot_pd_kernel:
|
||||
.skip 4096
|
||||
.global boot_pd_kernel_pts
|
||||
boot_pd_kernel_pts:
|
||||
.skip 4096 * (MAX_KERNEL_SIZE >> 21)
|
||||
.global boot_pd_kernel_pt1023
|
||||
boot_pd_kernel_pt1023:
|
||||
.skip 4096
|
||||
|
||||
.section .boot_text, "ax"
|
||||
|
||||
.global start
|
||||
.type start, @function
|
||||
|
||||
.extern init
|
||||
.type init, @function
|
||||
|
||||
.global reload_cr3
|
||||
.type reload_cr3, @function
|
||||
|
||||
.extern multiboot_info_ptr
|
||||
.type multiboot_info_ptr, @object
|
||||
|
||||
/*
|
||||
construct the following (64-bit PML4T) page table layout:
|
||||
(the PML4T part is not used for 32-bit x86)
|
||||
|
||||
pml4t:
|
||||
|
||||
0: pdpt (0-512GB)
|
||||
|
||||
pdpt
|
||||
|
||||
0: boot_pd0 (0-1GB)
|
||||
1: n/a (1-2GB)
|
||||
2: n/a (2-3GB)
|
||||
3: n/a (3-4GB)
|
||||
|
||||
boot_pd0 : 512 PDEs
|
||||
|
||||
boot_pd0_pts (0MB - MAX_KERNEL_SIZE) (id 512 4KB pages)
|
||||
|
||||
the page tables each contain 512 PTEs that map individual 4KB pages
|
||||
|
||||
*/
|
||||
|
||||
#if ARCH(X86_64)
|
||||
gdt64:
|
||||
.quad 0
|
||||
gdt64code:
|
||||
.quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */
|
||||
.global gdt64ptr
|
||||
gdt64ptr:
|
||||
.short . - gdt64 - 1
|
||||
.quad gdt64
|
||||
|
||||
.set code64_sel_value, gdt64code - gdt64
|
||||
|
||||
.global code64_sel
|
||||
code64_sel:
|
||||
.short code64_sel_value
|
||||
#endif
|
||||
|
||||
start:
|
||||
jmp real_start
|
||||
|
||||
/*
|
||||
this function assumes that paging is disabled (or everything is mapped 1:1)
|
||||
param 1: pointer to string ended with null terminator (C string)
|
||||
*/
|
||||
print_and_halt:
|
||||
|
||||
/* from now on, we don't really care about booting because we are missing required CPU features such as PAE or long mode.
|
||||
the flow from now is like so:
|
||||
1. Copy all necessary parts to low memory section in RAM
|
||||
2. Jump to that section
|
||||
3. In that section we do:
|
||||
a. exit protected mode to pure 16 bit real mode
|
||||
b. load the "<missing feature> is not supported" String, call the BIOS print to screen service
|
||||
c. halt
|
||||
*/
|
||||
|
||||
.equ COPIED_STRING_LOCATION, 0x400
|
||||
.equ GDT_REAL_MODE_LOCATION, 0x45000
|
||||
.equ EXITING_PROTECTED_MODE_CODE_LOCATION, 0x10000
|
||||
.equ REAL_MODE_CODE, 0x500
|
||||
.equ PROTECTED_MODE_16_BIT_CODE, 0x600
|
||||
movl %esp, %ebp
|
||||
movl 4(%ebp), %edi
|
||||
|
||||
/* Copy string to low memory section */
|
||||
movl %edi, %esi
|
||||
xor %ecx, %ecx
|
||||
|
||||
pushl %eax
|
||||
pushl %edi
|
||||
check_string_length:
|
||||
movb (%edi), %ah
|
||||
cmp $0, %ah
|
||||
je check_string_length_exit
|
||||
inc %ecx
|
||||
inc %edi
|
||||
jmp check_string_length
|
||||
check_string_length_exit:
|
||||
popl %edi
|
||||
popl %eax
|
||||
|
||||
/* source address of the code is ESI */
|
||||
movw %cx, (COPIED_STRING_LOCATION)
|
||||
mov $COPIED_STRING_LOCATION + 2, %edi /* destination address of the code */
|
||||
rep movsb
|
||||
|
||||
/* Copy gdt_table_real_mode to low memory section */
|
||||
movl $gdt_table_real_mode, %eax
|
||||
movl $gdt_table_real_mode_end, %ebx
|
||||
|
||||
movl %ebx, %ecx
|
||||
sub %eax, %ecx
|
||||
mov %eax, %esi /* source address of the code */
|
||||
mov $GDT_REAL_MODE_LOCATION, %edi /* destination address of the code */
|
||||
rep movsb
|
||||
|
||||
/* Copy protected_mode_16_bit to real_mode to low memory section */
|
||||
movl $protected_mode_16_bit, %eax
|
||||
movl $real_mode, %ebx
|
||||
|
||||
movl %ebx, %ecx
|
||||
sub %eax, %ecx
|
||||
mov %eax, %esi /* source address of the code */
|
||||
mov $PROTECTED_MODE_16_BIT_CODE, %edi /* destination address of the code */
|
||||
rep movsb
|
||||
|
||||
/* Copy real_mode to end_of_print_and_halt_function to low memory section */
|
||||
movl $real_mode, %eax
|
||||
movl $end_of_print_and_halt_function, %ebx
|
||||
|
||||
movl %ebx, %ecx
|
||||
sub %eax, %ecx
|
||||
mov %eax, %esi /* source address of the code */
|
||||
mov $REAL_MODE_CODE, %edi /* destination address of the code */
|
||||
rep movsb
|
||||
|
||||
|
||||
/* Copy all opcodes from exiting_real_mode label to protected_mode_16_bit label to low memory RAM */
|
||||
movl $exiting_real_mode, %eax
|
||||
movl $protected_mode_16_bit, %ebx
|
||||
|
||||
movl %ebx, %ecx
|
||||
sub %eax, %ecx
|
||||
mov %eax, %esi /* source address of the code */
|
||||
mov $EXITING_PROTECTED_MODE_CODE_LOCATION, %edi /* destination address of the code */
|
||||
pushl %edi
|
||||
rep movsb
|
||||
popl %edi
|
||||
pushl %edi
|
||||
ret
|
||||
|
||||
gdt_table_real_mode:
|
||||
.quad 0 /* Empty entry */
|
||||
|
||||
.short 0xffff
|
||||
.short 0
|
||||
.byte 0
|
||||
.byte 0b10011010
|
||||
.byte 0b00001111
|
||||
.byte 0x0
|
||||
|
||||
.short 0xffff
|
||||
.short 0
|
||||
.byte 0
|
||||
.byte 0b10010010
|
||||
.byte 0b00001111
|
||||
.byte 0x0
|
||||
gdt_table_real_mode_end:
|
||||
|
||||
no_long_mode_string:
|
||||
.asciz "Your computer does not support long mode (64-bit mode). Halting!"
|
||||
|
||||
no_pae_string:
|
||||
.asciz "Your computer does not support PAE. Halting!"
|
||||
|
||||
kernel_image_too_big_string:
|
||||
.asciz "Error: Kernel Image too big for memory slot. Halting!"
|
||||
|
||||
/*
|
||||
This part is completely standalone - it doesn't involve any location from this
|
||||
near code. It uses arbitrary locations in the low memory section of the RAM.
|
||||
We don't really worry about where are these locations, because we only want to quickly
|
||||
print a string and halt.
|
||||
*/
|
||||
.code32
|
||||
exiting_real_mode:
|
||||
|
||||
/* Build IDT pointer and load it */
|
||||
mov $0x50000, %eax
|
||||
pushl %eax
|
||||
movl $0x3ff, 0(%eax)
|
||||
add $2, %eax
|
||||
movl $0, 0(%eax)
|
||||
popl %eax
|
||||
lidt (%eax)
|
||||
|
||||
/* Build GDT pointer and load it */
|
||||
mov $0x40000, %eax
|
||||
pushl %eax
|
||||
movl $32, 0(%eax)
|
||||
add $2, %eax
|
||||
movl $GDT_REAL_MODE_LOCATION, 0(%eax)
|
||||
popl %eax
|
||||
lgdt (%eax)
|
||||
|
||||
/* far jump to protected_mode_16_bit in 0x5000 */
|
||||
pushw $8
|
||||
push $PROTECTED_MODE_16_BIT_CODE
|
||||
lret
|
||||
hlt
|
||||
|
||||
.code16
|
||||
protected_mode_16_bit:
|
||||
xor %eax, %eax
|
||||
movl $0x10, %eax
|
||||
movw %ax, %ds
|
||||
and $0xFE, %al /* switch to pure real mode */
|
||||
mov %eax, %cr0
|
||||
mov $0x10, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
pushw $0
|
||||
push $REAL_MODE_CODE
|
||||
lret
|
||||
hlt
|
||||
|
||||
real_mode:
|
||||
movw $0x7000, %ax
|
||||
movl $0x0000, %esp
|
||||
movw %ax, %ss
|
||||
|
||||
xor %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
mov $0x3, %ax
|
||||
int $0x10
|
||||
|
||||
movb $0x13, %ah
|
||||
movb $0x0, %bh
|
||||
movb $0xf, %bl
|
||||
movw (COPIED_STRING_LOCATION), %cx
|
||||
movw $0, %dx
|
||||
movw $COPIED_STRING_LOCATION + 2, %bp
|
||||
int $0x10
|
||||
|
||||
movl $0xdeadcafe, %ebx
|
||||
cli
|
||||
hlt
|
||||
end_of_print_and_halt_function:
|
||||
|
||||
.code32
|
||||
real_start:
|
||||
cli
|
||||
cld
|
||||
mov $end_of_prekernel_image, %esi
|
||||
cmp $MAX_KERNEL_SIZE, %esi
|
||||
jbe kernel_not_too_large
|
||||
|
||||
movl $kernel_image_too_big_string, %esi
|
||||
pushl %esi
|
||||
call print_and_halt
|
||||
/* We should not return, but just in case, halt */
|
||||
hlt
|
||||
|
||||
kernel_not_too_large:
|
||||
/* test for PAE presence, save the most important registers from corruption */
|
||||
pushl %eax
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
|
||||
movl $0x1, %eax /* PAE presence is in CPUID input 0x1 */
|
||||
cpuid
|
||||
testl $(1 << 6), %edx /* Test if the PAE-bit, which is bit 6, is set in the edx register. */
|
||||
jnz pae_supported /* If the bit is not set, there is no PAE capability. */
|
||||
|
||||
/* Since there is no PAE capability, halt with an error message */
|
||||
movl $no_pae_string, %esi
|
||||
pushl %esi
|
||||
call print_and_halt
|
||||
/* We should not return, but just in case, halt */
|
||||
hlt
|
||||
|
||||
|
||||
#if ARCH(X86_64)
|
||||
pae_supported:
|
||||
movl $0x80000001, %eax
|
||||
cpuid
|
||||
testl $(1 << 29), %edx /* Test if the LM-bit, which is bit 29, is set in the edx register. */
|
||||
jnz long_mode_supported /* If LM-bit is not enabled, there is no long mode. */
|
||||
|
||||
/* Since there is no long mode, halt with an error message */
|
||||
movl $no_long_mode_string, %esi
|
||||
pushl %esi
|
||||
call print_and_halt
|
||||
/* We should not return, but just in case, halt */
|
||||
hlt
|
||||
|
||||
|
||||
/* If both PAE and long mode is supported, continue with booting the system */
|
||||
|
||||
long_mode_supported:
|
||||
/* restore the pushed registers and continue with booting */
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %eax
|
||||
#else
|
||||
/* If PAE is supported, continue with booting the system */
|
||||
|
||||
pae_supported:
|
||||
/* restore the pushed registers and continue with booting */
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %eax
|
||||
#endif
|
||||
|
||||
/* We don't know where the bootloader might have put the command line.
|
||||
* It might be at an inconvenient location that we're not about to map,
|
||||
* so let's just copy it to a convenient location while we have the whole
|
||||
* memory space identity-mapped anyway. :^)
|
||||
*/
|
||||
|
||||
movl %ebx, %esi
|
||||
addl $16, %esi
|
||||
movl (%esi), %esi
|
||||
movl $1024, %ecx
|
||||
movl $kernel_cmdline, %edi
|
||||
rep movsl
|
||||
|
||||
#if ARCH(X86_64)
|
||||
/* clear pml4t */
|
||||
movl $boot_pml4t, %edi
|
||||
movl $1024, %ecx
|
||||
xorl %eax, %eax
|
||||
rep stosl
|
||||
|
||||
/* set up pml4t[0] */
|
||||
movl $boot_pml4t, %edi
|
||||
movl $boot_pdpt, 0(%edi)
|
||||
/* R/W + Present */
|
||||
orl $0x3, 0(%edi)
|
||||
#endif
|
||||
|
||||
/* clear pdpt */
|
||||
movl $boot_pdpt, %edi
|
||||
movl $1024, %ecx
|
||||
xorl %eax, %eax
|
||||
rep stosl
|
||||
|
||||
/* set up pdpt[0] and pdpt[3] */
|
||||
movl $boot_pdpt, %edi
|
||||
#if ARCH(X86_64)
|
||||
movl $(boot_pd0 + 3), 0(%edi)
|
||||
#else
|
||||
movl $(boot_pd0 + 1), 0(%edi)
|
||||
#endif
|
||||
|
||||
/* clear pd0 */
|
||||
movl $boot_pd0, %edi
|
||||
movl $1024, %ecx
|
||||
xorl %eax, %eax
|
||||
rep stosl
|
||||
|
||||
/* clear pd0's PTs */
|
||||
movl $boot_pd0_pts, %edi
|
||||
movl $(1024 * (MAX_KERNEL_SIZE >> 21)), %ecx
|
||||
xorl %eax, %eax
|
||||
rep stosl
|
||||
|
||||
/* add boot_pd0_pts to boot_pd0 */
|
||||
movl $(MAX_KERNEL_SIZE >> 21), %ecx
|
||||
movl $boot_pd0, %edi
|
||||
movl $boot_pd0_pts, %eax
|
||||
|
||||
1:
|
||||
movl %eax, 0(%edi)
|
||||
/* R/W + Present */
|
||||
orl $0x3, 0(%edi)
|
||||
addl $8, %edi
|
||||
addl $4096, %eax
|
||||
loop 1b
|
||||
|
||||
/* identity map the 0MB to MAX_KERNEL_SIZE range */
|
||||
movl $(512 * (MAX_KERNEL_SIZE >> 21)), %ecx
|
||||
movl $boot_pd0_pts, %edi
|
||||
xorl %eax, %eax
|
||||
|
||||
1:
|
||||
movl %eax, 0(%edi)
|
||||
/* R/W + Present */
|
||||
orl $0x3, 0(%edi)
|
||||
addl $8, %edi
|
||||
addl $4096, %eax
|
||||
loop 1b
|
||||
|
||||
#if ARCH(X86_64)
|
||||
/* point CR3 to PML4T */
|
||||
movl $boot_pml4t, %eax
|
||||
#else
|
||||
/* point CR3 to PDPT */
|
||||
movl $boot_pdpt, %eax
|
||||
#endif
|
||||
|
||||
movl %eax, %cr3
|
||||
|
||||
/* enable PAE + PSE */
|
||||
movl %cr4, %eax
|
||||
orl $0x60, %eax
|
||||
movl %eax, %cr4
|
||||
|
||||
#if ARCH(X86_64)
|
||||
1:
|
||||
/* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
|
||||
mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
|
||||
rdmsr /* Read from the model-specific register.*/
|
||||
or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
|
||||
wrmsr /* Write to the model-specific register.*/
|
||||
#endif
|
||||
|
||||
/* enable PG */
|
||||
movl %cr0, %eax
|
||||
orl $0x80000000, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* set up stack */
|
||||
mov $stack_top, %esp
|
||||
and $-16, %esp
|
||||
|
||||
#if ARCH(X86_64)
|
||||
/* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
|
||||
mov $gdt64ptr, %eax
|
||||
lgdt (%eax)
|
||||
ljmpl $code64_sel_value, $1f
|
||||
|
||||
.code64
|
||||
1:
|
||||
movl %ebx, %ebx
|
||||
movq %rbx, multiboot_info_ptr
|
||||
|
||||
mov $0, %ax
|
||||
mov %ax, %ss
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
#else
|
||||
movl %ebx, multiboot_info_ptr
|
||||
#endif
|
||||
|
||||
call reload_cr3
|
||||
call init
|
||||
|
||||
cli
|
||||
loop:
|
||||
hlt
|
||||
jmp loop
|
||||
|
||||
reload_cr3:
|
||||
#if ARCH(X86_64)
|
||||
pushq %rax
|
||||
mov %cr3, %rax
|
||||
mov %rax, %cr3
|
||||
popq %rax
|
||||
#else
|
||||
pushl %eax
|
||||
movl %cr3, %eax
|
||||
movl %eax, %cr3
|
||||
popl %eax
|
||||
#endif
|
||||
ret
|
179
Kernel/Prekernel/init.cpp
Normal file
179
Kernel/Prekernel/init.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Multiboot.h>
|
||||
#include <Kernel/Prekernel/BootInfo.h>
|
||||
#include <Kernel/Prekernel/Prekernel.h>
|
||||
#include <Kernel/VirtualAddress.h>
|
||||
#include <LibC/elf.h>
|
||||
|
||||
// Defined in the linker script
|
||||
extern size_t __stack_chk_guard;
|
||||
size_t __stack_chk_guard;
|
||||
extern "C" [[noreturn]] void __stack_chk_fail();
|
||||
|
||||
extern "C" u8 start_of_prekernel_image[];
|
||||
extern "C" u8 end_of_prekernel_image[];
|
||||
|
||||
extern "C" u8 gdt64ptr[];
|
||||
extern "C" u16 code64_sel;
|
||||
extern "C" u64 boot_pml4t[512];
|
||||
extern "C" u64 boot_pdpt[512];
|
||||
extern "C" u64 boot_pd0[512];
|
||||
extern "C" u64 boot_pd0_pts[512 * (MAX_KERNEL_SIZE >> 21 & 0x1ff)];
|
||||
extern "C" u64 boot_pd_kernel[512];
|
||||
extern "C" u64 boot_pd_kernel_pts[512 * (MAX_KERNEL_SIZE >> 21 & 0x1ff)];
|
||||
extern "C" u64 boot_pd_kernel_pt1023[512];
|
||||
extern "C" char const kernel_cmdline[4096];
|
||||
|
||||
extern "C" void reload_cr3();
|
||||
|
||||
extern "C" {
|
||||
multiboot_info_t* multiboot_info_ptr;
|
||||
}
|
||||
|
||||
void __stack_chk_fail()
|
||||
{
|
||||
asm("ud2");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// boot.S expects these functions to exactly have the following signatures.
|
||||
// We declare them here to ensure their signatures don't accidentally change.
|
||||
extern "C" [[noreturn]] void init();
|
||||
|
||||
static void halt()
|
||||
{
|
||||
asm volatile("hlt");
|
||||
}
|
||||
|
||||
// SerenityOS Pre-Kernel Environment C++ entry point :^)
|
||||
//
|
||||
// This is where C++ execution begins, after boot.S transfers control here.
|
||||
//
|
||||
|
||||
extern "C" [[noreturn]] void init()
|
||||
{
|
||||
if (multiboot_info_ptr->mods_count < 1)
|
||||
halt();
|
||||
|
||||
multiboot_module_entry_t* kernel_module = (multiboot_module_entry_t*)(FlatPtr)multiboot_info_ptr->mods_addr;
|
||||
|
||||
u8* kernel_image = (u8*)(FlatPtr)kernel_module->start;
|
||||
ElfW(Ehdr)* kernel_elf_header = (ElfW(Ehdr)*)kernel_image;
|
||||
ElfW(Phdr)* kernel_program_headers = (ElfW(Phdr*))((char*)kernel_elf_header + kernel_elf_header->e_phoff);
|
||||
|
||||
FlatPtr kernel_load_base = kernel_program_headers[0].p_vaddr;
|
||||
FlatPtr kernel_load_end = kernel_program_headers[kernel_elf_header->e_phnum - 1].p_vaddr;
|
||||
|
||||
// align to 1GB
|
||||
kernel_load_base &= ~(FlatPtr)0x3fffffff;
|
||||
|
||||
if (kernel_program_headers[0].p_vaddr < (FlatPtr)end_of_prekernel_image)
|
||||
halt();
|
||||
|
||||
if (kernel_program_headers[0].p_paddr < (FlatPtr)end_of_prekernel_image)
|
||||
halt();
|
||||
|
||||
#if ARCH(I386)
|
||||
int pdpt_flags = 0x1;
|
||||
#else
|
||||
int pdpt_flags = 0x3;
|
||||
#endif
|
||||
boot_pdpt[(kernel_load_base >> 30) & 0x1ffu] = (FlatPtr)boot_pd_kernel | pdpt_flags;
|
||||
|
||||
for (size_t i = 0; i <= (kernel_load_end - kernel_load_base) >> 21; i++)
|
||||
boot_pd_kernel[i] = (FlatPtr)&boot_pd_kernel_pts[i * 512] | 0x3;
|
||||
|
||||
__builtin_memset(boot_pd_kernel_pts, 0, sizeof(boot_pd_kernel_pts));
|
||||
|
||||
/* pseudo-identity map 0M - end_of_prekernel_image */
|
||||
for (size_t i = 0; i < (FlatPtr)end_of_prekernel_image / PAGE_SIZE; i++)
|
||||
boot_pd_kernel_pts[i] = i * PAGE_SIZE | 0x3;
|
||||
|
||||
for (size_t i = 0; i < kernel_elf_header->e_phnum; i++) {
|
||||
auto& kernel_program_header = kernel_program_headers[i];
|
||||
if (kernel_program_header.p_type != PT_LOAD)
|
||||
continue;
|
||||
for (FlatPtr offset = 0; offset < kernel_program_header.p_memsz; offset += PAGE_SIZE) {
|
||||
auto pte_index = (kernel_program_header.p_vaddr + offset - kernel_load_base) >> 12;
|
||||
boot_pd_kernel_pts[pte_index] = (kernel_program_header.p_paddr + offset) | 0x3;
|
||||
}
|
||||
}
|
||||
|
||||
boot_pd_kernel[511] = (FlatPtr)boot_pd_kernel_pt1023 | 0x3;
|
||||
|
||||
reload_cr3();
|
||||
|
||||
for (ssize_t i = kernel_elf_header->e_phnum - 1; i >= 0; i--) {
|
||||
auto& kernel_program_header = kernel_program_headers[i];
|
||||
if (kernel_program_header.p_type != PT_LOAD)
|
||||
continue;
|
||||
__builtin_memmove((u8*)kernel_program_header.p_vaddr, kernel_image + kernel_program_header.p_offset, kernel_program_header.p_filesz);
|
||||
}
|
||||
|
||||
for (ssize_t i = kernel_elf_header->e_phnum - 1; i >= 0; i--) {
|
||||
auto& kernel_program_header = kernel_program_headers[i];
|
||||
if (kernel_program_header.p_type != PT_LOAD)
|
||||
continue;
|
||||
__builtin_memset((u8*)kernel_program_header.p_vaddr + kernel_program_header.p_filesz, 0, kernel_program_header.p_memsz - kernel_program_header.p_filesz);
|
||||
}
|
||||
|
||||
multiboot_info_ptr->mods_count--;
|
||||
multiboot_info_ptr->mods_addr += sizeof(multiboot_module_entry_t);
|
||||
|
||||
auto adjust_by_load_base = [kernel_load_base](auto* ptr) {
|
||||
return (decltype(ptr))((FlatPtr)ptr + kernel_load_base);
|
||||
};
|
||||
|
||||
BootInfo info;
|
||||
info.start_of_prekernel_image = adjust_by_load_base(start_of_prekernel_image);
|
||||
info.end_of_prekernel_image = adjust_by_load_base(end_of_prekernel_image);
|
||||
info.kernel_base = kernel_load_base;
|
||||
info.multiboot_info_ptr = adjust_by_load_base(multiboot_info_ptr);
|
||||
#if ARCH(X86_64)
|
||||
info.gdt64ptr = (FlatPtr)gdt64ptr;
|
||||
info.code64_sel = code64_sel;
|
||||
info.boot_pml4t = (FlatPtr)adjust_by_load_base(boot_pml4t);
|
||||
#endif
|
||||
info.boot_pdpt = (FlatPtr)adjust_by_load_base(boot_pdpt);
|
||||
info.boot_pd0 = (FlatPtr)adjust_by_load_base(boot_pd0);
|
||||
info.boot_pd_kernel = (FlatPtr)adjust_by_load_base(boot_pd_kernel);
|
||||
info.boot_pd_kernel_pt1023 = (FlatPtr)adjust_by_load_base(boot_pd_kernel_pt1023);
|
||||
info.kernel_cmdline = adjust_by_load_base(kernel_cmdline);
|
||||
|
||||
asm(
|
||||
#if ARCH(I386)
|
||||
"add %0, %%esp"
|
||||
#else
|
||||
"add %0, %%rsp"
|
||||
#endif
|
||||
::"g"(kernel_load_base));
|
||||
|
||||
// unmap the 0-1MB region
|
||||
for (size_t i = 0; i < 256; i++)
|
||||
boot_pd0_pts[i] = 0;
|
||||
|
||||
// unmap the end_of_prekernel_image - MAX_KERNEL_SIZE region
|
||||
for (FlatPtr vaddr = (FlatPtr)end_of_prekernel_image; vaddr < MAX_KERNEL_SIZE; vaddr += PAGE_SIZE)
|
||||
boot_pd0_pts[vaddr >> 12 & 0x1ff] = 0;
|
||||
|
||||
void (*entry)(BootInfo const&) = (void (*)(BootInfo const&))kernel_elf_header->e_entry;
|
||||
entry(*adjust_by_load_base(&info));
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// Define some Itanium C++ ABI methods to stop the linker from complaining.
|
||||
// If we actually call these something has gone horribly wrong
|
||||
void* __dso_handle __attribute__((visibility("hidden")));
|
||||
|
||||
}
|
54
Kernel/Prekernel/linker.ld
Normal file
54
Kernel/Prekernel/linker.ld
Normal file
|
@ -0,0 +1,54 @@
|
|||
ENTRY(start)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
boot_text PT_LOAD ;
|
||||
boot_bss PT_LOAD ;
|
||||
text PT_LOAD ;
|
||||
data PT_LOAD ;
|
||||
bss PT_LOAD ;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x00100000;
|
||||
|
||||
start_of_prekernel_image = .;
|
||||
|
||||
.boot_text ALIGN(4K) : AT (ADDR(.boot_text))
|
||||
{
|
||||
KEEP(*(.boot_text))
|
||||
KEEP(*(.multiboot))
|
||||
} :boot_text
|
||||
|
||||
.boot_bss ALIGN(4K) (NOLOAD) : AT (ADDR(.boot_bss))
|
||||
{
|
||||
KEEP(*(.page_tables))
|
||||
KEEP(*(.stack))
|
||||
*(.super_pages)
|
||||
} :boot_bss
|
||||
|
||||
.text ALIGN(4K) : AT (ADDR(.text))
|
||||
{
|
||||
start_of_kernel_text = .;
|
||||
*(.text*)
|
||||
} :text
|
||||
|
||||
.rodata ALIGN(4K) : AT (ADDR(.rodata))
|
||||
{
|
||||
*(.rodata*)
|
||||
} :data
|
||||
|
||||
.data ALIGN(4K) : AT (ADDR(.data))
|
||||
{
|
||||
*(.data*)
|
||||
} :data
|
||||
|
||||
.bss ALIGN(4K) (NOLOAD) : AT (ADDR(.bss))
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
} :bss
|
||||
|
||||
end_of_prekernel_image = .;
|
||||
}
|
28
Kernel/Prekernel/multiboot.S
Normal file
28
Kernel/Prekernel/multiboot.S
Normal file
|
@ -0,0 +1,28 @@
|
|||
.code32
|
||||
.set MULTIBOOT_MAGIC, 0x1badb002
|
||||
.set MULTIBOOT_PAGE_ALIGN, 0x1
|
||||
.set MULTIBOOT_MEMORY_INFO, 0x2
|
||||
.set MULTIBOOT_VIDEO_MODE, 0x4
|
||||
.set multiboot_flags, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
|
||||
.set multiboot_checksum, -(MULTIBOOT_MAGIC + multiboot_flags)
|
||||
|
||||
.section .multiboot, "a"
|
||||
.align 4
|
||||
|
||||
.long MULTIBOOT_MAGIC
|
||||
.long multiboot_flags
|
||||
.long multiboot_checksum
|
||||
|
||||
|
||||
/* for MULTIBOOT_MEMORY_INFO */
|
||||
.long 0x00000000 /* header_addr */
|
||||
.long 0x00000000 /* load_addr */
|
||||
.long 0x00000000 /* load_end_addr */
|
||||
.long 0x00000000 /* bss_end_addr */
|
||||
.long 0x00000000 /* entry_addr */
|
||||
|
||||
/* for MULTIBOOT_VIDEO_MODE */
|
||||
.long 0x00000000 /* mode_type */
|
||||
.long 1280 /* width */
|
||||
.long 1024 /* height */
|
||||
.long 32 /* depth */
|
Loading…
Add table
Add a link
Reference in a new issue