| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // Copyright (c) 2015 Intel Corporation |
| // Copyright (c) 2016 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #pragma once |
| |
| #include <zircon/compiler.h> |
| #include <sys/types.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| |
| #include <arch/x86/general_regs.h> |
| #include <arch/x86/registers.h> |
| |
| __BEGIN_CDECLS |
| |
| #define X86_8BYTE_MASK 0xFFFFFFFF |
| |
| struct x86_64_iframe { |
| uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax; // pushed by common handler |
| uint64_t r8, r9, r10, r11, r12, r13, r14, r15; // pushed by common handler |
| uint64_t vector; // pushed by stub |
| uint64_t err_code; // pushed by interrupt or stub |
| uint64_t ip, cs, flags; // pushed by interrupt |
| uint64_t user_sp, user_ss; // pushed by interrupt |
| }; |
| |
| typedef struct x86_64_iframe x86_iframe_t; |
| |
| void x86_exception_handler(x86_iframe_t *frame); |
| enum handler_return platform_irq(x86_iframe_t *frame); |
| |
| struct arch_exception_context { |
| bool is_page_fault; |
| x86_iframe_t *frame; |
| uint64_t cr2; |
| }; |
| |
| // Register state layout used by x86_64_context_switch(). |
| struct x86_64_context_switch_frame { |
| uint64_t r15, r14, r13, r12; |
| uint64_t rbp; |
| uint64_t rbx; |
| uint64_t rip; |
| }; |
| |
| struct x86_64_syscall_result { |
| // The assembler relies on the fact that the ABI will return this in |
| // rax,rdx so we use plain types here to ensure this. |
| uint64_t status; |
| // Non-zero if thread was signaled. |
| uint64_t is_signaled; |
| }; |
| |
| void x86_64_context_switch(vaddr_t *oldsp, vaddr_t newsp); |
| void x86_uspace_entry(uintptr_t arg1, uintptr_t arg2, uintptr_t sp, |
| uintptr_t pc, uint64_t rflags) __NO_RETURN; |
| |
| struct x86_64_syscall_result unknown_syscall( |
| uint64_t syscall_num, uint64_t ip); |
| |
| void x86_syscall(void); |
| |
| void x86_syscall_process_pending_signals(x86_syscall_general_regs_t *gregs); |
| |
| /* @brief Register all of the CPUs in the system |
| * |
| * Must be called only once. |
| * |
| * @param apic_ids A list of all APIC IDs in the system. The BP should be in |
| * the list. |
| * @param num_cpus The number of entries in the apic_ids list. |
| */ |
| void x86_init_smp(uint32_t *apic_ids, uint32_t num_cpus); |
| |
| /* @brief Bring all of the specified APs up and hand them over to the kernel |
| * |
| * This function must not be called before x86_init_smp. |
| * |
| * May be called by any running CPU. Due to requiring use of the very limited |
| * low 1MB of memory, this function is not re-entrant. Itshould not be executed |
| * more than once concurrently. |
| * |
| * @param apic_ids A list of all APIC IDs to launch. |
| * @param count The number of entries in the apic_ids list. |
| * |
| * @return ZX_ERR_INVALID_ARGS if an unknown APIC ID was provided. |
| * @return ZX_ERR_BAD_STATE if one of the targets is currently online |
| * @return ZX_ERR_TIMED_OUT if one of the targets failed to launch |
| */ |
| status_t x86_bringup_aps(uint32_t *apic_ids, uint32_t count); |
| |
| #define IO_BITMAP_BITS 65536 |
| #define IO_BITMAP_BYTES (IO_BITMAP_BITS/8) |
| #define IO_BITMAP_LONGS (IO_BITMAP_BITS/sizeof(long)) |
| |
| /* |
| * Assignment of Interrupt Stack Table entries |
| */ |
| #define NUM_ASSIGNED_IST_ENTRIES 3 |
| #define NMI_IST_INDEX 1 |
| #define MCE_IST_INDEX 2 |
| #define DBF_IST_INDEX 3 |
| |
| /* |
| * x86-64 TSS structure |
| */ |
| typedef struct { |
| uint32_t rsvd0; |
| uint64_t rsp0; |
| uint64_t rsp1; |
| uint64_t rsp2; |
| uint32_t rsvd1; |
| uint32_t rsvd2; |
| uint64_t ist1; |
| uint64_t ist2; |
| uint64_t ist3; |
| uint64_t ist4; |
| uint64_t ist5; |
| uint64_t ist6; |
| uint64_t ist7; |
| uint32_t rsvd3; |
| uint32_t rsvd4; |
| uint16_t rsvd5; |
| uint16_t iomap_base; |
| |
| uint8_t tss_bitmap[IO_BITMAP_BYTES + 1]; |
| } __PACKED tss_64_t; |
| |
| typedef tss_64_t tss_t; |
| |
| static inline void x86_clts(void) {__asm__ __volatile__ ("clts"); } |
| static inline void x86_hlt(void) {__asm__ __volatile__ ("hlt"); } |
| static inline void x86_sti(void) {__asm__ __volatile__ ("sti"); } |
| static inline void x86_cli(void) {__asm__ __volatile__ ("cli"); } |
| static inline void x86_ltr(uint16_t sel) |
| { |
| __asm__ __volatile__ ("ltr %%ax" :: "a" (sel)); |
| } |
| static inline void x86_lidt(uintptr_t base) |
| { |
| __asm volatile("lidt (%0)" :: "r"(base) : "memory"); |
| } |
| |
| static inline uint8_t inp(uint16_t _port) |
| { |
| uint8_t rv; |
| __asm__ __volatile__ ("inb %1, %0" |
| : "=a" (rv) |
| : "d" (_port)); |
| return (rv); |
| } |
| |
| static inline uint16_t inpw (uint16_t _port) |
| { |
| uint16_t rv; |
| __asm__ __volatile__ ("inw %1, %0" |
| : "=a" (rv) |
| : "d" (_port)); |
| return (rv); |
| } |
| |
| static inline uint32_t inpd(uint16_t _port) |
| { |
| uint32_t rv; |
| __asm__ __volatile__ ("inl %1, %0" |
| : "=a" (rv) |
| : "d" (_port)); |
| return (rv); |
| } |
| |
| static inline void outp(uint16_t _port, uint8_t _data) |
| { |
| __asm__ __volatile__ ("outb %1, %0" |
| : |
| : "d" (_port), |
| "a" (_data)); |
| } |
| |
| static inline void outpw(uint16_t _port, uint16_t _data) |
| { |
| __asm__ __volatile__ ("outw %1, %0" |
| : |
| : "d" (_port), |
| "a" (_data)); |
| } |
| |
| static inline void outpd(uint16_t _port, uint32_t _data) |
| { |
| __asm__ __volatile__ ("outl %1, %0" |
| : |
| : "d" (_port), |
| "a" (_data)); |
| } |
| |
| static inline uint64_t rdtsc(void) |
| { |
| uint64_t tsc; |
| |
| uint32_t tsc_low; |
| uint32_t tsc_hi; |
| |
| __asm__ __volatile__("rdtsc" : "=a" (tsc_low), "=d" (tsc_hi)); |
| |
| tsc = ((uint64_t)tsc_hi << 32) | tsc_low; |
| |
| return tsc; |
| } |
| |
| static inline void cpuid(uint32_t sel, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) |
| { |
| *a = sel; |
| |
| __asm__ __volatile__("cpuid" |
| : "+a" (*a), "=c" (*c), "=d" (*d), "=b"(*b) |
| ); |
| } |
| |
| /* cpuid wrapper with ecx set to a second argument */ |
| static inline void cpuid_c(uint32_t sel, uint32_t sel_c, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) |
| { |
| *a = sel; |
| *c = sel_c; |
| |
| __asm__ __volatile__("cpuid" |
| : "+a" (*a), "+c" (*c), "=d" (*d), "=b"(*b) |
| ); |
| } |
| |
| static inline void set_in_cr0(ulong mask) |
| { |
| ulong temp; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr0, %0 \n\t" |
| "or %1, %0 \n\t" |
| "mov %0, %%cr0 \n\t" |
| : "=r" (temp) : "irg" (mask) |
| :); |
| } |
| |
| static inline void clear_in_cr0(ulong mask) |
| { |
| ulong temp; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr0, %0 \n\t" |
| "and %1, %0 \n\t" |
| "mov %0, %%cr0 \n\t" |
| : "=r" (temp) : "irg" (~mask) |
| :); |
| } |
| |
| static inline ulong x86_get_cr2(void) |
| { |
| ulong rv; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr2, %0" |
| : "=r" (rv) |
| ); |
| |
| return rv; |
| } |
| |
| static inline ulong x86_get_cr3(void) |
| { |
| ulong rv; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr3, %0" |
| : "=r" (rv)); |
| return rv; |
| } |
| |
| static inline void x86_set_cr3(ulong in_val) |
| { |
| __asm__ __volatile__ ( |
| "mov %0,%%cr3 \n\t" |
| : |
| :"r" (in_val)); |
| } |
| |
| static inline ulong x86_get_cr0(void) |
| { |
| ulong rv; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr0, %0 \n\t" |
| : "=r" (rv)); |
| return rv; |
| } |
| |
| static inline ulong x86_get_cr4(void) |
| { |
| ulong rv; |
| |
| __asm__ __volatile__ ( |
| "mov %%cr4, %0 \n\t" |
| : "=r" (rv)); |
| return rv; |
| } |
| |
| |
| static inline void x86_set_cr0(ulong in_val) |
| { |
| __asm__ __volatile__ ( |
| "mov %0,%%cr0 \n\t" |
| : |
| :"r" (in_val)); |
| } |
| |
| static inline void x86_set_cr4(ulong in_val) |
| { |
| __asm__ __volatile__ ( |
| "mov %0,%%cr4 \n\t" |
| : |
| :"r" (in_val)); |
| } |
| |
| #define DEFINE_REGISTER_ACCESSOR(REG) \ |
| static inline void set_##REG(uint16_t value) { \ |
| __asm__ volatile("mov %0, %%" #REG : : "r"(value)); \ |
| } \ |
| static inline uint16_t get_##REG(void) { \ |
| uint16_t value; \ |
| __asm__ volatile("mov %%" #REG ", %0" : "=r"(value)); \ |
| return value; \ |
| } |
| |
| DEFINE_REGISTER_ACCESSOR(ds) |
| DEFINE_REGISTER_ACCESSOR(es) |
| DEFINE_REGISTER_ACCESSOR(fs) |
| DEFINE_REGISTER_ACCESSOR(gs) |
| |
| #undef DEFINE_REGISTER_ACCESSOR |
| |
| static inline uint64_t read_msr (uint32_t msr_id) |
| { |
| uint32_t msr_read_val_lo; |
| uint32_t msr_read_val_hi; |
| |
| __asm__ __volatile__ ( |
| "rdmsr \n\t" |
| : "=a" (msr_read_val_lo), "=d" (msr_read_val_hi) |
| : "c" (msr_id)); |
| |
| return ((uint64_t)msr_read_val_hi << 32) | msr_read_val_lo; |
| } |
| |
| status_t read_msr_safe(uint32_t msr_id, uint64_t *val); |
| |
| static inline void write_msr (uint32_t msr_id, uint64_t msr_write_val) |
| { |
| __asm__ __volatile__ ( |
| "wrmsr \n\t" |
| : : "c" (msr_id), "a" (msr_write_val & 0xffffffff), "d" (msr_write_val >> 32)); |
| } |
| |
| |
| static inline bool x86_is_paging_enabled(void) |
| { |
| if (x86_get_cr0() & X86_CR0_PG) |
| return true; |
| |
| return false; |
| } |
| |
| static inline bool x86_is_PAE_enabled(void) |
| { |
| if (x86_is_paging_enabled() == false) |
| return false; |
| |
| if (!(x86_get_cr4() & X86_CR4_PAE)) |
| return false; |
| |
| return true; |
| } |
| |
| static inline uint64_t x86_read_gs_offset64(uintptr_t offset) |
| { |
| uint64_t ret; |
| __asm__( "movq %%gs:%1, %0" : "=r" (ret) : "m" (*(uint64_t *)(offset))); |
| return ret; |
| } |
| |
| static inline void x86_write_gs_offset64(uintptr_t offset, uint64_t val) |
| { |
| __asm__( "movq %0, %%gs:%1" : : "ir" (val), "m" (*(uint64_t *)(offset)) : "memory"); |
| } |
| |
| static inline uint32_t x86_read_gs_offset32(uintptr_t offset) |
| { |
| uint32_t ret; |
| __asm__( "movl %%gs:%1, %0" : "=r" (ret) : "m" (*(uint32_t *)(offset))); |
| return ret; |
| } |
| |
| static inline void x86_write_gs_offset32(uintptr_t offset, uint32_t val) |
| { |
| __asm__( "movl %0, %%gs:%1" : : "ir" (val), "m" (*(uint32_t *)(offset)) : "memory"); |
| } |
| |
| typedef uint64_t x86_flags_t; |
| |
| static inline uint64_t x86_save_flags(void) |
| { |
| uint64_t state; |
| |
| __asm__ volatile( |
| "pushfq;" |
| "popq %0" |
| : "=rm" (state) |
| :: "memory"); |
| |
| return state; |
| } |
| |
| static inline void x86_restore_flags(uint64_t flags) |
| { |
| __asm__ volatile( |
| "pushq %0;" |
| "popfq" |
| :: "g" (flags) |
| : "memory", "cc"); |
| } |
| |
| static inline void inprep(uint16_t _port, uint8_t *_buffer, uint32_t _reads) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep insb \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "D" (_buffer), |
| "c" (_reads)); |
| } |
| |
| static inline void outprep(uint16_t _port, uint8_t *_buffer, uint32_t _writes) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep outsb \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "S" (_buffer), |
| "c" (_writes)); |
| } |
| |
| static inline void inpwrep(uint16_t _port, uint16_t *_buffer, uint32_t _reads) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep insw \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "D" (_buffer), |
| "c" (_reads)); |
| } |
| |
| static inline void outpwrep(uint16_t _port, uint16_t *_buffer, |
| uint32_t _writes) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep outsw \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "S" (_buffer), |
| "c" (_writes)); |
| } |
| |
| static inline void inpdrep(uint16_t _port, uint32_t *_buffer, |
| uint32_t _reads) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep insl \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "D" (_buffer), |
| "c" (_reads)); |
| } |
| |
| static inline void outpdrep(uint16_t _port, uint32_t *_buffer, |
| uint32_t _writes) |
| { |
| __asm__ __volatile__ ("pushfq \n\t" |
| "cli \n\t" |
| "cld \n\t" |
| "rep outsl \n\t" |
| "popfq \n\t" |
| : |
| : "d" (_port), |
| "S" (_buffer), |
| "c" (_writes)); |
| } |
| |
| __END_CDECLS |