akaros/kern/arch/x86/arch.h
<<
>>
Prefs
   1#pragma once
   2
   3#include <ros/arch/arch.h>
   4#include <ros/common.h>
   5#include <arch/x86.h>
   6
   7/* Arch Constants */
   8#define ARCH_CL_SIZE                             64
   9
  10static inline void breakpoint(void) __attribute__((always_inline));
  11static inline void icache_flush_page(void *va, void *kva)
  12              __attribute__((always_inline));
  13static inline uint64_t read_tsc(void) __attribute__((always_inline));
  14static inline uint64_t read_tscp(void) __attribute__((always_inline));
  15static inline uint64_t read_tsc_serialized(void) __attribute__((always_inline));
  16static inline void enable_irq(void) __attribute__((always_inline));
  17static inline void disable_irq(void) __attribute__((always_inline));
  18static inline void enable_irqsave(int8_t *state) __attribute__((always_inline));
  19static inline void disable_irqsave(int8_t *state)
  20              __attribute__((always_inline));
  21static inline void cpu_relax(void) __attribute__((always_inline));
  22static inline void clflush(uintptr_t* addr) __attribute__((always_inline));
  23static inline int irq_is_enabled(void) __attribute__((always_inline));
  24static inline void cache_flush(void) __attribute__((always_inline));
  25static inline void reboot(void)
  26              __attribute__((always_inline)) __attribute__((noreturn));
  27static inline void prefetch(void *addr);
  28static inline void prefetchw(void *addr);
  29static inline void swap_gs(void);
  30static inline void __attribute__((noreturn))
  31__reset_stack_pointer(void *arg, uintptr_t sp, void (*f)(void *));
  32
  33/* in trap.c */
  34void send_ipi(uint32_t os_coreid, uint8_t vector);
  35/* in cpuinfo.c */
  36int x86_family, x86_model, x86_stepping;
  37void print_cpuinfo(void);
  38void show_mapping(pgdir_t pgdir, uintptr_t start, size_t size);
  39int vendor_id(char *);
  40/* pmap.c */
  41void invlpg(void *addr);
  42void tlbflush(void);
  43void tlb_flush_global(void);
  44/* idle.c */
  45void cpu_halt(void);
  46struct preempt_data;
  47void cpu_halt_notif_pending(struct preempt_data *vcpd);
  48
  49static inline void breakpoint(void)
  50{
  51        asm volatile("int3");
  52}
  53
  54static inline void icache_flush_page(void *va, void *kva)
  55{
  56        // x86 handles self-modifying code (mostly) without SW support
  57}
  58
  59static inline uint64_t read_tsc(void)
  60{
  61        uint32_t edx, eax;
  62        asm volatile("rdtsc" : "=d"(edx), "=a"(eax));
  63        return (uint64_t)edx << 32 | eax;
  64}
  65
  66/* non-core-id reporting style (it is in ecx) */
  67static inline uint64_t read_tscp(void)
  68{
  69        uint32_t edx, eax;
  70        asm volatile("rdtscp" : "=d"(edx), "=a"(eax) : : X86_REG_CX);
  71        return (uint64_t)edx << 32 | eax;
  72}
  73
  74static inline void mwait(void *eax)
  75{
  76        asm volatile("xorq %%rcx, %%rcx;"
  77                     "xorq %%rdx, %%rdx;"
  78                     "monitor;"
  79                                 /* this is racy, generically.  we never check
  80                                  * if the write to the monitored address
  81                                  * happened already. */
  82                     "movq $0, %%rax;"  /* c-state hint.  this is C1 */
  83                     "mwait;"
  84                     : : "a"(eax));
  85}
  86
  87/* Check out k/a/x86/rdtsc_test.c for more info */
  88static inline uint64_t read_tsc_serialized(void)
  89{
  90        asm volatile("lfence" ::: "memory");    /* mfence on amd? */
  91        return read_tsc();
  92}
  93
  94static inline void enable_irq(void)
  95{
  96        asm volatile("sti");
  97}
  98
  99static inline void disable_irq(void)
 100{
 101        asm volatile("cli");
 102}
 103
 104static inline void enable_irqsave(int8_t *state)
 105{
 106        // *state tracks the number of nested enables and disables
 107        // initial value of state: 0 = first run / no favorite
 108        // > 0 means more enabled calls have been made
 109        // < 0 means more disabled calls have been made
 110        // Mostly doing this so we can call disable_irqsave first if we want
 111
 112        // one side or another "gets a point" if interrupts were already the way
 113        // it wanted to go.  o/w, state stays at 0.  if the state was not 0
 114        // then, enabling/disabling isn't even an option.  just
 115        // increment/decrement
 116
 117        // if enabling is winning or tied, make sure it's enabled
 118        if ((*state == 0) && !irq_is_enabled())
 119                enable_irq();
 120        else
 121                (*state)++;
 122}
 123
 124static inline void disable_irqsave(int8_t *state)
 125{
 126        if ((*state == 0) && irq_is_enabled())
 127                disable_irq();
 128        else
 129                (*state)--;
 130}
 131
 132static inline void cpu_relax(void)
 133{
 134        __cpu_relax();
 135}
 136
 137static inline void clflush(uintptr_t* addr)
 138{
 139        asm volatile("clflush %0" : : "m"(*addr));
 140}
 141
 142static inline int irq_is_enabled(void)
 143{
 144        return read_flags() & FL_IF;
 145}
 146
 147static inline void cache_flush(void)
 148{
 149        wbinvd();
 150}
 151
 152static inline void reboot(void)
 153{
 154        uint8_t cf9 = inb(0xcf9) & ~6;
 155
 156        outb(0x92, 0x3);
 157        outb(0xcf9, cf9 | 2);
 158        outb(0xcf9, cf9 | 6);
 159        asm volatile ("mov $0, %"X86_REG_SP"; int $0");
 160        while (1);
 161}
 162
 163static inline void prefetch(void *addr)
 164{
 165        asm volatile("prefetchnta (%0)" : : "r"(addr));
 166}
 167
 168static inline void prefetchw(void *addr)
 169{
 170        asm volatile("prefetchw (%0)" : : "r"(addr));
 171}
 172
 173/* Guest VMs have a maximum physical address they can use.  Guest
 174 * physical addresses are mapped into this MCP 1:1, but limited to
 175 * this max address *in hardware*.  I.e., the MCP process can address
 176 * more memory than the VMMCP can.  This is great; it means that
 177 * keeping VM management stuff separate from the VM is trivial: just
 178 * map it above max_vm_address. There's no need, as in other systems,
 179 * to tweak the page table or root pointer to protect management
 180 * memory from VM memory.
 181 *
 182 * TODO: read a register the first time this is called and save it
 183 * away.  But this is more than enough for now.
 184 */
 185static inline uint64_t max_guest_pa(void)
 186{
 187        return (1ULL<<40) - 1;
 188}
 189
 190static inline void swap_gs(void)
 191{
 192        asm volatile ("swapgs");
 193}
 194
 195/* Resets a stack pointer to sp, then calls f(arg) */
 196static inline void __attribute__((noreturn))
 197__reset_stack_pointer(void *arg, uintptr_t sp, void (*f)(void *))
 198{
 199        /* FP must be zeroed before SP.  Ideally, we'd do both atomically.  If
 200         * we take an IRQ/NMI in between and set SP first, then a backtrace
 201         * would be confused since FP points *below* the SP that the *IRQ
 202         * handler* is now using.  By zeroing FP first, at least we won't BT at
 203         * all (though FP is still out of sync with SP). */
 204        asm volatile ("mov $0x0, %%rbp;"
 205                      "mov %0, %%rsp;"
 206                      "jmp *%%rdx;"
 207                      : : "q"(sp), "D"(arg), "d"(f));
 208        while (1);
 209}
 210