added 5 changesets to branch 'refs/remotes/xyzzy-github/x86_64' old head: f69dd487b1c4e0a3f35d6477e9a6720315e89f35 new head: b8a9a3a16073d8f474ee3415e208d04f4b58b4b0 ---------------------------------------------------------------------------- 5a17b2f: Forgot to change driver_settings_file to use FixedWidthPointer. a820f12: Style fixes to elf.cpp. e5fc2bf: Implemented long mode setup/switch code, the bootloader can now start the 64-bit kernel! The setup procedure is fairly simple: create a 64-bit GDT and 64-bit page tables that include all kernel mappings from the 32-bit address space, but at the correct 64-bit address, then go through kernel_args and changes all virtual addresses to 64-bit addresses, and finally switch to long mode and jump to the kernel. 991e5be: Pass correct kernel_args address to the kernel. b8a9a3a: Copied the x86 debug console functions to x86_64. Note that this is only temporary so that I have serial output from the kernel, the x86 and x86_64 versions will be merged later. [ Alex Smith <alex@xxxxxxxxxxxxxxxx> ] ---------------------------------------------------------------------------- 19 files changed, 758 insertions(+), 32 deletions(-) headers/private/kernel/arch/x86/arch_kernel_args.h | 10 +- headers/private/kernel/arch/x86_64/descriptors.h | 149 ++++++++ headers/private/kernel/boot/driver_settings.h | 6 +- src/system/boot/loader/elf.cpp | 14 +- src/system/boot/platform/bios_ia32/Jamfile | 2 + src/system/boot/platform/bios_ia32/long.cpp | 290 ++++++++++++++++ src/system/boot/platform/bios_ia32/long.h | 18 + src/system/boot/platform/bios_ia32/long_asm.S | 75 ++++ src/system/boot/platform/bios_ia32/mmu.cpp | 55 ++- src/system/boot/platform/bios_ia32/mmu.h | 6 + src/system/boot/platform/bios_ia32/smp.cpp | 2 +- src/system/boot/platform/bios_ia32/start.cpp | 7 + src/system/boot/platform/pxe_ia32/Jamfile | 2 + src/system/kernel/arch/x86/arch_cpu.cpp | 2 +- src/system/kernel/arch/x86/arch_int.cpp | 2 +- .../arch/x86/paging/32bit/X86PagingMethod32Bit.cpp | 4 +- .../arch/x86/paging/pae/X86PagingMethodPAE.cpp | 3 +- src/system/kernel/arch/x86_64/arch_cpu.cpp | 2 + .../kernel/arch/x86_64/arch_debug_console.cpp | 141 +++++++- ############################################################################ Commit: 5a17b2f1b272c71940a67474fc029fa5cf40cff6 Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Tue Jun 26 09:54:37 2012 UTC Forgot to change driver_settings_file to use FixedWidthPointer. ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/boot/driver_settings.h b/headers/private/kernel/boot/driver_settings.h index bee57df..03d0d03 100644 --- a/headers/private/kernel/boot/driver_settings.h +++ b/headers/private/kernel/boot/driver_settings.h @@ -13,8 +13,8 @@ struct driver_settings_file { FixedWidthPointer<struct driver_settings_file> next; char name[B_OS_NAME_LENGTH]; - char *buffer; - size_t size; -}; + FixedWidthPointer<char> buffer; + uint32 size; +} _PACKED; #endif /* KERNEL_BOOT_DRIVER_SETTINGS_H */ ############################################################################ Commit: a820f12943d570cfcb6dbb78781dc587f56e0da8 Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Tue Jun 26 09:59:03 2012 UTC Style fixes to elf.cpp. ---------------------------------------------------------------------------- diff --git a/src/system/boot/loader/elf.cpp b/src/system/boot/loader/elf.cpp index 84a1d0a..8494ff7 100644 --- a/src/system/boot/loader/elf.cpp +++ b/src/system/boot/loader/elf.cpp @@ -76,11 +76,11 @@ struct ELF32Class { static inline status_t AllocateRegion(AddrType* _address, AddrType size, uint8 protection, - void **_mappedAddress) + void** _mappedAddress) { status_t status = platform_allocate_region((void**)_address, size, protection, false); - if (status < B_OK) + if (status != B_OK) return status; *_mappedAddress = (void*)*_address; @@ -123,7 +123,7 @@ struct ELF64Class { status_t status = platform_allocate_region(&address, size, protection, false); - if (status < B_OK) + if (status != B_OK) return status; *_mappedAddress = address; @@ -282,7 +282,7 @@ ELFLoader<Class>::Load(int fd, preloaded_image* _image) // can automatically allocate an address, but shall prefer the specified // base address. if (Class::AllocateRegion(&firstRegion->start, totalSize, - B_READ_AREA | B_WRITE_AREA, &mappedRegion) < B_OK) { + B_READ_AREA | B_WRITE_AREA, &mappedRegion) != B_OK) { status = B_NO_MEMORY; goto error1; } @@ -379,7 +379,7 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) (int)image->rel_len / (int)sizeof(RelType))); status = boot_arch_elf_relocate_rel(image, image->rel, image->rel_len); - if (status < B_OK) + if (status != B_OK) return status; } @@ -398,7 +398,7 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) status = boot_arch_elf_relocate_rela(image, (RelaType*)pltrel, image->pltrel_len); } - if (status < B_OK) + if (status != B_OK) return status; } @@ -407,7 +407,7 @@ ELFLoader<Class>::Relocate(preloaded_image* _image) (int)image->rela_len / (int)sizeof(RelaType))); status = boot_arch_elf_relocate_rela(image, image->rela, image->rela_len); - if (status < B_OK) + if (status != B_OK) return status; } ############################################################################ Commit: e5fc2bfcab8c15a3ff7d33c358f9aa82ed73c823 Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Tue Jun 26 10:01:23 2012 UTC Implemented long mode setup/switch code, the bootloader can now start the 64-bit kernel! The setup procedure is fairly simple: create a 64-bit GDT and 64-bit page tables that include all kernel mappings from the 32-bit address space, but at the correct 64-bit address, then go through kernel_args and changes all virtual addresses to 64-bit addresses, and finally switch to long mode and jump to the kernel. ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/arch/x86/arch_kernel_args.h b/headers/private/kernel/arch/x86/arch_kernel_args.h index de17c7c..7e6c967 100644 --- a/headers/private/kernel/arch/x86/arch_kernel_args.h +++ b/headers/private/kernel/arch/x86/arch_kernel_args.h @@ -26,15 +26,15 @@ typedef struct { uint32 system_time_cv_factor; uint64 cpu_clock_speed; uint32 phys_pgdir; - uint32 vir_pgdir; + uint64 vir_pgdir; uint32 num_pgtables; uint32 pgtables[MAX_BOOT_PTABLES]; - uint32 virtual_end; + uint64 virtual_end; uint32 phys_idt; - uint32 vir_idt; + uint64 vir_idt; uint32 phys_gdt; - uint32 vir_gdt; - uint32 page_hole; + uint64 vir_gdt; + uint64 page_hole; // smp stuff uint32 apic_time_cv_factor; // apic ticks per second uint32 apic_phys; diff --git a/headers/private/kernel/arch/x86_64/descriptors.h b/headers/private/kernel/arch/x86_64/descriptors.h new file mode 100644 index 0000000..a1850f0 --- /dev/null +++ b/headers/private/kernel/arch/x86_64/descriptors.h @@ -0,0 +1,149 @@ +/* + * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_X86_64_DESCRIPTORS_H +#define _KERNEL_ARCH_X86_64_DESCRIPTORS_H + + +// Segment definitions. +// Note that the ordering of these is important to SYSCALL/SYSRET. +#define KERNEL_CODE_SEG 0x08 +#define KERNEL_DATA_SEG 0x10 +#define USER_DATA_SEG 0x18 +#define USER_CODE_SEG 0x20 + + +#ifndef _ASSEMBLER + + +#define TSS_BASE_SEGMENT 5 +#define TLS_BASE_SEGMENT (TSS_BASE_SEGMENT + smp_get_num_cpus()) + + +// Structure of a segment descriptor. +struct segment_descriptor { + uint32 limit0 : 16; + uint32 base0 : 24; + uint32 type : 4; + uint32 desc_type : 1; + uint32 dpl : 2; + uint32 present : 1; + uint32 limit1 : 4; + uint32 available : 1; + uint32 long_mode : 1; + uint32 d_b : 1; + uint32 granularity : 1; + uint32 base1 : 8; +} _PACKED; + +// Structure of a TSS segment descriptor. +struct tss_descriptor { + uint32 limit0 : 16; + uint32 base0 : 24; + uint32 type : 4; + uint32 desc_type : 1; + uint32 dpl : 2; + uint32 present : 1; + uint32 limit1 : 4; + uint32 available : 1; + uint32 unused1 : 2; + uint32 granularity : 1; + uint32 base1 : 8; + uint32 base2 : 32; + uint32 unused2 : 32; +} _PACKED; + +// Structure of an interrupt descriptor. +struct interrupt_descriptor { + uint32 base0 : 16; + uint32 sel : 16; + uint32 ist : 3; + uint32 unused : 5; + uint32 flags : 8; + uint32 base1 : 16; + uint32 base2 : 32; + uint32 reserved : 32; +} _PACKED; + +enum descriptor_privilege_levels { + DPL_KERNEL = 0, + DPL_USER = 3, +}; + +enum descriptor_types { + // Code/data descriptor types. + DT_CODE_EXECUTE_ONLY = 0x8, + DT_CODE_ACCESSED = 0x9, + DT_CODE_READABLE = 0xa, + DT_CODE_CONFORM = 0xc, + DT_DATA_READ_ONLY = 0x0, + DT_DATA_ACCESSED = 0x1, + DT_DATA_WRITEABLE = 0x2, + DT_DATA_EXPANSION_DOWN = 0x4, + + // System descriptor types. + DT_TSS = 9, + + // Descriptor types + DT_SYSTEM_SEGMENT = 0, + DT_CODE_DATA_SEGMENT = 1, +}; + + +static inline void +clear_segment_descriptor(segment_descriptor* desc) +{ + *(uint64*)desc = 0; +} + + +static inline void +set_segment_descriptor(segment_descriptor* desc, uint8 type, uint8 dpl) +{ + clear_segment_descriptor(desc); + + // In 64-bit mode the CPU ignores the base/limit of code/data segments, + // it always treats base as 0 and does no limit checks. + desc->base0 = 0; + desc->base1 = 0; + desc->limit0 = 0xFFFF; + desc->limit1 = 0xF; + desc->granularity = 1; + + desc->type = type; + desc->desc_type = DT_CODE_DATA_SEGMENT; + desc->dpl = dpl; + desc->present = 1; + + desc->long_mode = (type & DT_CODE_EXECUTE_ONLY) ? 1 : 0; + // Must be set to 1 for code segments only. +} + + +static inline void +set_tss_descriptor(segment_descriptor* _desc, uint64 base, uint32 limit) +{ + clear_segment_descriptor(_desc); + clear_segment_descriptor(&_desc[1]); + + // The TSS descriptor is a special format in 64-bit mode, it is 16 bytes + // instead of 8. + tss_descriptor* desc = (tss_descriptor*)_desc; + + desc->base0 = base & 0xffffff; + desc->base1 = ((base) >> 24) & 0xff; + desc->base2 = ((base) >> 32); + desc->limit0 = limit & 0xffff; + desc->limit1 = (limit >> 16) & 0xf; + + desc->present = 1; + desc->type = DT_TSS; + desc->desc_type = DT_SYSTEM_SEGMENT; + desc->dpl = DPL_KERNEL; +} + + +#endif /* _ASSEMBLER */ + +#endif /* _KERNEL_ARCH_X86_64_DESCRIPTORS_H */ diff --git a/src/system/boot/platform/bios_ia32/Jamfile b/src/system/boot/platform/bios_ia32/Jamfile index 6c0c273..347d2b4 100644 --- a/src/system/boot/platform/bios_ia32/Jamfile +++ b/src/system/boot/platform/bios_ia32/Jamfile @@ -40,6 +40,8 @@ BootMergeObject boot_platform_bios_ia32.o : hpet.cpp interrupts.cpp interrupts_asm.S + long.cpp + long_asm.S # VESA/DDC EDID decode_edid.c diff --git a/src/system/boot/platform/bios_ia32/long.cpp b/src/system/boot/platform/bios_ia32/long.cpp new file mode 100644 index 0000000..829e2ad --- /dev/null +++ b/src/system/boot/platform/bios_ia32/long.cpp @@ -0,0 +1,290 @@ +/* + * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ + + +// Stop this from being included, it conflicts with the x86_64 version. +#define _KERNEL_ARCH_x86_DESCRIPTORS_H + + +#include "long.h" + +#include <KernelExport.h> + +#include <arch/x86_64/descriptors.h> +#include <arch_system_info.h> +#include <boot/platform.h> +#include <boot/heap.h> +#include <boot/stage2.h> +#include <boot/stdio.h> +#include <kernel.h> + +#include "debug.h" +#include "mmu.h" + + +struct gdt_idt_descr { + uint16 limit; + addr_t base; +} _PACKED; + + +/*! Convert a 32-bit address to a 64-bit address. */ +static inline uint64 +fix_address(uint64 address) +{ + return address - KERNEL_BASE + KERNEL_BASE_64BIT; +} + + +template<typename Type> +inline void +fix_address(FixedWidthPointer<Type>& p) +{ + if(p != NULL) + p.SetTo(fix_address(p.Get())); +} + + +static void +long_gdt_init() +{ + // Allocate memory for the GDT. + segment_descriptor* gdt = (segment_descriptor*) + mmu_allocate_page(&gKernelArgs.arch_args.phys_gdt); + gKernelArgs.arch_args.vir_gdt = fix_address((addr_t)gdt); + + dprintf("GDT at phys 0x%lx, virt 0x%llx\n", gKernelArgs.arch_args.phys_gdt, + gKernelArgs.arch_args.vir_gdt); + + clear_segment_descriptor(&gdt[0]); + + // Set up code/data segments (TSS segments set up later in the kernel). + set_segment_descriptor(&gdt[KERNEL_CODE_SEG / 8], DT_CODE_EXECUTE_ONLY, + DPL_KERNEL); + set_segment_descriptor(&gdt[KERNEL_DATA_SEG / 8], DT_DATA_WRITEABLE, + DPL_KERNEL); + set_segment_descriptor(&gdt[USER_CODE_SEG / 8], DT_CODE_EXECUTE_ONLY, + DPL_USER); + set_segment_descriptor(&gdt[USER_DATA_SEG / 8], DT_DATA_WRITEABLE, + DPL_USER); +} + + +static void +long_idt_init() +{ + interrupt_descriptor* idt = (interrupt_descriptor*) + mmu_allocate_page(&gKernelArgs.arch_args.phys_idt); + gKernelArgs.arch_args.vir_idt = fix_address((addr_t)idt); + + dprintf("IDT at phys 0x%lx, virt 0x%llx\n", gKernelArgs.arch_args.phys_idt, + gKernelArgs.arch_args.vir_idt); + + // The 32-bit kernel gets an IDT with the loader's exception handlers until + // it can set up its own. Can't do that here because they won't work after + // switching to long mode. Therefore, just clear the IDT and leave the + // kernel to set it up. + memset(idt, 0, B_PAGE_SIZE); +} + + +static void +long_mmu_init() +{ + addr_t physicalAddress; + + // Allocate the top level PML4. + uint64* pml4 = (uint64*)mmu_allocate_page(&gKernelArgs.arch_args.phys_pgdir); + memset(pml4, 0, B_PAGE_SIZE); + gKernelArgs.arch_args.vir_pgdir = (uint64)(addr_t)pml4; + + // Identity map the first 1GB of memory, do so using large pages. + + uint64* pdpt = (uint64*)mmu_allocate_page(&physicalAddress); + memset(pdpt, 0, B_PAGE_SIZE); + pml4[0] = physicalAddress | 0x3; + + uint64* pageDir = (uint64*)mmu_allocate_page(&physicalAddress); + memset(pageDir, 0, B_PAGE_SIZE); + pdpt[0] = physicalAddress | 0x3; + + for (uint32 i = 0; i < 512; i++) { + pageDir[i] = (i * 0x200000) | 0x83; + } + + // Allocate tables for the kernel mappings. + + pdpt = (uint64*)mmu_allocate_page(&physicalAddress); + memset(pdpt, 0, B_PAGE_SIZE); + pml4[511] = physicalAddress | 0x3; + + pageDir = (uint64*)mmu_allocate_page(&physicalAddress); + memset(pageDir, 0, B_PAGE_SIZE); + pdpt[510] = physicalAddress | 0x3; + + // Store the virtual memory usage information. + gKernelArgs.virtual_allocated_range[0].start = KERNEL_BASE_64BIT; + gKernelArgs.virtual_allocated_range[0].size = mmu_get_virtual_usage(); + gKernelArgs.num_virtual_allocated_ranges = 1; + + // We can now allocate page tables and duplicate the mappings across from + // the 32-bit address space to them. + uint64* pageTable = NULL; + for (uint32 i = 0; i < gKernelArgs.virtual_allocated_range[0].size + / B_PAGE_SIZE; i++) { + if ((i % 512) == 0) { + pageTable = (uint64*)mmu_allocate_page(&physicalAddress); + memset(pageTable, 0, B_PAGE_SIZE); + pageDir[i / 512] = physicalAddress | 0x3; + + // Just performed another virtual allocation, account for it. + gKernelArgs.virtual_allocated_range[0].size += B_PAGE_SIZE; + } + + // Get the physical address to map. + if (!mmu_get_virtual_mapping(KERNEL_BASE + (i * B_PAGE_SIZE), + &physicalAddress)) + continue; + + pageTable[i % 512] = physicalAddress | 0x3; + } + + gKernelArgs.arch_args.virtual_end = ROUNDUP(KERNEL_BASE_64BIT + + gKernelArgs.virtual_allocated_range[0].size, 0x200000); + + // Sort the address ranges. + sort_address_ranges(gKernelArgs.physical_memory_range, + gKernelArgs.num_physical_memory_ranges); + sort_address_ranges(gKernelArgs.physical_allocated_range, + gKernelArgs.num_physical_allocated_ranges); + sort_address_ranges(gKernelArgs.virtual_allocated_range, + gKernelArgs.num_virtual_allocated_ranges); + + dprintf("phys memory ranges:\n"); + for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { + dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", + gKernelArgs.physical_memory_range[i].start, + gKernelArgs.physical_memory_range[i].size); + } + + dprintf("allocated phys memory ranges:\n"); + for (uint32 i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { + dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", + gKernelArgs.physical_allocated_range[i].start, + gKernelArgs.physical_allocated_range[i].size); + } + + dprintf("allocated virt memory ranges:\n"); + for (uint32 i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { + dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n", + gKernelArgs.virtual_allocated_range[i].start, + gKernelArgs.virtual_allocated_range[i].size); + } +} + + +static void +convert_preloaded_image(preloaded_elf64_image* image) +{ + fix_address(image->next); + fix_address(image->name); + fix_address(image->debug_string_table); + fix_address(image->syms); + fix_address(image->rel); + fix_address(image->rela); + fix_address(image->pltrel); + fix_address(image->debug_symbols); +} + + +/*! Convert all addresses in kernel_args to 64-bit addresses. */ +static void +convert_kernel_args() +{ + fix_address(gKernelArgs.boot_volume); + fix_address(gKernelArgs.vesa_modes); + fix_address(gKernelArgs.edid_info); + fix_address(gKernelArgs.debug_output); + fix_address(gKernelArgs.boot_splash); + fix_address(gKernelArgs.arch_args.apic); + fix_address(gKernelArgs.arch_args.hpet); + + convert_preloaded_image(static_cast<preloaded_elf64_image*>( + gKernelArgs.kernel_image.Pointer())); + fix_address(gKernelArgs.kernel_image); + + // Iterate over the preloaded images. Must save the next address before + // converting, as the next pointer will be converted. + preloaded_image* image = gKernelArgs.preloaded_images; + fix_address(gKernelArgs.preloaded_images); + while (image) { + preloaded_image* next = image->next; + convert_preloaded_image(static_cast<preloaded_elf64_image*>(image)); + image = next; + } + + // Set correct kernel stack addresses. + for (uint32 i = 0; i < gKernelArgs.num_cpus; i++) { + gKernelArgs.cpu_kstack[i].start + = fix_address(gKernelArgs.cpu_kstack[i].start); + } + + // Fix driver settings files. + driver_settings_file* file = gKernelArgs.driver_settings; + fix_address(gKernelArgs.driver_settings); + while (file) { + driver_settings_file* next = file->next; + fix_address(file->next); + fix_address(file->buffer); + file = next; + } +} + + +void +long_start_kernel() +{ + // Check whether long mode is supported. + cpuid_info info; + get_current_cpuid(&info, 0x80000001); + if ((info.regs.edx & (1<<29)) == 0) + panic("64-bit kernel requires a 64-bit CPU"); + + preloaded_elf64_image *image = static_cast<preloaded_elf64_image *>( + gKernelArgs.kernel_image.Pointer()); + + // TODO: x86_64 SMP, disable for now. + gKernelArgs.num_cpus = 1; + + long_gdt_init(); + long_idt_init(); + long_mmu_init(); + convert_kernel_args(); + + debug_cleanup(); + + // Calculate the arguments for long_enter_kernel(). + uint64 entry = image->elf_header.e_entry; + uint64 stackTop = gKernelArgs.cpu_kstack[0].start + + gKernelArgs.cpu_kstack[0].size; + uint64 kernelArgs = fix_address((addr_t)&gKernelArgs); + + dprintf("kernel entry at 0x%llx, stack 0x%llx, args 0x%llx\n", entry, + stackTop, kernelArgs); + + // We're about to enter the kernel -- disable console output. + stdout = NULL; + + // Load the new GDT. The physical address is used because long_enter_kernel + // disables 32-bit paging. + gdt_idt_descr gdtr = { GDT_LIMIT - 1, gKernelArgs.arch_args.phys_gdt }; + asm volatile("lgdt %0" :: "m"(gdtr)); + + // Enter the kernel! + long_enter_kernel(gKernelArgs.arch_args.phys_pgdir, entry, stackTop, + kernelArgs, 0); + panic("Shouldn't get here"); +} + diff --git a/src/system/boot/platform/bios_ia32/long.h b/src/system/boot/platform/bios_ia32/long.h new file mode 100644 index 0000000..de2a039 --- /dev/null +++ b/src/system/boot/platform/bios_ia32/long.h @@ -0,0 +1,18 @@ +/* + * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef LONG_H +#define LONG_H + + +#include <SupportDefs.h> + + +extern "C" void long_enter_kernel(uint32 pml4, uint64 entry, uint64 stackTop, + uint64 kernelArgs, int currentCPU); + +extern void long_start_kernel(); + + +#endif /* LONG_H */ diff --git a/src/system/boot/platform/bios_ia32/long_asm.S b/src/system/boot/platform/bios_ia32/long_asm.S new file mode 100644 index 0000000..d22355c --- /dev/null +++ b/src/system/boot/platform/bios_ia32/long_asm.S @@ -0,0 +1,75 @@ +/* + * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include <asm_defs.h> + +#include <arch/x86_64/descriptors.h> + + +.code32 + + +/*! void long_enter_kernel(uint32 pml4, uint64 entry, uint64 stackTop, + uint64 kernelArgs, int currentCPU); +*/ +FUNCTION(long_enter_kernel): + // Currently running with 32-bit paging tables at an identity mapped + // address. To switch to 64-bit paging we must first disable 32-bit paging, + // otherwise loading the new CR3 will fault. + movl %cr0, %eax + andl $~(1<<31), %eax + movl %eax, %cr0 + + // Enable PAE. + movl %cr4, %eax + orl $(1<<5), %eax + movl %eax, %cr4 + + // Point CR3 to the kernel's PML4. + movl 4(%esp), %eax + movl %eax, %cr3 + + // Enable long mode by setting EFER.LME. + movl $0xC0000080, %ecx + rdmsr + orl $(1<<8), %eax + wrmsr + + // Re-enable paging, which will put us in compatibility mode as we are + // currently in a 32-bit code segment. + movl %cr0, %ecx + orl $(1<<31), %ecx + movl %ecx, %cr0 + + // Jump into the 64-bit code segment. + ljmp $KERNEL_CODE_SEG, $.Llmode +.align 8 +.code64 +.Llmode: + // Set data segments. + mov $KERNEL_DATA_SEG, %ax + mov %ax, %ss + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + // Clear the high 32 bits of RSP. + movl %esp, %esp + + // Get the entry point address, arguments and new stack pointer. + movq 8(%rsp), %rax + movq 24(%rsp), %rdi + movl 32(%rsp), %esi + movq 16(%rsp), %rsp + + // Clear the stack frame/RFLAGS. + xorq %rbp, %rbp + push $0 + popf + + // Call the kernel entry point. + call *%rax diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp b/src/system/boot/platform/bios_ia32/mmu.cpp index cfed3de..93d1a1f 100644 --- a/src/system/boot/platform/bios_ia32/mmu.cpp +++ b/src/system/boot/platform/bios_ia32/mmu.cpp @@ -419,6 +419,24 @@ mmu_allocate(void *virtualAddress, size_t size) } +/*! Allocates a single page and returns both its virtual and physical + addresses. +*/ +void * +mmu_allocate_page(addr_t *_physicalAddress) +{ + addr_t virt = get_next_virtual_page(); + addr_t phys = get_next_physical_page(); + + map_page(virt, phys, kDefaultPageFlags); + + if (_physicalAddress) + *_physicalAddress = phys; + + return (void *)virt; +} + + /*! Allocates the given physical range. \return \c true, if the range could be allocated, \c false otherwise. */ @@ -478,6 +496,35 @@ mmu_free(void *virtualAddress, size_t size) } +size_t +mmu_get_virtual_usage() +{ + return sNextVirtualAddress - KERNEL_BASE; +} + + +bool +mmu_get_virtual_mapping(addr_t virtualAddress, addr_t *_physicalAddress) +{ + if (virtualAddress < KERNEL_BASE) { + panic("mmu_get_virtual_mapping: asked to lookup invalid page %p!\n", + (void *)virtualAddress); + } + + uint32 *pageTable = (uint32 *)(sPageDirectory[virtualAddress + / (B_PAGE_SIZE * 1024)] & 0xfffff000); + uint32 tableEntry = pageTable[(virtualAddress % (B_PAGE_SIZE * 1024)) + / B_PAGE_SIZE]; + + if ((tableEntry & (1<<0)) != 0) { + *_physicalAddress = tableEntry & 0xFFFFF000; + return true; + } else { + return false; + } +} + + /*! Sets up the final and kernel accessible GDT and IDT tables. BIOS calls won't work any longer after this function has been called. @@ -501,10 +548,10 @@ mmu_init_for_kernel(void) map_page(gKernelArgs.arch_args.vir_idt, (uint32)idt, kDefaultPageFlags); // initialize it - interrupts_init_kernel_idt((void*)gKernelArgs.arch_args.vir_idt, + interrupts_init_kernel_idt((void*)(addr_t)gKernelArgs.arch_args.vir_idt, IDT_LIMIT); - TRACE("idt at virtual address 0x%lx\n", gKernelArgs.arch_args.vir_idt); + TRACE("idt at virtual address 0x%llx\n", gKernelArgs.arch_args.vir_idt); } // set up a new gdt @@ -524,7 +571,7 @@ mmu_init_for_kernel(void) // put standard segment descriptors in it segment_descriptor* virtualGDT - = (segment_descriptor*)gKernelArgs.arch_args.vir_gdt; + = (segment_descriptor*)(addr_t)gKernelArgs.arch_args.vir_gdt; clear_segment_descriptor(&virtualGDT[0]); // seg 0x08 - kernel 4GB code @@ -548,7 +595,7 @@ mmu_init_for_kernel(void) // load the GDT gdtDescriptor.limit = GDT_LIMIT - 1; - gdtDescriptor.base = (void*)gKernelArgs.arch_args.vir_gdt; + gdtDescriptor.base = (void*)(addr_t)gKernelArgs.arch_args.vir_gdt; asm("lgdt %0;" : : "m" (gdtDescriptor)); diff --git a/src/system/boot/platform/bios_ia32/mmu.h b/src/system/boot/platform/bios_ia32/mmu.h index fc197e9..9c59677 100644 --- a/src/system/boot/platform/bios_ia32/mmu.h +++ b/src/system/boot/platform/bios_ia32/mmu.h @@ -20,9 +20,15 @@ extern void mmu_init(void); extern void mmu_init_for_kernel(void); extern addr_t mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags); extern void *mmu_allocate(void *virtualAddress, size_t size); +extern void *mmu_allocate_page(addr_t *_physicalAddress); extern bool mmu_allocate_physical(addr_t base, size_t size); extern void mmu_free(void *virtualAddress, size_t size); +// Used by the long mode switch code +extern size_t mmu_get_virtual_usage(); +extern bool mmu_get_virtual_mapping(addr_t virtualAddress, + addr_t *_physicalAddress); + #ifdef __cplusplus } #endif diff --git a/src/system/boot/platform/bios_ia32/smp.cpp b/src/system/boot/platform/bios_ia32/smp.cpp index 4bc2402..a7ec21b 100644 --- a/src/system/boot/platform/bios_ia32/smp.cpp +++ b/src/system/boot/platform/bios_ia32/smp.cpp @@ -366,7 +366,7 @@ smp_cpu_ready(void) // Set up the final idt idt_descr.a = IDT_LIMIT - 1; - idt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_idt; + idt_descr.b = (uint32 *)(addr_t)gKernelArgs.arch_args.vir_idt; asm("lidt %0;" : : "m" (idt_descr)); diff --git a/src/system/boot/platform/bios_ia32/start.cpp b/src/system/boot/platform/bios_ia32/start.cpp index b15d539..717b2c2 100644 --- a/src/system/boot/platform/bios_ia32/start.cpp +++ b/src/system/boot/platform/bios_ia32/start.cpp @@ -22,6 +22,7 @@ #include "hpet.h" #include "interrupts.h" #include "keyboard.h" +#include "long.h" #include "mmu.h" #include "multiboot.h" #include "serial.h" @@ -75,6 +76,12 @@ platform_boot_options(void) extern "C" void platform_start_kernel(void) { + // 64-bit kernel entry is all handled in long.cpp + if (gKernelArgs.kernel_image->elf_class == ELFCLASS64) { + long_start_kernel(); + return; + } + static struct kernel_args *args = &gKernelArgs; // something goes wrong when we pass &gKernelArgs directly // to the assembler inline below - might be a bug in GCC diff --git a/src/system/boot/platform/pxe_ia32/Jamfile b/src/system/boot/platform/pxe_ia32/Jamfile index fe59db7..7adb307 100644 --- a/src/system/boot/platform/pxe_ia32/Jamfile +++ b/src/system/boot/platform/pxe_ia32/Jamfile @@ -37,6 +37,8 @@ local bios_ia32_src = apm.cpp interrupts.cpp interrupts_asm.S + long.cpp + long_asm.S ; local bios_ia32_edid_src = diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp b/src/system/kernel/arch/x86/arch_cpu.cpp index 5ebb194..c2b5a45 100644 --- a/src/system/kernel/arch/x86/arch_cpu.cpp +++ b/src/system/kernel/arch/x86/arch_cpu.cpp @@ -817,7 +817,7 @@ arch_cpu_init_post_vm(kernel_args *args) uint32 i; // account for the segment descriptors - gGDT = (segment_descriptor *)args->arch_args.vir_gdt; + gGDT = (segment_descriptor *)(addr_t)args->arch_args.vir_gdt; create_area("gdt", (void **)&gGDT, B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 123f575..ace0767 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -553,7 +553,7 @@ arch_int_init(struct kernel_args *args) interrupt_handler_function** table; // set the global sIDT variable - sIDTs[0] = (desc_table *)args->arch_args.vir_idt; + sIDTs[0] = (desc_table *)(addr_t)args->arch_args.vir_idt; // setup the standard programmable interrupt controller pic_init(); diff --git a/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.cpp b/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.cpp index 9f84057..674c4b5 100644 --- a/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.cpp +++ b/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.cpp @@ -266,7 +266,7 @@ X86PagingMethod32Bit::Init(kernel_args* args, TRACE("X86PagingMethod32Bit::Init(): entry\n"); // page hole set up in stage2 - fPageHole = (page_table_entry*)args->arch_args.page_hole; + fPageHole = (page_table_entry*)(addr_t)args->arch_args.page_hole; // calculate where the pgdir would be fPageHolePageDir = (page_directory_entry*) (((addr_t)args->arch_args.page_hole) @@ -276,7 +276,7 @@ X86PagingMethod32Bit::Init(kernel_args* args, sizeof(page_directory_entry) * NUM_USER_PGDIR_ENTS); fKernelPhysicalPageDirectory = args->arch_args.phys_pgdir; - fKernelVirtualPageDirectory = (page_directory_entry*) + fKernelVirtualPageDirectory = (page_directory_entry*)(addr_t) args->arch_args.vir_pgdir; #ifdef TRACE_X86_PAGING_METHOD_32_BIT diff --git a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp index e7001bb..280dd14 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp +++ b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp @@ -56,7 +56,8 @@ struct X86PagingMethodPAE::ToPAESwitcher { fKernelArgs(args) { // page hole set up in the boot loader - fPageHole = (page_table_entry*)fKernelArgs->arch_args.page_hole; + fPageHole = (page_table_entry*) + (addr_t)fKernelArgs->arch_args.page_hole; // calculate where the page dir would be fPageHolePageDir = (page_directory_entry*) diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index 6ce9648..125f284 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -78,6 +78,8 @@ static int32 main2(void *); extern "C" int _start(kernel_args *bootKernelArgs, int currentCPU) { + while (1) {} + if (bootKernelArgs->kernel_args_size != sizeof(kernel_args) || bootKernelArgs->version != CURRENT_KERNEL_ARGS_VERSION) { // This is something we cannot handle right now - release kernels ############################################################################ Commit: 991e5bee90ffd98a39f012f6ce4e0740eb620073 Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Tue Jun 26 10:27:00 2012 UTC Pass correct kernel_args address to the kernel. ---------------------------------------------------------------------------- diff --git a/src/system/boot/platform/bios_ia32/long.cpp b/src/system/boot/platform/bios_ia32/long.cpp index 829e2ad..4ae8dc3 100644 --- a/src/system/boot/platform/bios_ia32/long.cpp +++ b/src/system/boot/platform/bios_ia32/long.cpp @@ -269,7 +269,7 @@ long_start_kernel() uint64 entry = image->elf_header.e_entry; uint64 stackTop = gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size; - uint64 kernelArgs = fix_address((addr_t)&gKernelArgs); + uint64 kernelArgs = (addr_t)&gKernelArgs; dprintf("kernel entry at 0x%llx, stack 0x%llx, args 0x%llx\n", entry, stackTop, kernelArgs); ############################################################################ Commit: b8a9a3a16073d8f474ee3415e208d04f4b58b4b0 Author: Alex Smith <alex@xxxxxxxxxxxxxxxx> Date: Tue Jun 26 10:42:28 2012 UTC Copied the x86 debug console functions to x86_64. Note that this is only temporary so that I have serial output from the kernel, the x86 and x86_64 versions will be merged later. ---------------------------------------------------------------------------- diff --git a/src/system/kernel/arch/x86_64/arch_cpu.cpp b/src/system/kernel/arch/x86_64/arch_cpu.cpp index 108b4d1..64b8571 100644 --- a/src/system/kernel/arch/x86_64/arch_cpu.cpp +++ b/src/system/kernel/arch/x86_64/arch_cpu.cpp @@ -30,6 +30,8 @@ arch_cpu_init_percpu(kernel_args *args, int cpu) status_t arch_cpu_init(kernel_args *args) { + dprintf("not implemented...\n"); + while (1); return B_OK; } diff --git a/src/system/kernel/arch/x86_64/arch_debug_console.cpp b/src/system/kernel/arch/x86_64/arch_debug_console.cpp index cae226d..8e5db41 100644 --- a/src/system/kernel/arch/x86_64/arch_debug_console.cpp +++ b/src/system/kernel/arch/x86_64/arch_debug_console.cpp @@ -1,13 +1,74 @@ /* + * Copyright 2002-2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx + * Copyright 2001, Rob Judd <judd@xxxxxxxxxx> + * Copyright 2002, Marcus Overhagen <marcus@xxxxxxxxxxxx> * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxxx * Distributed under the terms of the MIT License. + * + * Copyright 2001, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. */ -#include <debug.h> +#include <KernelExport.h> +#include <driver_settings.h> +#include <int.h> #include <arch/cpu.h> #include <arch/debug_console.h> +#include <boot/stage2.h> +#include <debug.h> + +#include <string.h> +#include <stdlib.h> + + +enum serial_register_offsets { + SERIAL_TRANSMIT_BUFFER = 0, + SERIAL_RECEIVE_BUFFER = 0, + SERIAL_DIVISOR_LATCH_LOW = 0, + SERIAL_DIVISOR_LATCH_HIGH = 1, + SERIAL_FIFO_CONTROL = 2, + SERIAL_LINE_CONTROL = 3, + SERIAL_MODEM_CONTROL = 4, + SERIAL_LINE_STATUS = 5, + SERIAL_MODEM_STATUS = 6, +}; + + +static const uint32 kSerialBaudRate = 115200; +static uint16 sSerialBasePort = 0x3f8; + // COM1 is the default debug output port + +static spinlock sSerialOutputSpinlock = B_SPINLOCK_INITIALIZER; + + +static void +init_serial_port(uint16 basePort, uint32 baudRate) +{ + sSerialBasePort = basePort; + + uint16 divisor = (uint16)(115200 / baudRate); + + out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL); /* set divisor latch access bit */ + out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW); + out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH); + out8(3, sSerialBasePort + SERIAL_LINE_CONTROL); /* 8N1 */ +} + + +static void +put_char(const char c) +{ + // wait until the transmitter empty bit is set + while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0) + asm volatile ("pause;"); + + out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER); +} + + +// #pragma mark - void @@ -43,43 +104,109 @@ arch_debug_blue_screen_getchar(void) int arch_debug_serial_try_getchar(void) { - return -1; + uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS); + if (lineStatus == 0xff) { + // The "data available" bit is set, but also all error bits. Likely we + // don't have a valid I/O port. + return -1; + } + + if ((lineStatus & 0x1) == 0) + return -1; + + return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER); } char arch_debug_serial_getchar(void) { - while(true) + while (true) { + uint8 lineStatus = in8(sSerialBasePort + SERIAL_LINE_STATUS); + if (lineStatus == 0xff) { + // The "data available" bit is set, but also all error bits. Likely + // we don't have a valid I/O port. + return 0; + } + + if ((lineStatus & 0x1) != 0) + break; + PAUSE(); - return 0; + } + + return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER); } +static void +_arch_debug_serial_putchar(const char c) +{ + if (c == '\n') { + put_char('\r'); + put_char('\n'); + } else if (c != '\r') + put_char(c); +} + void arch_debug_serial_putchar(const char c) { - + cpu_status state = 0; + if (!debug_debugger_running()) { + state = disable_interrupts(); + acquire_spinlock(&sSerialOutputSpinlock); + } + + _arch_debug_serial_putchar(c); + + if (!debug_debugger_running()) { + release_spinlock(&sSerialOutputSpinlock); + restore_interrupts(state); + } } void arch_debug_serial_puts(const char *s) { - + cpu_status state = 0; + if (!debug_debugger_running()) { + state = disable_interrupts(); + acquire_spinlock(&sSerialOutputSpinlock); + } + + while (*s != '\0') { + _arch_debug_serial_putchar(*s); + s++; + } + + if (!debug_debugger_running()) { + release_spinlock(&sSerialOutputSpinlock); + restore_interrupts(state); + } } void arch_debug_serial_early_boot_message(const char *string) { - + // this function will only be called in fatal situations + // ToDo: also enable output via text console?! + arch_debug_console_init(NULL); + arch_debug_serial_puts(string); } status_t arch_debug_console_init(kernel_args *args) { + // only use the port if we could find one, else use the standard port + if (args != NULL && args->platform_args.serial_base_ports[0] != 0) + sSerialBasePort = args->platform_args.serial_base_ports[0]; + + init_serial_port(sSerialBasePort, kSerialBaudRate); + return B_OK; } diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index 125f284..6ce9648 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -78,8 +78,6 @@ static int32 main2(void *); extern "C" int _start(kernel_args *bootKernelArgs, int currentCPU) { - while (1) {} - if (bootKernelArgs->kernel_args_size != sizeof(kernel_args) || bootKernelArgs->version != CURRENT_KERNEL_ARGS_VERSION) { // This is something we cannot handle right now - release kernels