1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:28:12 +00:00

Kernel: Make the kernel compile & link for x86_64

It's now possible to build the whole kernel with an x86_64 toolchain.
There's no bootstrap code so it doesn't work yet (obviously.)
This commit is contained in:
Andreas Kling 2021-03-04 17:50:05 +01:00
parent aae91dda66
commit adb2e6be5f
15 changed files with 316 additions and 48 deletions

View file

@ -76,6 +76,7 @@ extern "C" void handle_interrupt(TrapFrame*);
// clang-format off
#if ARCH(I386)
#define EH_ENTRY(ec, title) \
extern "C" void title##_asm_entry(); \
extern "C" void title##_handler(TrapFrame*); \
@ -127,6 +128,26 @@ extern "C" void handle_interrupt(TrapFrame*);
" call " #title "_handler\n" \
" jmp common_trap_exit \n");
#elif ARCH(X86_64)
#define EH_ENTRY(ec, title) \
extern "C" void title##_asm_entry(); \
extern "C" void title##_handler(TrapFrame*); \
asm( \
".globl " #title "_asm_entry\n" \
"" #title "_asm_entry: \n" \
" cli;hlt;\n" \
);
#define EH_ENTRY_NO_CODE(ec, title) \
extern "C" void title##_handler(TrapFrame*); \
extern "C" void title##_asm_entry(); \
asm( \
".globl " #title "_asm_entry\n" \
"" #title "_asm_entry: \n" \
" cli;hlt;\n" \
);
#endif
// clang-format on
static void dump(const RegisterState& regs)
@ -1593,6 +1614,7 @@ extern "C" u32 do_init_context(Thread* thread, u32 flags)
extern "C" void do_assume_context(Thread* thread, u32 flags);
#if ARCH(I386)
// clang-format off
asm(
".global do_assume_context \n"
@ -1614,8 +1636,9 @@ asm(
" jmp enter_thread_context \n"
);
// clang-format on
#endif
void Processor::assume_context(Thread& thread, u32 flags)
void Processor::assume_context(Thread& thread, FlatPtr flags)
{
dbgln_if(CONTEXT_SWITCH_DEBUG, "Assume context for thread {} {}", VirtualAddress(&thread), thread);
@ -1624,7 +1647,12 @@ void Processor::assume_context(Thread& thread, u32 flags)
// in_critical() should be 2 here. The critical section in Process::exec
// and then the scheduler lock
VERIFY(Processor::current().in_critical() == 2);
#if ARCH(I386)
do_assume_context(&thread, flags);
#elif ARCH(X86_64)
(void)flags;
TODO();
#endif
VERIFY_NOT_REACHED();
}
@ -2315,12 +2343,14 @@ UNMAP_AFTER_INIT void Processor::gdt_init()
: "memory");
set_fs(GDT_SELECTOR_PROC);
#if ARCH(I386)
// Make sure CS points to the kernel code descriptor.
// clang-format off
asm volatile(
"ljmpl $" __STRINGIFY(GDT_SELECTOR_CODE0) ", $sanity\n"
"sanity:\n");
// clang-format on
#endif
}
void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, const RegisterState& kernel_regs)

View file

@ -1014,7 +1014,7 @@ public:
[[noreturn]] void initialize_context_switching(Thread& initial_thread);
void switch_context(Thread*& from_thread, Thread*& to_thread);
[[noreturn]] static void assume_context(Thread& thread, u32 flags);
[[noreturn]] static void assume_context(Thread& thread, FlatPtr flags);
u32 init_context(Thread& thread, bool leave_crit);
static Vector<FlatPtr> capture_stack_trace(Thread& thread, size_t max_frames = 0);

View file

