diff options
Diffstat (limited to 'arch/x86/include/asm/desc.h')
| -rw-r--r-- | arch/x86/include/asm/desc.h | 287 | 
1 files changed, 199 insertions, 88 deletions
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 617bd56b307..50d033a8947 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h @@ -4,38 +4,45 @@  #include <asm/desc_defs.h>  #include <asm/ldt.h>  #include <asm/mmu.h> +  #include <linux/smp.h> +#include <linux/percpu.h> + +static inline void fill_ldt(struct desc_struct *desc, const struct user_desc *info) +{ +	desc->limit0		= info->limit & 0x0ffff; + +	desc->base0		= (info->base_addr & 0x0000ffff); +	desc->base1		= (info->base_addr & 0x00ff0000) >> 16; + +	desc->type		= (info->read_exec_only ^ 1) << 1; +	desc->type	       |= info->contents << 2; + +	desc->s			= 1; +	desc->dpl		= 0x3; +	desc->p			= info->seg_not_present ^ 1; +	desc->limit		= (info->limit & 0xf0000) >> 16; +	desc->avl		= info->useable; +	desc->d			= info->seg_32bit; +	desc->g			= info->limit_in_pages; -static inline void fill_ldt(struct desc_struct *desc, -			    const struct user_desc *info) -{ -	desc->limit0 = info->limit & 0x0ffff; -	desc->base0 = info->base_addr & 0x0000ffff; - -	desc->base1 = (info->base_addr & 0x00ff0000) >> 16; -	desc->type = (info->read_exec_only ^ 1) << 1; -	desc->type |= info->contents << 2; -	desc->s = 1; -	desc->dpl = 0x3; -	desc->p = info->seg_not_present ^ 1; -	desc->limit = (info->limit & 0xf0000) >> 16; -	desc->avl = info->useable; -	desc->d = info->seg_32bit; -	desc->g = info->limit_in_pages; -	desc->base2 = (info->base_addr & 0xff000000) >> 24; +	desc->base2		= (info->base_addr & 0xff000000) >> 24;  	/* -	 * Don't allow setting of the lm bit. It is useless anyway -	 * because 64bit system calls require __USER_CS: +	 * Don't allow setting of the lm bit. It would confuse +	 * user_64bit_mode and would get overridden by sysret anyway.  	 */ -	desc->l = 0; +	desc->l			= 0;  }  extern struct desc_ptr idt_descr;  extern gate_desc idt_table[]; +extern struct desc_ptr debug_idt_descr; +extern gate_desc debug_idt_table[];  struct gdt_page {  	struct desc_struct gdt[GDT_ENTRIES];  } __attribute__((aligned(PAGE_SIZE))); +  DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);  static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu) @@ -48,16 +55,16 @@ static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)  static inline void pack_gate(gate_desc *gate, unsigned type, unsigned long func,  			     unsigned dpl, unsigned ist, unsigned seg)  { -	gate->offset_low = PTR_LOW(func); -	gate->segment = __KERNEL_CS; -	gate->ist = ist; -	gate->p = 1; -	gate->dpl = dpl; -	gate->zero0 = 0; -	gate->zero1 = 0; -	gate->type = type; -	gate->offset_middle = PTR_MIDDLE(func); -	gate->offset_high = PTR_HIGH(func); +	gate->offset_low	= PTR_LOW(func); +	gate->segment		= __KERNEL_CS; +	gate->ist		= ist; +	gate->p			= 1; +	gate->dpl		= dpl; +	gate->zero0		= 0; +	gate->zero1		= 0; +	gate->type		= type; +	gate->offset_middle	= PTR_MIDDLE(func); +	gate->offset_high	= PTR_HIGH(func);  }  #else @@ -66,8 +73,7 @@ static inline void pack_gate(gate_desc *gate, unsigned char type,  			     unsigned short seg)  {  	gate->a = (seg << 16) | (base & 0xffff); -	gate->b = (base & 0xffff0000) | -		  (((0x80 | type | (dpl << 5)) & 0xff) << 8); +	gate->b = (base & 0xffff0000) | (((0x80 | type | (dpl << 5)) & 0xff) << 8);  }  #endif @@ -75,31 +81,29 @@ static inline void pack_gate(gate_desc *gate, unsigned char type,  static inline int desc_empty(const void *ptr)  {  	const u32 *desc = ptr; +  	return !(desc[0] | desc[1]);  }  #ifdef CONFIG_PARAVIRT  #include <asm/paravirt.h>  #else -#define load_TR_desc() native_load_tr_desc() -#define load_gdt(dtr) native_load_gdt(dtr) -#define load_idt(dtr) native_load_idt(dtr) -#define load_tr(tr) asm volatile("ltr %0"::"m" (tr)) -#define load_ldt(ldt) asm volatile("lldt %0"::"m" (ldt)) - -#define store_gdt(dtr) native_store_gdt(dtr) -#define store_idt(dtr) native_store_idt(dtr) -#define store_tr(tr) (tr = native_store_tr()) - -#define load_TLS(t, cpu) native_load_tls(t, cpu) -#define set_ldt native_set_ldt - -#define write_ldt_entry(dt, entry, desc)	\ -	native_write_ldt_entry(dt, entry, desc) -#define write_gdt_entry(dt, entry, desc, type)		\ -	native_write_gdt_entry(dt, entry, desc, type) -#define write_idt_entry(dt, entry, g)		\ -	native_write_idt_entry(dt, entry, g) +#define load_TR_desc()				native_load_tr_desc() +#define load_gdt(dtr)				native_load_gdt(dtr) +#define load_idt(dtr)				native_load_idt(dtr) +#define load_tr(tr)				asm volatile("ltr %0"::"m" (tr)) +#define load_ldt(ldt)				asm volatile("lldt %0"::"m" (ldt)) + +#define store_gdt(dtr)				native_store_gdt(dtr) +#define store_idt(dtr)				native_store_idt(dtr) +#define store_tr(tr)				(tr = native_store_tr()) + +#define load_TLS(t, cpu)			native_load_tls(t, cpu) +#define set_ldt					native_set_ldt + +#define write_ldt_entry(dt, entry, desc)	native_write_ldt_entry(dt, entry, desc) +#define write_gdt_entry(dt, entry, desc, type)	native_write_gdt_entry(dt, entry, desc, type) +#define write_idt_entry(dt, entry, g)		native_write_idt_entry(dt, entry, g)  static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries)  { @@ -112,33 +116,27 @@ static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries)  #define store_ldt(ldt) asm("sldt %0" : "=m"(ldt)) -static inline void native_write_idt_entry(gate_desc *idt, int entry, -					  const gate_desc *gate) +static inline void native_write_idt_entry(gate_desc *idt, int entry, const gate_desc *gate)  {  	memcpy(&idt[entry], gate, sizeof(*gate));  } -static inline void native_write_ldt_entry(struct desc_struct *ldt, int entry, -					  const void *desc) +static inline void native_write_ldt_entry(struct desc_struct *ldt, int entry, const void *desc)  {  	memcpy(&ldt[entry], desc, 8);  } -static inline void native_write_gdt_entry(struct desc_struct *gdt, int entry, -					  const void *desc, int type) +static inline void +native_write_gdt_entry(struct desc_struct *gdt, int entry, const void *desc, int type)  {  	unsigned int size; +  	switch (type) { -	case DESC_TSS: -		size = sizeof(tss_desc); -		break; -	case DESC_LDT: -		size = sizeof(ldt_desc); -		break; -	default: -		size = sizeof(struct desc_struct); -		break; +	case DESC_TSS:	size = sizeof(tss_desc);	break; +	case DESC_LDT:	size = sizeof(ldt_desc);	break; +	default:	size = sizeof(*gdt);		break;  	} +  	memcpy(&gdt[entry], desc, size);  } @@ -154,20 +152,21 @@ static inline void pack_descriptor(struct desc_struct *desc, unsigned long base,  } -static inline void set_tssldt_descriptor(void *d, unsigned long addr, -					 unsigned type, unsigned size) +static inline void set_tssldt_descriptor(void *d, unsigned long addr, unsigned type, unsigned size)  {  #ifdef CONFIG_X86_64  	struct ldttss_desc64 *desc = d; +  	memset(desc, 0, sizeof(*desc)); -	desc->limit0 = size & 0xFFFF; -	desc->base0 = PTR_LOW(addr); -	desc->base1 = PTR_MIDDLE(addr) & 0xFF; -	desc->type = type; -	desc->p = 1; -	desc->limit1 = (size >> 16) & 0xF; -	desc->base2 = (PTR_MIDDLE(addr) >> 8) & 0xFF; -	desc->base3 = PTR_HIGH(addr); + +	desc->limit0		= size & 0xFFFF; +	desc->base0		= PTR_LOW(addr); +	desc->base1		= PTR_MIDDLE(addr) & 0xFF; +	desc->type		= type; +	desc->p			= 1; +	desc->limit1		= (size >> 16) & 0xF; +	desc->base2		= (PTR_MIDDLE(addr) >> 8) & 0xFF; +	desc->base3		= PTR_HIGH(addr);  #else  	pack_descriptor((struct desc_struct *)d, addr, size, 0x80 | type, 0);  #endif @@ -237,14 +236,16 @@ static inline void native_store_idt(struct desc_ptr *dtr)  static inline unsigned long native_store_tr(void)  {  	unsigned long tr; +  	asm volatile("str %0":"=r" (tr)); +  	return tr;  }  static inline void native_load_tls(struct thread_struct *t, unsigned int cpu)  { -	unsigned int i;  	struct desc_struct *gdt = get_cpu_gdt_table(cpu); +	unsigned int i;  	for (i = 0; i < GDT_ENTRY_TLS_ENTRIES; i++)  		gdt[GDT_ENTRY_TLS_MIN + i] = t->tls_array[i]; @@ -309,16 +310,56 @@ static inline void set_desc_limit(struct desc_struct *desc, unsigned long limit)  	desc->limit = (limit >> 16) & 0xf;  } +#ifdef CONFIG_X86_64 +static inline void set_nmi_gate(int gate, void *addr) +{ +	gate_desc s; + +	pack_gate(&s, GATE_INTERRUPT, (unsigned long)addr, 0, 0, __KERNEL_CS); +	write_idt_entry(debug_idt_table, gate, &s); +} +#endif + +#ifdef CONFIG_TRACING +extern struct desc_ptr trace_idt_descr; +extern gate_desc trace_idt_table[]; +static inline void write_trace_idt_entry(int entry, const gate_desc *gate) +{ +	write_idt_entry(trace_idt_table, entry, gate); +} + +static inline void _trace_set_gate(int gate, unsigned type, void *addr, +				   unsigned dpl, unsigned ist, unsigned seg) +{ +	gate_desc s; + +	pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg); +	/* +	 * does not need to be atomic because it is only done once at +	 * setup time +	 */ +	write_trace_idt_entry(gate, &s); +} +#else +static inline void write_trace_idt_entry(int entry, const gate_desc *gate) +{ +} + +#define _trace_set_gate(gate, type, addr, dpl, ist, seg) +#endif +  static inline void _set_gate(int gate, unsigned type, void *addr,  			     unsigned dpl, unsigned ist, unsigned seg)  {  	gate_desc s; +  	pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);  	/*  	 * does not need to be atomic because it is only done once at  	 * setup time  	 */  	write_idt_entry(idt_table, gate, &s); +	write_trace_idt_entry(gate, &s);  }  /* @@ -327,11 +368,14 @@ static inline void _set_gate(int gate, unsigned type, void *addr,   * Pentium F0 0F bugfix can have resulted in the mapped   * IDT being write-protected.   */ -static inline void set_intr_gate(unsigned int n, void *addr) -{ -	BUG_ON((unsigned)n > 0xFF); -	_set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS); -} +#define set_intr_gate(n, addr)						\ +	do {								\ +		BUG_ON((unsigned)n > 0xFF);				\ +		_set_gate(n, GATE_INTERRUPT, (void *)addr, 0, 0,	\ +			  __KERNEL_CS);					\ +		_trace_set_gate(n, GATE_INTERRUPT, (void *)trace_##addr,\ +				0, 0, __KERNEL_CS);			\ +	} while (0)  extern int first_system_vector;  /* used_vectors is BITMAP for irq is not managed by percpu vector_irq */ @@ -343,15 +387,16 @@ static inline void alloc_system_vector(int vector)  		set_bit(vector, used_vectors);  		if (first_system_vector > vector)  			first_system_vector = vector; -	} else +	} else {  		BUG(); +	}  } -static inline void alloc_intr_gate(unsigned int n, void *addr) -{ -	alloc_system_vector(n); -	set_intr_gate(n, addr); -} +#define alloc_intr_gate(n, addr)				\ +	do {							\ +		alloc_system_vector(n);				\ +		set_intr_gate(n, addr);				\ +	} while (0)  /*   * This routine sets up an interrupt gate at directory privilege level 3. @@ -392,4 +437,70 @@ static inline void set_system_intr_gate_ist(int n, void *addr, unsigned ist)  	_set_gate(n, GATE_INTERRUPT, addr, 0x3, ist, __KERNEL_CS);  } +#ifdef CONFIG_X86_64 +DECLARE_PER_CPU(u32, debug_idt_ctr); +static inline bool is_debug_idt_enabled(void) +{ +	if (this_cpu_read(debug_idt_ctr)) +		return true; + +	return false; +} + +static inline void load_debug_idt(void) +{ +	load_idt((const struct desc_ptr *)&debug_idt_descr); +} +#else +static inline bool is_debug_idt_enabled(void) +{ +	return false; +} + +static inline void load_debug_idt(void) +{ +} +#endif + +#ifdef CONFIG_TRACING +extern atomic_t trace_idt_ctr; +static inline bool is_trace_idt_enabled(void) +{ +	if (atomic_read(&trace_idt_ctr)) +		return true; + +	return false; +} + +static inline void load_trace_idt(void) +{ +	load_idt((const struct desc_ptr *)&trace_idt_descr); +} +#else +static inline bool is_trace_idt_enabled(void) +{ +	return false; +} + +static inline void load_trace_idt(void) +{ +} +#endif + +/* + * The load_current_idt() must be called with interrupts disabled + * to avoid races. That way the IDT will always be set back to the expected + * descriptor. It's also called when a CPU is being initialized, and + * that doesn't need to disable interrupts, as nothing should be + * bothering the CPU then. + */ +static inline void load_current_idt(void) +{ +	if (is_debug_idt_enabled()) +		load_debug_idt(); +	else if (is_trace_idt_enabled()) +		load_trace_idt(); +	else +		load_idt((const struct desc_ptr *)&idt_descr); +}  #endif /* _ASM_X86_DESC_H */  | 
