1/** 2 * \file 3 * \brief x86-64 execution and miscellany 4 */ 5 6/* 7 * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <kernel.h> 16#include <init.h> 17#include <barrelfish_kpi/cpu.h> 18#include <barrelfish_kpi/cpu_arch.h> 19#include <exec.h> 20#include <irq.h> 21#include <x86.h> 22#include <dispatch.h> 23 24/** 25 * \brief Reboots the system. 26 * 27 * This function tries hard not to return. 28 */ 29void reboot(void) 30{ 31 struct region_descriptor region = { 32 .rd_limit = 0, 33 .rd_base = 0 34 }; 35 36 printk(LOG_NOTE, "Rebooting...\n"); 37 38 // try PCI reset register 39 uint8_t val = inb(0xcf9) & ~0x6; 40 val |= 0x2; // hard reset mode 41 outb(0xcf9, val); 42 val |= 0x4; // do the reset! 43 outb(0xcf9, val); 44 45 // try to reboot using keyboard controller hack (this works on QEMU) 46 printk(LOG_NOTE, "PCI reset failed, trying keyboard controller\n"); 47 // try 10 times! 48 for (int i = 0; i < 10; i++) { 49 // toggle reset line 50 outb(0x64, 0xfe); 51 } 52 53 // Otherwise load invalid IDT and cause illegal opcode for triple fault 54 printk(LOG_NOTE, "Keyboard controller reset failed, trying triple fault\n"); 55 __asm volatile("lidt %[region] \n\t" 56 "ud2 \n\t" 57 : /* No output */ 58 : [region] "m" (region) 59 ); 60 61 halt(); // trick GCC into thinking we don't return 62} 63 64/** 65 * \brief Triggers a debugger breakpoint. 66 */ 67void breakpoint(void) 68{ 69 if(idt_initialized) { 70 hw_breakpoint(); 71 } else { 72 printk(LOG_PANIC, 73 "Cannot trap into debugger -- Interrupts not set up yet!\n"); 74 } 75} 76 77/** 78 * \brief Go to user-space at entry point 'entry'. 79 * 80 * This function goes to user-space and starts executing the program at 81 * its entry point at virtual address 'entry'. 82 * 83 * \param entry Entry point address of program to execute. 84 */ 85void __attribute__ ((noreturn)) 86execute(lvaddr_t entry) 87{ 88 // FIXME: make argument 89 uintptr_t arg = get_dispatcher_shared_generic(dcb_current->disp)->udisp; 90 91 __asm volatile ("pushl %[ss] \n\t" 92 "pushl $0 \n\t" // ESP 93 "pushl %[eflags] \n\t" 94 "pushl %[cs] \n\t" 95 "pushl %[entry] \n\t" // EIP 96 "movl $0, %%eax \n\t" 97 "movl $0, %%ebx \n\t" 98 "movl $0, %%ecx \n\t" 99 "movl $0, %%edx \n\t" 100 "movl $0, %%esi \n\t" 101 "movl $0, %%ebp \n\t" 102 "mov %%dx, %%fs \n\t" 103 "mov %%dx, %%gs \n\t" 104 "iretl \n\t" 105 : /* No output */ 106 : 107 [ss] "i" (GSEL(USTACK_SEL, SEL_UPL)), 108 [cs] "i" (GSEL(UCODE_SEL, SEL_UPL)), 109 [eflags] "r" (USER_EFLAGS), 110 [disp] "D" (arg), 111 [entry] "r" (entry) 112 ); 113 114 // Trick GCC to believe us not to return 115 halt(); 116} 117 118/** 119 * \brief Resume the given user-space snapshot. 120 * 121 * This function resumes user-space execution by restoring the CPU 122 * registers with the ones given in the array, pointed to by 'regs'. 123 */ 124void __attribute__ ((noreturn)) resume(arch_registers_state_t *state) 125{ 126 struct registers_x86_32 *regs = state; 127 128 __asm volatile ("pushl %[ss] \n\t" 129 "pushl 7*4(%[regs]) \n\t" // ESP 130 "pushl %[eflags] \n\t" 131 "pushl %[cs] \n\t" 132 "pushl 8*4(%[regs]) \n\t" // EIP 133 "mov %[fs], %%fs \n\t" 134 "mov %[gs], %%gs \n\t" 135 "movl 1*4(%[regs]), %%ebx \n\t" 136 "movl 2*4(%[regs]), %%ecx \n\t" 137 "movl 3*4(%[regs]), %%edx \n\t" 138 "movl 4*4(%[regs]), %%esi \n\t" 139 "movl 5*4(%[regs]), %%edi \n\t" 140 "movl 6*4(%[regs]), %%ebp \n\t" 141 "movl 0*4(%[regs]), %%eax \n\t" // EAX was base register 142 "iretl \n\t" 143 : /* No output */ 144 : 145 [regs] "a" (regs), 146 [ss] "i" (GSEL(USTACK_SEL, SEL_UPL)), 147 [cs] "i" (GSEL(UCODE_SEL, SEL_UPL)), 148 [fs] "m" (regs->fs), 149 [gs] "m" (regs->gs), 150 [eflags] "r" ((regs->eflags & USER_EFLAGS_MASK) 151 | USER_EFLAGS) 152 ); 153 154 // Trick GCC to believe us not to return 155 halt(); 156} 157 158/** 159 * \brief Halt processor until an interrupt arrives 160 * 161 * For use in the idle loop when nothing is runnable. 162 */ 163void __attribute__ ((noreturn)) wait_for_interrupt(void) 164{ 165 __asm volatile("mov %[x86_32_kernel_stack], %%esp\n\t" 166 "addl %[stack_size], %%esp\n\t" 167 "sti \n\t" 168 // The instruction right after STI is still in interrupt 169 // shadow. To avoid unecessary calls to HLT we insert a nop 170 // to make sure pending interrupts are handeled immediately. 171 "nop \n\t" 172 "hlt \n\t" 173 :: 174 [x86_32_kernel_stack] "r" (&x86_32_kernel_stack), 175 [stack_size] "i" (X86_32_KERNEL_STACK_SIZE) 176 : "esp" ); 177 panic("hlt should not return"); 178} 179 180/** 181 * \brief Use MONITOR/MWAIT to block until a given word changes 182 * 183 * \param base Virtual address of 64-bit word to monitor 184 * \param lastval Previous value of word 185 * \param extensions Processor-specific extensions (zero for defaults) 186 * \param hints Processor-specific hints (zero for defaults) 187 * 188 * Returns when the 64-bit word at base is not equal to lastval. 189 */ 190void monitor_mwait(lvaddr_t base, uint64_t lastval, uint32_t extensions, 191 uint32_t hints) 192{ 193 volatile uint64_t *val = (uint64_t *)base; 194 195 assert(extensions == 0); 196 assert(hints == 0); 197 198 while(*val == lastval) { 199 monitor(base, extensions, hints); 200 if(*val != lastval) { 201 return; 202 } 203 mwait(hints, extensions); 204 } 205} 206