@ -32,13 +32,15 @@
extern "C" void interrupt_common_asm_entry();
#define GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(isr_number) \
extern "C" void interrupt_##isr_number##_asm_entry(); \
asm(".globl interrupt_" #isr_number "_asm_entry\n" \
"interrupt_" #isr_number "_asm_entry:\n" \
" pushw $" #isr_number "\n" \
" pushw $0\n" \
" jmp interrupt_common_asm_entry\n");
#if ARCH(I386)
# define GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(isr_number) \
extern "C" void interrupt_##isr_number##_asm_entry(); \
asm(".globl interrupt_" #isr_number "_asm_entry\n" \
"interrupt_" #isr_number "_asm_entry:\n" \
" pushw $" #isr_number "\n" \
" pushw $0\n" \
" jmp interrupt_common_asm_entry\n");
// clang-format off
asm(
@ -83,3 +85,19 @@ asm(
" iret\n"
);
// clang-format on
#elif ARCH(X86_64)
# define GENERATE_GENERIC_INTERRUPT_HANDLER_ASM_ENTRY(isr_number) \
extern "C" void interrupt_##isr_number##_asm_entry(); \
asm(".globl interrupt_" #isr_number "_asm_entry\n" \
"interrupt_" #isr_number "_asm_entry:\n" \
" cli\n" \
" hlt\n");
asm(
".globl common_trap_exit\n"
"common_trap_exit:\n"
" cli;hlt\n");
#endif

View file

@ -0,0 +1,183 @@
.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 | MULTIBOOT_VIDEO_MODE
.set multiboot_checksum, -(MULTIBOOT_MAGIC + multiboot_flags)
.section .multiboot
.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 */
.section .stack, "aw", @nobits
stack_bottom:
.skip 32768
stack_top:
.global kernel_cmdline
kernel_cmdline:
.skip 4096
.section .page_tables, "aw", @nobits
.align 4096
.global boot_pdpt
boot_pdpt:
.skip 4096
.global boot_pd0
boot_pd0:
.skip 4096
.global boot_pd3
boot_pd3:
.skip 4096
.global boot_pd0_pt0
boot_pd0_pt0:
.skip 4096 * 4
.global boot_pd3_pts
boot_pd3_pts:
.skip 4096 * 8
.global boot_pd3_pt1023
boot_pd3_pt1023:
.skip 4096
.section .text
.global start
.type start, @function
.extern init
.type init, @function
.extern multiboot_info_ptr
.type multiboot_info_ptr, @object
start:
cli
cld
/* 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 - 0xc0000000), %edi
rep movsl
call init
add $4, %esp
cli
loop:
hlt
jmp loop
.extern init_ap
.type init_ap, @function
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
cli
hlt
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
ap_cpu_gdtr_initial2:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0xc0008000
.global ap_cpu_gdtr
ap_cpu_gdtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.4byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .

View file

@ -1,5 +1,11 @@
add_compile_options(-Os)
if ("${SERENITY_ARCH}" STREQUAL "i686")
set(KERNEL_ARCH i386)
elseif("${SERENITY_ARCH}" STREQUAL "x86_64")
set(KERNEL_ARCH x86_64)
endif()
set(KERNEL_HEAP_SOURCES
Heap/SlabAllocator.cpp
Heap/kmalloc.cpp
@ -285,6 +291,10 @@ if (NOT ${CMAKE_HOST_SYSTEM_NAME} MATCHES SerenityOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib -nostdinc -nostdinc++")
endif()
if ("${SERENITY_ARCH}" STREQUAL "x86_64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcmodel=large -mno-red-zone")
endif()
# Kernel Undefined Behavior Sanitizer (KUBSAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
@ -307,9 +317,9 @@ add_link_options(LINKER:-T ${CMAKE_CURRENT_BINARY_DIR}/linker.ld -nostdlib)
# kernel won't re-link when boot.S changes without this.
set_source_files_properties(init.cpp
PROPERTIES
OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Arch/i386/Boot/boot.S
OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Arch/${KERNEL_ARCH}/Boot/boot.S
)
add_library(boot OBJECT Arch/i386/Boot/boot.S)
add_library(boot OBJECT Arch/${KERNEL_ARCH}/Boot/boot.S)
add_library(kernel_heap STATIC ${KERNEL_HEAP_SOURCES})
file(GENERATE OUTPUT linker.ld INPUT linker.ld)

View file

@ -25,15 +25,20 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Kernel/Debug.h>
#include <Kernel/Devices/USB/UHCIController.h>
#include <Kernel/Process.h>
#include <Kernel/StdLib.h>
#include <Kernel/Time/TimeManagement.h>
#include <Kernel/VM/AnonymousVMObject.h>
#include <Kernel/VM/MemoryManager.h>
#include <AK/Platform.h>
#define UHCI_ENABLED 1
// FIXME: This should not be i386-specific.
#if ARCH(I386)
# include <Kernel/Debug.h>
# include <Kernel/Devices/USB/UHCIController.h>
# include <Kernel/Process.h>
# include <Kernel/StdLib.h>
# include <Kernel/Time/TimeManagement.h>
# include <Kernel/VM/AnonymousVMObject.h>
# include <Kernel/VM/MemoryManager.h>
# define UHCI_ENABLED 1
static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated
static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64;
@ -88,9 +93,9 @@ UHCIController& UHCIController::the()
UNMAP_AFTER_INIT void UHCIController::detect()
{
#if !UHCI_ENABLED
# if !UHCI_ENABLED
return;
#endif
# endif
PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
if (address.is_null())
return;
@ -195,9 +200,9 @@ UNMAP_AFTER_INIT void UHCIController::create_structures()
transfer_descriptor->set_isochronous();
transfer_descriptor->link_queue_head(m_interrupt_transfer_queue->paddr());
#if UHCI_VERBOSE_DEBUG
# if UHCI_VERBOSE_DEBUG
transfer_descriptor->print();
#endif
# endif
}
m_free_td_pool.resize(MAXIMUM_NUMBER_OF_TDS);
@ -211,17 +216,17 @@ UNMAP_AFTER_INIT void UHCIController::create_structures()
// access the raw descriptor (that we later send to the controller)
m_free_td_pool.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr);
#if UHCI_VERBOSE_DEBUG
# if UHCI_VERBOSE_DEBUG
auto transfer_descriptor = m_free_td_pool.at(i);
transfer_descriptor->print();
#endif
# endif
}
#if UHCI_DEBUG
# if UHCI_DEBUG
klog() << "UHCI: Pool information:";
klog() << "\tqh_pool: " << PhysicalAddress(m_qh_pool->physical_page(0)->paddr()) << ", length: " << m_qh_pool->range().size();
klog() << "\ttd_pool: " << PhysicalAddress(m_td_pool->physical_page(0)->paddr()) << ", length: " << m_td_pool->range().size();
#endif
# endif
}
UNMAP_AFTER_INIT void UHCIController::setup_schedule()
@ -289,9 +294,9 @@ QueueHead* UHCIController::allocate_queue_head() const
for (QueueHead* queue_head : m_free_qh_pool) {
if (!queue_head->in_use()) {
queue_head->set_in_use(true);
#if UHCI_DEBUG
# if UHCI_DEBUG
klog() << "UHCI: Allocated a new Queue Head! Located @ " << VirtualAddress(queue_head) << "(" << PhysicalAddress(queue_head->paddr()) << ")";
#endif
# endif
return queue_head;
}
}
@ -305,9 +310,9 @@ TransferDescriptor* UHCIController::allocate_transfer_descriptor() const
for (TransferDescriptor* transfer_descriptor : m_free_td_pool) {
if (!transfer_descriptor->in_use()) {
transfer_descriptor->set_in_use(true);
#if UHCI_DEBUG
# if UHCI_DEBUG
klog() << "UHCI: Allocated a new Transfer Descriptor! Located @ " << VirtualAddress(transfer_descriptor) << "(" << PhysicalAddress(transfer_descriptor->paddr()) << ")";
#endif
# endif
return transfer_descriptor;
}
}
@ -464,3 +469,5 @@ void UHCIController::handle_irq(const RegisterState&)
}
}
#endif

View file

@ -27,13 +27,18 @@
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <Kernel/Devices/USB/UHCIDescriptorTypes.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Device.h>
#include <Kernel/Process.h>
#include <Kernel/Time/TimeManagement.h>
#include <Kernel/VM/ContiguousVMObject.h>
#include <AK/Platform.h>
// FIXME: This should not be i386-specific.
#if ARCH(I386)
# include <AK/NonnullOwnPtr.h>
# include <Kernel/Devices/USB/UHCIDescriptorTypes.h>
# include <Kernel/IO.h>
# include <Kernel/PCI/Device.h>
# include <Kernel/Process.h>
# include <Kernel/Time/TimeManagement.h>
# include <Kernel/VM/ContiguousVMObject.h>
namespace Kernel::USB {
@ -100,3 +105,4 @@ private:
};
}
#endif

View file

@ -281,8 +281,10 @@ void signal_trampoline_dummy()
"int 0x82\n" // sigreturn syscall
"asm_signal_trampoline_end:\n"
".att_syntax" ::"i"(Syscall::SC_sigreturn));
#else
// FIXME: Implement trampoline for other architectures.
#elif ARCH(X86_64)
asm("asm_signal_trampoline:\n"
"cli;hlt\n"
"asm_signal_trampoline_end:\n");
#endif
}

View file

@ -37,6 +37,7 @@ extern "C" void syscall_handler(TrapFrame*);
extern "C" void syscall_asm_entry();
// clang-format off
#if ARCH(I386)
asm(
".globl syscall_asm_entry\n"
"syscall_asm_entry:\n"
@ -64,6 +65,13 @@ asm(
" call syscall_handler \n"
" movl %ebx, 0(%esp) \n" // push pointer to TrapFrame
" jmp common_trap_exit \n");
#elif ARCH(X86_64)
asm(
".globl syscall_asm_entry\n"
"syscall_asm_entry:\n"
" cli\n"
" hlt\n");
#endif
// clang-format on
namespace Syscall {

View file

@ -795,12 +795,12 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
auto setup_stack = [&](RegisterState& state) {
#if ARCH(I386)
FlatPtr* stack = &state.userspace_esp;
#elif ARCH(X86_64)
FlatPtr* stack = &state.userspace_esp;
#endif
FlatPtr old_esp = *stack;
FlatPtr ret_eip = state.eip;
FlatPtr ret_eflags = state.eflags;
#elif ARCH(X86_64)
FlatPtr* stack = &state.userspace_esp;
#endif
#if SIGNAL_DEBUG
klog() << "signal: setting up user stack to return to eip: " << String::format("%p", (void*)ret_eip) << " esp: " << String::format("%p", (void*)old_esp);

View file

@ -75,7 +75,7 @@ RefPtr<AnonymousVMObject> AnonymousVMObject::create_with_size(size_t size, Alloc
{
if (commit == AllocationStrategy::Reserve || commit == AllocationStrategy::AllocateNow) {
// We need to attempt to commit before actually creating the object
if (!MM.commit_user_physical_pages(ceil_div(size, PAGE_SIZE)))
if (!MM.commit_user_physical_pages(ceil_div(size, static_cast<size_t>(PAGE_SIZE))))
return {};
}
return adopt(*new AnonymousVMObject(size, commit));

View file

@ -699,7 +699,7 @@ NonnullRefPtrVector<PhysicalPage> MemoryManager::allocate_contiguous_supervisor_
{
VERIFY(!(size % PAGE_SIZE));
ScopedSpinLock lock(s_mm_lock);
size_t count = ceil_div(size, PAGE_SIZE);
size_t count = ceil_div(size, static_cast<size_t>(PAGE_SIZE));
NonnullRefPtrVector<PhysicalPage> physical_pages;
for (auto& region : m_super_physical_regions) {

View file

@ -57,12 +57,12 @@ constexpr FlatPtr page_round_down(FlatPtr x)
return ((FlatPtr)(x)) & ~(PAGE_SIZE - 1);
}
inline u32 low_physical_to_virtual(u32 physical)
inline FlatPtr low_physical_to_virtual(FlatPtr physical)
{
return physical + 0xc0000000;
}
inline u32 virtual_to_low_physical(u32 physical)
inline FlatPtr virtual_to_low_physical(FlatPtr physical)
{
return physical - 0xc0000000;
}

View file

@ -37,7 +37,7 @@ VMObject::VMObject(const VMObject& other)
VMObject::VMObject(size_t size)
{
m_physical_pages.resize(ceil_div(size, PAGE_SIZE));
m_physical_pages.resize(ceil_div(size, static_cast<size_t>(PAGE_SIZE)));
MM.register_vmobject(*this);
}

View file

@ -272,7 +272,11 @@ void init_stage2(void*)
}
}
// FIXME: This should not be i386-specific.
#if ARCH(I386)
USB::UHCIController::detect();
#endif
DMIExpose::initialize();
E1000NetworkAdapter::detect();