blob: 60fd72b5184cffc47c1ff2d9535aa49ee9936c5f [file] [log] [blame] [edit]
/* See COPYRIGHT for copyright information. */
#include <ros/common.h>
#include <smp.h>
#include <arch/x86.h>
#include <arch/pci.h>
#include <arch/console.h>
#include <arch/perfmon.h>
#include <arch/init.h>
#include <console.h>
#include <monitor.h>
#include <arch/usb.h>
#include <assert.h>
#include <ros/procinfo.h>
#include <cpu_feat.h>
struct ancillary_state x86_default_fpu;
uint32_t kerndate;
#define capchar2ctl(x) ((x) - '@')
/* irq handler for the console (kb, serial, etc) */
static void irq_console(struct hw_trapframe *hw_tf, void *data)
{
uint8_t c;
struct cons_dev *cdev = (struct cons_dev*)data;
assert(cdev);
if (cons_get_char(cdev, &c))
return;
/* Control code intercepts */
switch (c) {
case capchar2ctl('G'):
/* traditional 'ctrl-g', will put you in the monitor gracefully */
send_kernel_message(core_id(), __run_mon, 0, 0, 0, KMSG_ROUTINE);
return;
case capchar2ctl('Q'):
/* force you into the monitor. you might deadlock. */
printk("\nForcing entry to the monitor\n");
monitor(hw_tf);
return;
case capchar2ctl('B'):
/* backtrace / debugging for the core receiving the irq */
printk("\nForced trapframe and backtrace for core %d\n", core_id());
if (!hw_tf) {
printk("(no hw_tf, we probably polled the console)\n");
return;
}
print_trapframe(hw_tf);
backtrace_hwtf(hw_tf);
return;
}
/* Do our work in an RKM, instead of interrupt context. Note the RKM will
* cast 'c' to a char. */
send_kernel_message(core_id(), __cons_add_char, (long)&cons_buf, (long)c,
0, KMSG_ROUTINE);
}
static void cons_poller(void *arg)
{
while (1) {
kthread_usleep(10000);
irq_console(0, arg);
}
}
static void cons_irq_init(void)
{
struct cons_dev *i;
/* Register interrupt handlers for all console devices */
SLIST_FOREACH(i, &cdev_list, next) {
register_irq(i->irq, irq_console, i, MKBUS(BusISA, 0, 0, 0));
#ifdef CONFIG_POLL_CONSOLE
ktask("cons_poller", cons_poller, i);
#endif /* CONFIG_POLL_CONSOLE */
}
}
/* Init x86 processor extended state */
void ancillary_state_init(void)
{
uint32_t eax, ebx, ecx, edx;
uint64_t proc_supported_features; /* proc supported user state components */
// If you don't at least have FXSAVE and FXRSTOR
// (includes OSFXSR), you don't boot.
if (!cpu_has_feat(CPU_FEAT_X86_FXSR))
panic("No FXSAVE/FXRSTOR (FXSR) support! Refusing to boot.");
if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
// Next determine the user state components supported
// by the processor and set x86_default_xcr0 in proc_global_info.
cpuid(0x0d, 0x00, &eax, 0, 0, &edx);
proc_supported_features = ((uint64_t)edx << 32) | eax;
// Intersection of processor-supported and Akaros-supported
// features is the Akaros-wide default at runtime.
__proc_global_info.x86_default_xcr0 = X86_MAX_XCR0 &
proc_supported_features;
/*
* Make sure CR4.OSXSAVE is set and set the local xcr0 to the default.
* We will do both of these things again during per-cpu init,
* but we are about to use XSAVE to build our default extended state
* record, so we need them enabled.
* You must set CR4_OSXSAVE before setting xcr0, or a #UD fault occurs.
*/
lcr4(rcr4() | CR4_OSXSAVE);
lxcr0(__proc_global_info.x86_default_xcr0);
/*
* Build a default set of extended state values that we can later use
* to initialize extended state on other cores, or restore on this
* core. We need to use FNINIT to reset the FPU before saving, in case
* boot agents used the FPU or it is dirty for some reason. An old
* comment that used to be here said "had this happen on c89, which had
* a full FP stack after booting." Note that FNINIT does not clear the
* data registers, but it tags them all as empty (0b11).
*/
// Zero the default extended state memory region before saving.
// It may be possible for memset to clobber SSE registers.
memset(&x86_default_fpu, 0x00, sizeof(struct ancillary_state));
/*
* FNINIT clears FIP and FDP and, even though it is technically a
* control instruction, it clears FOP while initializing the FPU.
*
* This marks the STX/MMX registers as empty in the FPU tag word,
* but does not actually clear the values in the registers,
* so we manually clear them in the xsave area after saving.
*/
asm volatile ("fninit");
/*
* Save only the x87 FPU state so that the extended state registers
* remain zeroed in the default. The MXCSR is in a separate state
* component (SSE), so we manually set its value in the default state.
*
* We use XSAVE64 instead of XSAVEOPT64 (save_fp_state uses
* XSAVEOPT64), because XSAVEOPT64 may decide to skip saving a state
* component if that state component is in its initial configuration,
* and we just used FNINIT to put the x87 in its initial configuration.
* We can be confident that the x87 bit (bit 0) is set in xcr0, because
* Intel requires it to be set at all times.
*/
edx = 0x0;
eax = 0x1;
asm volatile("xsave64 %0" : : "m"(x86_default_fpu), "a"(eax), "d"(edx));
// Clear junk that might have been saved from STX/MMX registers
memset(&(x86_default_fpu.st0_mm0), 0x00, 128);
/* We must set the MXCSR field in the default state struct to its
* power-on value of 0x1f80. This masks all SIMD floating
* point exceptions and clears all SIMD floating-point exception
* flags, sets rounding control to round-nearest, disables
* flush-to-zero mode, and disables denormals-are-zero mode.
*
* We don't actually have to set the MXCSR itself here,
* because it will be set from the default state struct when
* we perform per-cpu init.
*
* Right now, we set the MXCSR through fp_head_64d. Since
* the mxcsr is at the same offset in all fp header formats
* implemented for Akaros, this will function correctly for
* all supported operating modes.
*/
x86_default_fpu.fp_head_64d.mxcsr = 0x1f80;
} else {
// Since no program should try to use XSAVE features
// on this processor, we set x86_default_xcr0 to 0x0
__proc_global_info.x86_default_xcr0 = 0x0;
/*
* Build a default set of extended state values that we can later use to
* initialize extended state on other cores, or restore on this core.
* We need to use FNINIT to reset the FPU before saving, in case boot
* agents used the FPU or it is dirty for some reason. An old comment
* that used to be here said "had this happen on c89, which had a full
* FP stack after booting." Note that FNINIT does not clear the data
* registers, but it tags them all as empty (0b11).
*/
// Zero the default extended state memory region before saving.
// It may be possible for memset to clobber SSE registers.
memset(&x86_default_fpu, 0x00, sizeof(struct ancillary_state));
/*
* FNINIT clears FIP and FDP and, even though it is technically a
* control instruction, it clears FOP while initializing the FPU.
*
* This marks the STX/MMX registers as empty in the FPU tag word,
* but does not actually clear the values in the registers,
* so we manually clear them in the xsave area after saving.
*/
asm volatile ("fninit");
// Save the x87 FPU state
asm volatile("fxsave64 %0" : : "m"(x86_default_fpu));
/*
* Clear junk that might have been saved from the STX/MMX registers.
*
* FXSAVE may have also saved junk from the XMM registers,
* depending on how the hardware was implemented and the setting
* of CR4.OSFXSR. So we clear that too.
*
* MMX: 128 bytes, XMM: 256 bytes
*/
memset(&(x86_default_fpu.st0_mm0), 0x00, 128 + 256);
/*
* Finally, because Only the Paranoid Survive, we set the MXCSR
* for our default state. It should have been saved by FXSAVE,
* but who knows if the default value is still there at this
* point in the boot process.
*/
x86_default_fpu.fp_head_64d.mxcsr = 0x1f80;
}
}
void arch_init(void)
{
ancillary_state_init();
pci_init();
vmm_init();
perfmon_global_init();
// this returns when all other cores are done and ready to receive IPIs
#ifdef CONFIG_SINGLE_CORE
smp_percpu_init();
#else
smp_boot();
#endif
proc_init();
cons_irq_init();
intel_lpc_init();
#ifdef CONFIG_ENABLE_LEGACY_USB
printk("Legacy USB support enabled, expect SMM interference!\n");
#else
usb_disable_legacy();
#endif
check_timing_stability();
}