x86: Add support for (paging-based) protection domains
This patch implements a simple, lightweight form of protection domains using a pluggable framework. Currently, the following plugin is available: - Flat memory model with paging. The overall goal of a protection domain implementation within this framework is to define a set of resources that should be accessible to each protection domain and to prevent that protection domain from accessing other resources. The details of each implementation of protection domains may differ substantially, but they should all be guided by the principle of least privilege. However, that idealized principle is balanced against the practical objectives of limiting the number of relatively time-consuming context switches and minimizing changes to existing code. For additional information, please refer to cpu/x86/mm/README.md. This patch also causes the C compiler to be used as the default linker and assembler.
This commit is contained in:
parent
b0de416682
commit
3908253038
48 changed files with 3558 additions and 295 deletions
|
@ -28,11 +28,13 @@
|
|||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "gdt.h"
|
||||
#include "helpers.h"
|
||||
#include "idt.h"
|
||||
#include "interrupt.h"
|
||||
#include "irq.h"
|
||||
#include "stacks.h"
|
||||
|
||||
static void
|
||||
double_fault_handler(struct interrupt_context context)
|
||||
|
@ -40,16 +42,79 @@ double_fault_handler(struct interrupt_context context)
|
|||
halt();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
cpu_init(void)
|
||||
/* The OS has switched to its own segment descriptors. However, the protection
|
||||
* domain support, if enabled, has not yet been fully activated.
|
||||
*/
|
||||
static void
|
||||
boot_stage1(void)
|
||||
{
|
||||
gdt_init();
|
||||
idt_init();
|
||||
|
||||
/* Set an interrupt handler for Double Fault exception. This way, we avoid
|
||||
* the system to triple fault, leaving no trace about what happened.
|
||||
*/
|
||||
SET_INTERRUPT_HANDLER(8, 1, double_fault_handler);
|
||||
SET_EXCEPTION_HANDLER(8, 1, double_fault_handler);
|
||||
|
||||
irq_init();
|
||||
/* Initialize protection domain support, if enabled */
|
||||
prot_domains_init();
|
||||
|
||||
prot_domains_leave_boot_stage1();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int main(void);
|
||||
/* This routine runs with the initial, flat address space, even if protection
|
||||
* domain support is enabled. The goal behind the design of this routine is to
|
||||
* keep it as short as possible, since it is unable to directly reference data
|
||||
* and invoke functions that are intended to be accessible later after the
|
||||
* system has booted when a multi-segment protection domain model is in use.
|
||||
*/
|
||||
void
|
||||
cpu_boot_stage0(void)
|
||||
{
|
||||
/* Reserve three stack slots for return addresses */
|
||||
uintptr_t top_of_stack = STACKS_INIT_TOP;
|
||||
|
||||
#if X86_CONF_PROT_DOMAINS != X86_CONF_PROT_DOMAINS__NONE
|
||||
uintptr_t *top_of_stack_ptr = (uintptr_t *)top_of_stack;
|
||||
|
||||
top_of_stack_ptr[0] = (uintptr_t)prot_domains_launch_kernel;
|
||||
top_of_stack_ptr[1] = (uintptr_t)prot_domains_launch_app;
|
||||
#endif
|
||||
|
||||
/* Perform common GDT initialization */
|
||||
gdt_init();
|
||||
|
||||
/* Switch all data segment registers to the newly-initialized flat data
|
||||
* descriptor.
|
||||
*/
|
||||
__asm__(
|
||||
"mov %0, %%ds\n\t"
|
||||
"mov %0, %%es\n\t"
|
||||
"mov %0, %%fs\n\t"
|
||||
"mov %0, %%gs\n\t"
|
||||
:
|
||||
: "r" (GDT_SEL_DATA_FLAT)
|
||||
);
|
||||
|
||||
/**
|
||||
* Perform specific GDT initialization tasks for the protection domain
|
||||
* implementation that is enabled, if any.
|
||||
*/
|
||||
prot_domains_gdt_init();
|
||||
|
||||
/* Do not pass memory operands to the asm block below, since it is
|
||||
* switching from the flat address space to a multi-segment address space
|
||||
* model if such a model is used by the enabled protection domain
|
||||
* implementation, if any.
|
||||
*/
|
||||
__asm__(
|
||||
"mov %[_ss_], %%ss\n\t"
|
||||
"mov %[_esp_], %%esp\n\t"
|
||||
"ljmp %[_cs_], %[_stage1_]\n\t"
|
||||
:
|
||||
: [_ss_] "r" (GDT_SEL_STK_EXC),
|
||||
[_esp_] "r" (top_of_stack),
|
||||
[_cs_] "i" ((uint16_t)GDT_SEL_CODE_EXC),
|
||||
[_stage1_] "i" (boot_stage1)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
void cpu_init(void);
|
||||
#include "prot-domains.h"
|
||||
|
||||
void cpu_boot_stage0(void) ATTR_CODE_BOOT;
|
||||
|
||||
#endif /* CPU_H */
|
||||
|
|
|
@ -29,45 +29,15 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "gdt.h"
|
||||
#include "gdt-layout.h"
|
||||
#include "helpers.h"
|
||||
#include "prot-domains.h"
|
||||
#include "segmentation.h"
|
||||
|
||||
#define NUM_DESC 3
|
||||
|
||||
#define GDT_IDX_NULL 0
|
||||
#define GDT_IDX_CODE 1
|
||||
#define GDT_IDX_DATA 2
|
||||
|
||||
/* All code in the x86 port of Contiki runs at ring (privilege) level 0 */
|
||||
#define PRIV_LVL 0
|
||||
|
||||
/* Compute GDT selector from descriptor index and requested privilege level */
|
||||
#define GDT_SEL(IDX, RPL) (((IDX) << 3) | (RPL))
|
||||
|
||||
#define GDT_SEL_NULL GDT_SEL(GDT_IDX_NULL, 0)
|
||||
#define GDT_SEL_CODE GDT_SEL(GDT_IDX_CODE, PRIV_LVL)
|
||||
#define GDT_SEL_DATA GDT_SEL(GDT_IDX_DATA, PRIV_LVL)
|
||||
|
||||
/* Each define here is for a specific flag in the descriptor. Refer to Intel
|
||||
* Combined Manual (Intel 64 and IA-32 Architectures Software Developer's
|
||||
* Manual), Vol. 3, Section 3.4.5 for a description of each flag.
|
||||
*/
|
||||
#define SEG_DESCTYPE(x) ((x) << 0x04) /* Descriptor type (0 for system, 1 for code/data) */
|
||||
#define SEG_PRES(x) ((x) << 0x07) /* Present */
|
||||
#define SEG_SAVL(x) ((x) << 0x0C) /* Available for system use */
|
||||
#define SEG_LONG(x) ((x) << 0x0D) /* Long mode */
|
||||
#define SEG_SIZE(x) ((x) << 0x0E) /* Size (0 for 16-bit, 1 for 32) */
|
||||
#define SEG_GRAN(x) ((x) << 0x0F) /* Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB) */
|
||||
#define SEG_PRIV(x) (((x) & 0x03) << 0x05) /* Set privilege level (0 - 3) */
|
||||
|
||||
#define SEG_DATA_RDWR 0x02 /* Read/Write */
|
||||
#define SEG_CODE_EXRD 0x0A /* Execute/Read */
|
||||
|
||||
#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
|
||||
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
|
||||
SEG_PRIV(0) | SEG_CODE_EXRD
|
||||
|
||||
#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
|
||||
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
|
||||
SEG_PRIV(0) | SEG_DATA_RDWR
|
||||
#define GDT_MEM_PL0 (SEG_DESCTYPE_NSYS | SEG_GRAN_PAGE)
|
||||
#define GDT_CODE_PL0 (GDT_MEM_PL0 | SEG_TYPE_CODE_EXRD)
|
||||
#define GDT_DATA_PL0 (GDT_MEM_PL0 | SEG_TYPE_DATA_RDWR)
|
||||
|
||||
typedef struct gdtr
|
||||
{
|
||||
|
@ -75,41 +45,53 @@ typedef struct gdtr
|
|||
uint32_t base;
|
||||
} __attribute__((packed)) gdtr_t;
|
||||
|
||||
typedef uint64_t segment_desc_t;
|
||||
|
||||
/* From Intel Combined Manual, Vol. 3 , Section 3.5.1: The base addresses of
|
||||
* the GDT should be aligned on an eight-byte boundary to yield the best
|
||||
* processor performance.
|
||||
*/
|
||||
static segment_desc_t gdt[NUM_DESC] __attribute__ ((aligned (8)));
|
||||
segment_desc_t __attribute__ ((aligned(8))) ATTR_BSS_GDT_START
|
||||
gdt[GDT_NUM_FIXED_DESC];
|
||||
|
||||
static void
|
||||
set_descriptor(unsigned int index, uint32_t base, uint32_t limit, uint16_t flag)
|
||||
#define GDT_LEN \
|
||||
((((uintptr_t)&_ebss_gdt_addr) - \
|
||||
(uintptr_t)gdt)/sizeof(segment_desc_t))
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void ATTR_CODE_BOOT
|
||||
set_descriptor(unsigned int index,
|
||||
uint32_t base,
|
||||
uint32_t len,
|
||||
uint16_t flag)
|
||||
{
|
||||
segment_desc_t descriptor;
|
||||
|
||||
if (index >= NUM_DESC)
|
||||
return;
|
||||
if(GDT_LEN <= index) {
|
||||
halt();
|
||||
}
|
||||
|
||||
/* Create the high 32 bit segment */
|
||||
descriptor = limit & 0x000F0000; /* set limit bits 19:16 */
|
||||
descriptor |= (flag << 8) & 0x00F0FF00; /* set type, p, dpl, s, g, d/b, l and avl fields */
|
||||
descriptor |= (base >> 16) & 0x000000FF; /* set base bits 23:16 */
|
||||
descriptor |= base & 0xFF000000; /* set base bits 31:24 */
|
||||
|
||||
/* Shift by 32 to allow for low part of segment */
|
||||
descriptor <<= 32;
|
||||
|
||||
/* Create the low 32 bit segment */
|
||||
descriptor |= base << 16; /* set base bits 15:0 */
|
||||
descriptor |= limit & 0x0000FFFF; /* set limit bits 15:0 */
|
||||
segment_desc_init(&descriptor, base, len, flag);
|
||||
|
||||
/* Save descriptor into gdt */
|
||||
gdt[index] = descriptor;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
gdt_copy_desc_change_dpl(unsigned int dest_idx,
|
||||
unsigned int src_idx,
|
||||
unsigned dpl)
|
||||
{
|
||||
segment_desc_t desc;
|
||||
|
||||
if((GDT_LEN <= dest_idx) || (GDT_LEN <= src_idx)) {
|
||||
halt();
|
||||
}
|
||||
|
||||
/* This function initializes the Global Offset Table. For simplicity, the
|
||||
desc = gdt[src_idx];
|
||||
SEG_SET_FLAG(desc, DPL, dpl);
|
||||
gdt[dest_idx] = desc;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* This function initializes the Global Descriptor Table. For simplicity, the
|
||||
* memory is organized following the flat model. Thus, memory appears to
|
||||
* Contiki as a single continuous address space. Code, data, and stack
|
||||
* are all contained in this address space (so called linear address space).
|
||||
|
@ -120,29 +102,35 @@ gdt_init(void)
|
|||
gdtr_t gdtr;
|
||||
|
||||
/* Initialize gdtr structure */
|
||||
gdtr.limit = sizeof(segment_desc_t) * NUM_DESC - 1;
|
||||
gdtr.limit = sizeof(segment_desc_t) * GDT_LEN - 1;
|
||||
gdtr.base = (uint32_t) &gdt;
|
||||
|
||||
/* Initialize descriptors */
|
||||
set_descriptor(GDT_IDX_NULL, 0, 0, 0);
|
||||
set_descriptor(GDT_IDX_CODE, 0, 0x0FFFFF, GDT_CODE_PL0);
|
||||
set_descriptor(GDT_IDX_DATA, 0, 0x0FFFFF, GDT_DATA_PL0);
|
||||
set_descriptor(GDT_IDX_CODE_FLAT, 0, 0x100000, GDT_CODE_PL0);
|
||||
set_descriptor(GDT_IDX_DATA_FLAT, 0, 0x100000, GDT_DATA_PL0);
|
||||
|
||||
/* Load GDTR register and update segment registers.
|
||||
*
|
||||
* CS register cannot be changed directly. For that reason, we do a far jump.
|
||||
*/
|
||||
__asm__ ("lgdt %[_gdtr_]\n\t"
|
||||
"jmp %[_cs_], $1f\n\t"
|
||||
"1:\n\t"
|
||||
"mov %[_ds_], %%ds\n\t"
|
||||
"mov %[_ds_], %%ss\n\t"
|
||||
"mov %[_ds_], %%es\n\t"
|
||||
"mov %[_ds_], %%fs\n\t"
|
||||
"mov %[_ds_], %%gs\n\t"
|
||||
:
|
||||
: [_gdtr_] "m" (gdtr),
|
||||
[_cs_] "i" (GDT_SEL_CODE),
|
||||
[_ds_] "r" (GDT_SEL_DATA)
|
||||
);
|
||||
/* Load GDTR */
|
||||
__asm__ __volatile__ ("lgdt %0" :: "m" (gdtr));
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
gdt_insert(unsigned int idx, segment_desc_t desc)
|
||||
{
|
||||
if(GDT_LEN <= idx) {
|
||||
halt();
|
||||
}
|
||||
|
||||
gdt[idx] = desc;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
gdt_lookup(unsigned int idx, segment_desc_t *desc)
|
||||
{
|
||||
if((GDT_LEN <= idx) || (desc == NULL)) {
|
||||
halt();
|
||||
}
|
||||
|
||||
*desc = gdt[idx];
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
||||
* Copyright (C) 2015-2016, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -31,6 +31,32 @@
|
|||
#ifndef GDT_H
|
||||
#define GDT_H
|
||||
|
||||
void gdt_init(void);
|
||||
#include "gdt-layout.h"
|
||||
#include "prot-domains.h"
|
||||
#include "segmentation.h"
|
||||
|
||||
extern segment_desc_t gdt[];
|
||||
extern int _ebss_gdt_addr;
|
||||
|
||||
#define GDT_IDX_OF_DESC(ptr) \
|
||||
((((uintptr_t)(ptr)) - ((uintptr_t)&gdt))/ \
|
||||
sizeof(segment_desc_t))
|
||||
|
||||
/**
|
||||
* \brief Compute the selector for a GDT entry allocated somewhere besides gdt.c.
|
||||
* \param ptr Pointer to GDT descriptor.
|
||||
* \param rpl Requested Privilege Level.
|
||||
*/
|
||||
#define GDT_SEL_OF_DESC(ptr, rpl) GDT_SEL(GDT_IDX_OF_DESC(ptr), rpl)
|
||||
|
||||
#define ATTR_BSS_GDT __attribute__((section(".gdt_bss")))
|
||||
#define ATTR_BSS_GDT_START __attribute__((section(".gdt_bss_start")))
|
||||
|
||||
void gdt_copy_desc_change_dpl(unsigned int dest_idx,
|
||||
unsigned int src_idx,
|
||||
unsigned dpl);
|
||||
void gdt_init(void) ATTR_CODE_BOOT;
|
||||
void gdt_insert(unsigned int idx, segment_desc_t desc);
|
||||
void gdt_lookup(unsigned int idx, segment_desc_t *desc);
|
||||
|
||||
#endif /* GDT_H */
|
||||
|
|
|
@ -28,9 +28,13 @@
|
|||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "gdt-layout.h"
|
||||
#include "prot-domains.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "segmentation.h"
|
||||
#include "idt.h"
|
||||
|
||||
#define NUM_DESC 256
|
||||
|
||||
|
@ -55,22 +59,27 @@ typedef struct intr_gate_desc {
|
|||
* of the IDT should be aligned on an 8-byte boundary to maximize performance
|
||||
* of cache line fills.
|
||||
*/
|
||||
static intr_gate_desc_t idt[NUM_DESC] __attribute__ ((aligned(8)));
|
||||
static intr_gate_desc_t __attribute__((aligned(8))) ATTR_BSS_KERN
|
||||
idt[NUM_DESC];
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* XXX: If you change this function prototype, make sure you fix the assembly
|
||||
* code in SET_INTERRUPT_HANDLER macro in interrupt.h. Otherwise, you might
|
||||
* code in SET_INT_EXC_HANDLER macro in interrupt.h. Otherwise, you might
|
||||
* face a very-hard-to-find bug in the interrupt handling system.
|
||||
*/
|
||||
void
|
||||
idt_set_intr_gate_desc(int intr_num, uint32_t offset)
|
||||
idt_set_intr_gate_desc(int intr_num,
|
||||
uint32_t offset,
|
||||
uint16_t cs,
|
||||
uint16_t dpl)
|
||||
{
|
||||
intr_gate_desc_t *desc = &idt[intr_num];
|
||||
|
||||
desc->offset_low = offset & 0xFFFF;
|
||||
desc->selector = 0x08; /* Offset in GDT for code segment */
|
||||
desc->selector = cs;
|
||||
desc->fixed = BIT(9) | BIT(10);
|
||||
desc->d = 1;
|
||||
desc->dpl = 0;
|
||||
desc->dpl = dpl;
|
||||
desc->p = 1;
|
||||
desc->offset_high = (offset >> 16) & 0xFFFF;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,12 @@
|
|||
#define IDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "prot-domains.h"
|
||||
|
||||
void idt_init(void);
|
||||
void idt_set_intr_gate_desc(int intr_num, uint32_t offset);
|
||||
void idt_init(void) ATTR_CODE_BOOT;
|
||||
void idt_set_intr_gate_desc(int intr_num,
|
||||
uint32_t offset,
|
||||
uint16_t cs,
|
||||
uint16_t dpl);
|
||||
|
||||
#endif /* IDT_H */
|
||||
|
|
|
@ -32,10 +32,17 @@
|
|||
#define INTERRUPT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "gdt-layout.h"
|
||||
|
||||
#include "idt.h"
|
||||
|
||||
struct interrupt_context {
|
||||
/* The general-purpose register values are saved by the pushal instruction in
|
||||
* the interrupt dispatcher. Having access to these saved values may be
|
||||
* useful in some future interrupt or exception handler, and saving and later
|
||||
* restoring them also enables the ISR to freely overwrite the EAX, ECX, and
|
||||
* EDX registers as is permitted by the cdecl calling convention.
|
||||
*/
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t ebp;
|
||||
|
@ -44,16 +51,28 @@ struct interrupt_context {
|
|||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t eax;
|
||||
/* These two values are pushed on the stack by the CPU when it delivers an
|
||||
* exception with an associated error code. Currently, only the double fault
|
||||
* handler accepts this structure as a parameter, and that type of exception
|
||||
* does have an associated error code.
|
||||
*/
|
||||
uint32_t error_code;
|
||||
uint32_t eip;
|
||||
/* The CPU pushes additional values beyond these on the stack, specifically
|
||||
* the code segment descriptor and flags. If a privilege-level change occurs
|
||||
* during delivery, the CPU additionally pushes the stack pointer and stack
|
||||
* segment descriptor.
|
||||
*/
|
||||
};
|
||||
|
||||
#define ISR_STUB(label_str, has_error_code, handler_str) \
|
||||
#define ISR_STUB(label_str, has_error_code, handler_str, exc) \
|
||||
"jmp 2f\n\t" \
|
||||
".align 4\n\t" \
|
||||
label_str ":\n\t" \
|
||||
" pushal\n\t" \
|
||||
PROT_DOMAINS_ENTER_ISR(exc) \
|
||||
" call " handler_str "\n\t" \
|
||||
PROT_DOMAINS_LEAVE_ISR(exc) \
|
||||
" popal\n\t" \
|
||||
" .if " #has_error_code "\n\t" \
|
||||
" add $4, %%esp\n\t" \
|
||||
|
@ -72,6 +91,14 @@ struct interrupt_context {
|
|||
* void handler(void)
|
||||
* Otherwise, it should be:
|
||||
* void handler(struct interrupt_context context)
|
||||
* exc: 0 if this is an interrupt, which should be handled
|
||||
* at the interrupt privilege level. 1 if this is an
|
||||
* exception, which should be handled at the
|
||||
* exception privilege level.
|
||||
* dpl: Privilege level for IDT descriptor, which is the
|
||||
* numerically-highest privilege level that can
|
||||
* generate this interrupt with a software interrupt
|
||||
* instruction.
|
||||
*
|
||||
* Since there is no easy way to write an Interrupt Service Routines
|
||||
* (ISR) in C (for further information on this, see [1]), we provide
|
||||
|
@ -81,18 +108,30 @@ struct interrupt_context {
|
|||
*
|
||||
* [1] http://wiki.osdev.org/Interrupt_Service_Routines
|
||||
*/
|
||||
#define SET_INTERRUPT_HANDLER(num, has_error_code, handler) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"push $1f\n\t" \
|
||||
"push %0\n\t" \
|
||||
"call %P1\n\t" \
|
||||
"add $8, %%esp\n\t" \
|
||||
ISR_STUB("1", has_error_code, "%P2") \
|
||||
:: "g" (num), "i" (idt_set_intr_gate_desc), "i" (handler) \
|
||||
: "eax", "ecx", "edx" \
|
||||
); \
|
||||
#define SET_INT_EXC_HANDLER(num, has_error_code, handler, exc, dpl) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"pushl %[_dpl_]\n\t" \
|
||||
"pushl %[_cs_]\n\t" \
|
||||
"pushl $1f\n\t" \
|
||||
"pushl %[_isr_num_]\n\t" \
|
||||
"call idt_set_intr_gate_desc\n\t" \
|
||||
"add $16, %%esp\n\t" \
|
||||
ISR_STUB("1", has_error_code, "%P[_handler_]", exc) \
|
||||
: \
|
||||
: [_isr_num_] "g" (num), \
|
||||
[_handler_] "i" (handler), \
|
||||
[_cs_] "i" (exc ? GDT_SEL_CODE_EXC : GDT_SEL_CODE_INT), \
|
||||
[_dpl_] "i" (dpl) \
|
||||
/* the invocation of idt_set_intr_gate_desc may clobber */ \
|
||||
/* the caller-saved registers: */ \
|
||||
: "eax", "ecx", "edx" \
|
||||
); \
|
||||
} while (0)
|
||||
#define SET_INTERRUPT_HANDLER(num, has_error_code, handler) \
|
||||
SET_INT_EXC_HANDLER(num, has_error_code, handler, 0, PRIV_LVL_INT)
|
||||
#define SET_EXCEPTION_HANDLER(num, has_error_code, handler) \
|
||||
SET_INT_EXC_HANDLER(num, has_error_code, handler, 1, PRIV_LVL_EXC)
|
||||
|
||||
/* Disable maskable hardware interrupts */
|
||||
#define DISABLE_IRQ() \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue