blob: 7778a0f80034491092043772147aed157b6e6b76 [file] [log] [blame]
#ifndef _X86_DESC_H_
#define _X86_DESC_H_
#include <setjmp.h>
#ifdef __ASSEMBLY__
#define __ASM_FORM(x, ...) x,## __VA_ARGS__
#else
#define __ASM_FORM(x, ...) " " xstr(x,##__VA_ARGS__) " "
#endif
#ifndef __x86_64__
#define __ASM_SEL(a,b) __ASM_FORM(a)
#else
#define __ASM_SEL(a,b) __ASM_FORM(b)
#endif
void setup_idt(void);
void load_idt(void);
void setup_alt_stack(void);
struct ex_regs {
unsigned long rax, rcx, rdx, rbx;
unsigned long dummy, rbp, rsi, rdi;
#ifdef __x86_64__
unsigned long r8, r9, r10, r11;
unsigned long r12, r13, r14, r15;
#endif
unsigned long vector;
unsigned long error_code;
unsigned long rip;
unsigned long cs;
unsigned long rflags;
#ifdef __x86_64__
unsigned long rsp;
unsigned long ss;
#endif
};
typedef void (*handler)(struct ex_regs *regs);
typedef struct {
u16 prev;
u16 res1;
u32 esp0;
u16 ss0;
u16 res2;
u32 esp1;
u16 ss1;
u16 res3;
u32 esp2;
u16 ss2;
u16 res4;
u32 cr3;
u32 eip;
u32 eflags;
u32 eax, ecx, edx, ebx, esp, ebp, esi, edi;
u16 es;
u16 res5;
u16 cs;
u16 res6;
u16 ss;
u16 res7;
u16 ds;
u16 res8;
u16 fs;
u16 res9;
u16 gs;
u16 res10;
u16 ldt;
u16 res11;
u16 t:1;
u16 res12:15;
u16 iomap_base;
} tss32_t;
typedef struct __attribute__((packed)) {
u32 res1;
u64 rsp0;
u64 rsp1;
u64 rsp2;
u64 res2;
u64 ist1;
u64 ist2;
u64 ist3;
u64 ist4;
u64 ist5;
u64 ist6;
u64 ist7;
u64 res3;
u16 res4;
u16 iomap_base;
} tss64_t;
#define __ASM_TRY(prefix, catch) \
"movl $0, %%gs:4\n\t" \
".pushsection .data.ex\n\t" \
__ASM_SEL(.long, .quad) " 1111f, " catch "\n\t" \
".popsection \n\t" \
prefix "\n\t" \
"1111:"
#define ASM_TRY(catch) __ASM_TRY("", catch)
/* Forced emulation prefix, used to invoke the emulator unconditionally. */
#define KVM_FEP "ud2; .byte 'k', 'v', 'm';"
#define ASM_TRY_FEP(catch) __ASM_TRY(KVM_FEP, catch)
static inline bool is_fep_available(void)
{
/*
* Use the non-FEP ASM_TRY() as KVM will inject a #UD on the prefix
* itself if forced emulation is not available.
*/
asm goto(ASM_TRY("%l[fep_unavailable]")
KVM_FEP "nop\n\t"
::: "memory" : fep_unavailable);
return true;
fep_unavailable:
return false;
}
/*
* selector 32-bit 64-bit
* 0x00 NULL descriptor NULL descriptor
* 0x08 ring-0 code segment (32-bit) ring-0 code segment (64-bit)
* 0x10 ring-0 data segment (32-bit) ring-0 data segment (32/64-bit)
* 0x18 ring-0 code segment (P=0) ring-0 code segment (64-bit, P=0)
* 0x20 intr_alt_stack TSS ring-0 code segment (32-bit)
* 0x28 ring-0 code segment (16-bit) same
* 0x30 ring-0 data segment (16-bit) same
* 0x38 (0x3b) ring-3 code segment (32-bit) same
* 0x40 (0x43) ring-3 data segment (32-bit) ring-3 data segment (32/64-bit)
* 0x48 (0x4b) **unused** ring-3 code segment (64-bit)
* 0x50-0x78 free to use for test cases same
* 0x80-0x870 primary TSS (CPU 0..254) same
* 0x878-0x1068 percpu area (CPU 0..254) not used
*
* Note that the same segment can be used for 32-bit and 64-bit data segments
* (the L bit is only defined for code segments)
*
* Selectors 0x08-0x10 and 0x3b-0x4b are set up for use with the SYSCALL
* and SYSRET instructions.
*/
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
#define NP_SEL 0x18
#ifdef __x86_64__
#define KERNEL_CS32 0x20
#else
#define TSS_INTR 0x20
#endif
#define KERNEL_CS16 0x28
#define KERNEL_DS16 0x30
#define USER_CS32 0x3b
#define USER_DS 0x43
#ifdef __x86_64__
#define USER_CS64 0x4b
#endif
/* Synonyms */
#define KERNEL_DS32 KERNEL_DS
#define USER_DS32 USER_DS
#ifdef __x86_64__
#define KERNEL_CS64 KERNEL_CS
#define USER_CS USER_CS64
#define KERNEL_DS64 KERNEL_DS
#define USER_DS64 USER_DS
#else
#define KERNEL_CS32 KERNEL_CS
#define USER_CS USER_CS32
#endif
#define FIRST_SPARE_SEL 0x50
#define TSS_MAIN 0x80
typedef struct {
unsigned short offset0;
unsigned short selector;
unsigned short ist : 3;
unsigned short : 5;
unsigned short type : 4;
unsigned short : 1;
unsigned short dpl : 2;
unsigned short p : 1;
unsigned short offset1;
#ifdef __x86_64__
unsigned offset2;
unsigned reserved;
#endif
} idt_entry_t;
typedef struct {
uint16_t limit1;
uint16_t base1;
uint8_t base2;
union {
uint16_t type_limit_flags; /* Type and limit flags */
struct {
uint16_t type:4;
uint16_t s:1;
uint16_t dpl:2;
uint16_t p:1;
uint16_t limit2:4;
uint16_t avl:1;
uint16_t l:1;
uint16_t db:1;
uint16_t g:1;
} __attribute__((__packed__));
} __attribute__((__packed__));
uint8_t base3;
} __attribute__((__packed__)) gdt_entry_t;
#ifdef __x86_64__
struct system_desc64 {
gdt_entry_t common;
uint32_t base4;
uint32_t zero;
} __attribute__((__packed__));
#endif
#define DESC_BUSY 2
extern idt_entry_t boot_idt[256];
#ifndef __x86_64__
extern tss32_t tss[];
extern tss32_t tss_intr;
void set_gdt_task_gate(u16 tss_sel, u16 sel);
void set_idt_task_gate(int vec, u16 sel);
void set_intr_task_gate(int vec, void *fn);
void setup_tss32(void);
#else
extern tss64_t tss[];
#endif
extern gdt_entry_t gdt[];
unsigned exception_vector(void);
unsigned exception_error_code(void);
bool exception_rflags_rf(void);
void set_desc_entry(idt_entry_t *e, size_t e_sz, void *addr,
u16 sel, u16 type, u16 dpl);
void set_idt_entry(int vec, void *addr, int dpl);
void set_idt_sel(int vec, u16 sel);
void set_idt_dpl(int vec, u16 dpl);
void set_gdt_entry(int sel, unsigned long base, u32 limit, u8 access, u8 gran);
void load_gdt_tss(size_t tss_offset);
void set_intr_alt_stack(int e, void *fn);
void print_current_tss_info(void);
handler handle_exception(u8 v, handler fn);
void unhandled_exception(struct ex_regs *regs, bool cpu);
const char* exception_mnemonic(int vector);
bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
void *data);
void __set_exception_jmpbuf(jmp_buf *addr);
#define set_exception_jmpbuf(jmpbuf) \
(setjmp(jmpbuf) ? : (__set_exception_jmpbuf(&(jmpbuf)), 0))
static inline void *get_idt_addr(idt_entry_t *entry)
{
uintptr_t addr = entry->offset0 | ((u32)entry->offset1 << 16);
#ifdef __x86_64__
addr |= (u64)entry->offset2 << 32;
#endif
return (void *)addr;
}
extern gdt_entry_t *get_tss_descr(void);
extern unsigned long get_gdt_entry_base(gdt_entry_t *entry);
extern unsigned long get_gdt_entry_limit(gdt_entry_t *entry);
#define asm_safe(insn, inputs...) \
({ \
asm volatile(ASM_TRY("1f") \
insn "\n\t" \
"1:\n\t" \
: \
: inputs \
: "memory"); \
exception_vector(); \
})
#define asm_safe_out1(insn, output, inputs...) \
({ \
asm volatile(ASM_TRY("1f") \
insn "\n\t" \
"1:\n\t" \
: output \
: inputs \
: "memory"); \
exception_vector(); \
})
#define asm_safe_out2(insn, output1, output2, inputs...) \
({ \
asm volatile(ASM_TRY("1f") \
insn "\n\t" \
"1:\n\t" \
: output1, output2 \
: inputs \
: "memory"); \
exception_vector(); \
})
#define __asm_safe_report(want, insn, inputs...) \
do { \
int vector = asm_safe(insn, inputs); \
\
report(vector == want, "Expected %s on '%s', got %s", \
want ? exception_mnemonic(want) : "SUCCESS", \
insn, \
vector ? exception_mnemonic(vector) : "SUCCESS"); \
} while (0)
#define asm_safe_report(insn, inputs...) \
__asm_safe_report(0, insn, inputs)
#define asm_safe_report_ex __asm_safe_report
#endif