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