#ifndef __ARCH_DESC_H
#define __ARCH_DESC_H

#include <asm/ldt.h>

/*
 * The layout of the GDT under Linux:
 *
 *   0 - null
 *   1 - not used
 *   2 - kernel code segment
 *   3 - kernel data segment
 *   4 - user code segment                  <-- new cacheline 
 *   5 - user data segment
 *   6 - not used
 *   7 - not used
 *   8 - APM BIOS support                   <-- new cacheline 
 *   9 - APM BIOS support
 *  10 - APM BIOS support
 *  11 - APM BIOS support
 *
 * The TSS+LDT descriptors are spread out a bit so that every CPU
 * has an exclusive cacheline for the per-CPU TSS and LDT:
 *
 *  12 - CPU#0 TSS                          <-- new cacheline 
 *  13 - CPU#0 LDT
 *  14 - not used 
 *  15 - not used 
 *  .. - not used
 *  28 - CPU#1 TSS                          <-- new cacheline 
 *  29 - CPU#1 LDT
 *  30 - not used
 *  31 - not used
 *  ... NR_CPUS per-CPU TSS+LDT's if on SMP
 *
 * Entry into gdt where to find first TSS.
 */
#define __FIRST_TSS_ENTRY 12
#define __FIRST_LDT_ENTRY (__FIRST_TSS_ENTRY+1)

#define __TSS(n) (((n)<<4) + __FIRST_TSS_ENTRY)
#define __LDT(n) (((n)<<4) + __FIRST_LDT_ENTRY)

#ifndef __ASSEMBLY__
struct desc_struct {
	unsigned long a,b;
};

extern struct desc_struct gdt_table[];
extern struct desc_struct gdt_table2[];
extern struct desc_struct *idt, *gdt, *gdt2;

struct Xgt_desc_struct {
	unsigned short size;
	unsigned long address __attribute__((packed));
};

#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
#define gdt_descr2 (*(struct Xgt_desc_struct *)((char *)&gdt2 - 2))

#define load_TR(n) __asm__ __volatile__("ltr %%ax"::"a" (__TSS(n)<<3))

#define __load_LDT(n) __asm__ __volatile__("lldt %%ax"::"a" (__LDT(n)<<3))

/*
 * This is the ldt that every process will get unless we need
 * something other than this.
 */
extern const struct desc_struct default_ldt[];
extern void set_intr_gate(unsigned int irq, void * addr);
extern void set_ldt_desc(unsigned int n, const void *addr, unsigned int size);
extern void __set_ldt_desc(unsigned int n, const void *addr, unsigned int size);
extern void set_tss_desc(unsigned int n, const void *addr);

static inline void clear_LDT(void)
{
	int cpu;
	preempt_disable();
	cpu = smp_processor_id();
	set_ldt_desc(cpu, &default_ldt[0], 5);
	__load_LDT(cpu);
	preempt_enable();
}

/*
 * load one particular LDT into the current CPU
 */
static inline void load_LDT (struct mm_struct *mm)
{
	int cpu = smp_processor_id();
	const void *segments = mm->context.segments;
	int count = LDT_ENTRIES;

	if (!segments) {
		segments = &default_ldt[0];
		count = 5;
	}
		
	set_ldt_desc(cpu, segments, count);
	__load_LDT(cpu);
}

static inline void _load_LDT (struct mm_struct *mm)
{
	int cpu = smp_processor_id();
	const void *segments = mm->context.segments;
	int count = LDT_ENTRIES;

	if (!segments) {
		segments = &default_ldt[0];
		count = 5;
	}
		
	__set_ldt_desc(cpu, segments, count);
	__load_LDT(cpu);
}

#define pax_open_kernel(flags, cr3)		\
do {						\
	local_irq_save(flags);			\
	asm("movl %%cr3,%0":"=r" (cr3));	\
	load_cr3(kernexec_pg_dir);		\
} while(0)

#define pax_close_kernel(flags, cr3)		\
do {						\
	asm("movl %0,%%cr3": :"r" (cr3));	\
	local_irq_restore(flags);		\
} while(0)

#endif /* !__ASSEMBLY__ */

#endif
