/********************************************************************* * * Copyright (C) 2002-2009, Karlsruhe University * * File path: arch/x86/x64/init32.cc * Description: Switch to 64bit long mode * This file is compiled as 32bit Code * * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: init32.cc,v 1.8 2006/11/30 15:32:07 stoess Exp $ * ********************************************************************/ #include <init.h> #include INC_ARCH(cpu.h) #include INC_ARCH(mmu.h) #include INC_ARCH(segdesc.h) #include INC_ARCH(x86.h) #include INC_GLUE_SA(offsets.h) /* ugly ... */ #define KERNEL_OFFSET_TYPED X86_64BIT_TYPED(KERNEL_OFFSET) /* Init Pagemap - We use 2MByt Page Translation, (only :-) 3 levels */ u64_t init_pdir[512] __attribute__((aligned(4096))) SECTION(".init.data"); u64_t init_pdp[512] __attribute__((aligned(4096))) SECTION(".init.data"); u64_t init_pml4[512] __attribute__((aligned(4096))) SECTION(".init.data"); #define INIT32_PDIR_ENTRIES 256 #define INIT32_PDIR_ATTRIBS (X86_PAGE_VALID | X86_PAGE_WRITABLE | X86_PAGE_SUPER) #define INIT32_PTAB_ATTRIBS (X86_PAGE_VALID | X86_PAGE_WRITABLE) /* Temporary GDT (INVALID, CS), temporary CS */ x86_segdesc_t init32_gdt[2] SECTION(".init.data"); #define INIT32_CS 0x08 /* Temporary debug messaging system */ #if defined(CONFIG_CONS_KBD) #define INIT32_DEBUG_SCREEN (0xb8000) INLINE void SECTION(".init.init32") init32_spin(int pos = 0) { while(1) { ((u16_t *) INIT32_DEBUG_SCREEN)[pos] += 1; } } static void SECTION(".init.init32") init32_cons (void) {}; #else #if !defined(CONFIG_KDB_COMPORT) #define CONFIG_KDB_COMPORT 0x3f8 #endif #if !defined(CONFIG_KDB_COMSPEED) #define CONFIG_KDB_COMSPEED 115200 #endif #define INIT32_COMPORT CONFIG_KDB_COMPORT #define INIT32_RATE CONFIG_KDB_COMSPEED inline void SECTION(".init.init32") init32_out(const u16_t port, const u8_t val) { /* GCC can optimize here if constant */ __asm__ __volatile__("outb %1, %0\n" : : "dN"(port), "a"(val)); } static inline u8_t SECTION(".init.init32") init32_in(const u16_t port) { u8_t tmp; /* GCC can optimize here if constant */ __asm__ __volatile__("inb %1, %0\n" : "=a"(tmp) : "dN"(port)); return tmp; } static inline void SECTION(".init.init32") init32_putc(const char c) { while ((init32_in(INIT32_COMPORT+5) & 0x60) == 0); init32_out(INIT32_COMPORT,c); } static void SECTION(".init.init32") init32_spin (int pos = 0) { char c= 'A'; while (1) { if (c++ == 'Z') c = 'A'; for (int i=0; i < pos; i++) init32_putc(' '); init32_putc(c); init32_putc('\r'); } } static void SECTION(".init.init32") init32_cons (void) { #define IER (INIT32_COMPORT+1) #define EIR (INIT32_COMPORT+2) #define LCR (INIT32_COMPORT+3) #define MCR (INIT32_COMPORT+4) #define LSR (INIT32_COMPORT+5) #define MSR (INIT32_COMPORT+6) #define DLLO (INIT32_COMPORT+0) #define DLHI (INIT32_COMPORT+1) init32_out(LCR, 0x80); /* select bank 1 */ # if !defined(CONFIG_CPU_X86_SIMICS) //for (volatile int i = 10000000; i--; ); #endif init32_out(DLLO, (((115200/INIT32_RATE) >> 0) & 0x00FF)); init32_out(DLHI, (((115200/INIT32_RATE) >> 8) & 0x00FF)); init32_out(LCR, 0x03); /* set 8,N,1 */ init32_out(IER, 0x00); /* disable interrupts */ init32_out(EIR, 0x07); /* enable FIFOs */ init32_out(IER, 0x01); /* enable RX interrupts */ init32_in(IER); init32_in(EIR); init32_in(LCR); init32_in(MCR); init32_in(LSR); init32_in(MSR); } #endif /** * * Initializes paged 64bit mode * * Precondition: paged or non paged protected mode d */ extern "C" void SECTION(".init.init32") init_paging( u32_t is_ap ) { init32_cons(); /* * Test if long mode is available * as long as no real console is available: * spin(x) signalizes error at character position x */ if (!x86_mmu_t::has_long_mode()) init32_spin(1); for (int i=0; i<512; i++){ init_pml4[i] = init_pdp[i] = init_pdir[i] = 0; } /* Create a pagetable hierarchy */ /* ugly... * otherwise we override our own pdir entries or we make entries * outside the pdir */ if (X86_X64_PDIR_IDX(KERNEL_OFFSET_TYPED) != 0 && (X86_X64_PDIR_IDX(KERNEL_OFFSET_TYPED) < INIT32_PDIR_ENTRIES || X86_X64_PDIR_IDX(KERNEL_OFFSET_TYPED) + INIT32_PDIR_ENTRIES > 512ULL)) init32_spin(2); for (int i=0; i< INIT32_PDIR_ENTRIES; i++){ /* the pdir (used twice!) maps 1 GByte */ init_pdir[i] = init_pdir[i + X86_X64_PDIR_IDX(KERNEL_OFFSET_TYPED)] = ((i << X86_SUPERPAGE_BITS) | (INIT32_PDIR_ATTRIBS & X86_SUPERPAGE_FLAGS_MASK)); } /* * the pdp maps 512 GByte * we use it twice for high and and low mapping, since it's only a * preliminary pagetable hierarchy */ init_pdp[0] = init_pdp[X86_X64_PDP_IDX(KERNEL_OFFSET_TYPED) ] =\ (u64_t) ( ( (u32_t) (init_pdir)) | INIT32_PTAB_ATTRIBS); /* * the pml4 needs 2 entries: * one for the first 512 GByte, one for the last 512 GByte */ init_pml4[0] = init_pml4[X86_X64_PML4_IDX(KERNEL_OFFSET_TYPED) ] =\ (u64_t) ( ( (u32_t) (init_pdp)) | INIT32_PTAB_ATTRIBS); /* Disable Paging (Vol. 2, 14.6.1) */ x86_mmu_t::disable_paging(); /* Enable PAE mode - required before long mode */ x86_mmu_t::enable_pae_mode(); /* Enable long mode (not active unless paging is enabled) */ x86_mmu_t::enable_long_mode(); /* Set pagemap base pointer (CR3) */ x86_mmu_t::set_active_pagetable((u64_t) ((u32_t)init_pml4)); /* Enable paged mode */ x86_mmu_t::enable_paging(); /* Success ? */ if (!(x86_mmu_t::long_mode_active())) init32_spin(3); /* Set up temporary GDT (true long mode needs 64bit Code Segment) */ init32_gdt[0].set_seg((u32_t)0, x86_segdesc_t::inv, 0, x86_segdesc_t::m_comp); init32_gdt[1].set_seg((u32_t)0, x86_segdesc_t::code, 0, x86_segdesc_t::m_long); /* Install temporary GDT */ x86_descreg_t gdtr((word_t) init32_gdt, sizeof(init32_gdt)); gdtr.setdescreg(x86_descreg_t::gdtr); /* * Search startup_system (see linker script) */ u8_t *startup64; asm(" call 1f \n\t" /* retrieve ip of next instruction */ "1: popl %0 \n\t" /* save in addr */ : "=r" (startup64) ); startup64 += 1023; bool found = false; for (int n=0; n<4096; n++) { if ( *++startup64 != 0x90) { found = true; break; } } if (found == false) init32_spin(4); /* * Load new (64-bit) code segment, * use it as data segment, too * and leave this type-casting hell... */ asm(" mov %0, %%ds \n\t" " pushw %1 \n\t" /* push segment selector, used by ljmp */ " lea %2, %%eax \n\t" /* load startup_system */ " pushl %%eax \n\t" /* load startup_system */ " movl %3, %%edi \n\t" /* pass AP info to startup_system */ " ljmp *(%%esp) \n\t" /* jump to startup_system and load new CS */ : /* No Output */ : "r" (INIT32_CS), "i" (INIT32_CS), "m" (*startup64), "r" (is_ap) ); init32_spin(5); }