GDT即Global Descriptor Table(全局描述符表),之所以要用到该表,是和IA-32平台的内存管理机制密切相关的...
ZLOX_GDT_ENTRY gdt_entries[5]; |
// This structure contains the value of one GDT entry. // We use the attribute 'packed' to tell GCC not to change // any of the alignment in the structure. struct _ZLOX_GDT_ENTRY { ZLOX_UINT16 limit_low; // The lower 16 bits of the limit. ZLOX_UINT16 base_low; // The lower 16 bits of the base. ZLOX_UINT8 base_middle; // The next 8 bits of the base. ZLOX_UINT8 access; // Access flags, determine what ring this segment can be used in. ZLOX_UINT8 granularity; ZLOX_UINT8 base_high; // The last 8 bits of the base. }__attribute__((packed)); |
// Set the value of one GDT entry. static ZLOX_VOID zlox_gdt_set_gate(ZLOX_SINT32 num, ZLOX_UINT32 base, ZLOX_UINT32 limit, ZLOX_UINT8 access, ZLOX_UINT8 gran) { gdt_entries[num].base_low = (base & 0xFFFF); gdt_entries[num].base_middle = (base >> 16) & 0xFF; gdt_entries[num].base_high = (base >> 24) & 0xFF; gdt_entries[num].limit_low = (limit & 0xFFFF); gdt_entries[num].granularity = (limit >> 16) & 0x0F; gdt_entries[num].granularity |= gran & 0xF0; gdt_entries[num].access = access; } |
// Initialises the GDT (global descriptor table) static ZLOX_VOID zlox_init_gdt() { gdt_ptr.limit = (sizeof(ZLOX_GDT_ENTRY) * 5) - 1; gdt_ptr.base = (ZLOX_UINT32)gdt_entries; zlox_gdt_set_gate(0, 0, 0, 0, 0); // Null segment zlox_gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment zlox_gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment zlox_gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment zlox_gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment _zlox_gdt_flush((ZLOX_UINT32)&gdt_ptr); } |
ZLOX_GDT_PTR gdt_ptr; |
// This struct describes a GDT pointer. It points to the start of // our array of GDT entries, and is in the format required by the // lgdt instruction. struct _ZLOX_GDT_PTR { ZLOX_UINT16 limit; // The upper 16 bits of all selector limits. ZLOX_UINT32 base; // The address of the first ZLOX_GDT_ENTRY struct. }__attribute__((packed)); |
.global _zlox_gdt_flush # Allows the C code to call _zlox_gdt_flush(). _zlox_gdt_flush: movl 4(%esp),%eax # Get the pointer to the GDT, passed as a parameter. lgdt (%eax) # Load the new GDT pointer movw $0x10,%ax # 0x10 is the offset in the GDT to our data segment movw %ax,%ds # Load all data segment selectors movw %ax,%es movw %ax,%fs movw %ax,%gs movw %ax,%ss ljmp $0x08,$_gdt_ljmp_flush # 0x08 is the offset to our code segment: Far jump! _gdt_ljmp_flush: ret |
struct _ZLOX_IDT_ENTRY { ZLOX_UINT16 base_lo; // The lower 16 bits of the address to jump to when this interrupt fires. ZLOX_UINT16 sel; // Kernel segment selector. ZLOX_UINT8 always0; // This must always be zero. ZLOX_UINT8 flags; // More flags. ZLOX_UINT16 base_hi; // The upper 16 bits of the address to jump to. }__attribute__((packed)); typedef struct _ZLOX_IDT_ENTRY ZLOX_IDT_ENTRY; |
ZLOX_IDT_ENTRY idt_entries[256]; |
static ZLOX_VOID zlox_idt_set_gate(ZLOX_UINT8 num, ZLOX_UINT32 base, ZLOX_UINT16 sel, ZLOX_UINT8 flags) { idt_entries[num].base_lo = base & 0xFFFF; idt_entries[num].base_hi = (base >> 16) & 0xFFFF; idt_entries[num].sel = sel; idt_entries[num].always0 = 0; // We must uncomment the OR below when we get to using user-mode. // It sets the interrupt gate's privilege level to 3. idt_entries[num].flags = flags /* | 0x60 */; } |
// Initialise the interrupt descriptor table. static ZLOX_VOID zlox_init_idt() { idt_ptr.limit = sizeof(ZLOX_IDT_ENTRY) * 256 - 1; idt_ptr.base = (ZLOX_UINT32)idt_entries; zlox_memset((ZLOX_UINT8 *)idt_entries, 0, sizeof(ZLOX_IDT_ENTRY)*256); zlox_idt_set_gate( 0, (ZLOX_UINT32)_zlox_isr_0 , 0x08, 0x8E); zlox_idt_set_gate( 1, (ZLOX_UINT32)_zlox_isr_1 , 0x08, 0x8E); zlox_idt_set_gate( 2, (ZLOX_UINT32)_zlox_isr_2 , 0x08, 0x8E); zlox_idt_set_gate( 3, (ZLOX_UINT32)_zlox_isr_3 , 0x08, 0x8E); zlox_idt_set_gate( 4, (ZLOX_UINT32)_zlox_isr_4 , 0x08, 0x8E); zlox_idt_set_gate( 5, (ZLOX_UINT32)_zlox_isr_5 , 0x08, 0x8E); zlox_idt_set_gate( 6, (ZLOX_UINT32)_zlox_isr_6 , 0x08, 0x8E); zlox_idt_set_gate( 7, (ZLOX_UINT32)_zlox_isr_7 , 0x08, 0x8E); zlox_idt_set_gate( 8, (ZLOX_UINT32)_zlox_isr_8 , 0x08, 0x8E); zlox_idt_set_gate( 9, (ZLOX_UINT32)_zlox_isr_9 , 0x08, 0x8E); zlox_idt_set_gate(10, (ZLOX_UINT32)_zlox_isr_10, 0x08, 0x8E); zlox_idt_set_gate(11, (ZLOX_UINT32)_zlox_isr_11, 0x08, 0x8E); zlox_idt_set_gate(12, (ZLOX_UINT32)_zlox_isr_12, 0x08, 0x8E); zlox_idt_set_gate(13, (ZLOX_UINT32)_zlox_isr_13, 0x08, 0x8E); zlox_idt_set_gate(14, (ZLOX_UINT32)_zlox_isr_14, 0x08, 0x8E); zlox_idt_set_gate(15, (ZLOX_UINT32)_zlox_isr_15, 0x08, 0x8E); zlox_idt_set_gate(16, (ZLOX_UINT32)_zlox_isr_16, 0x08, 0x8E); zlox_idt_set_gate(17, (ZLOX_UINT32)_zlox_isr_17, 0x08, 0x8E); zlox_idt_set_gate(18, (ZLOX_UINT32)_zlox_isr_18, 0x08, 0x8E); zlox_idt_set_gate(19, (ZLOX_UINT32)_zlox_isr_19, 0x08, 0x8E); zlox_idt_set_gate(20, (ZLOX_UINT32)_zlox_isr_20, 0x08, 0x8E); zlox_idt_set_gate(21, (ZLOX_UINT32)_zlox_isr_21, 0x08, 0x8E); zlox_idt_set_gate(22, (ZLOX_UINT32)_zlox_isr_22, 0x08, 0x8E); zlox_idt_set_gate(23, (ZLOX_UINT32)_zlox_isr_23, 0x08, 0x8E); zlox_idt_set_gate(24, (ZLOX_UINT32)_zlox_isr_24, 0x08, 0x8E); zlox_idt_set_gate(25, (ZLOX_UINT32)_zlox_isr_25, 0x08, 0x8E); zlox_idt_set_gate(26, (ZLOX_UINT32)_zlox_isr_26, 0x08, 0x8E); zlox_idt_set_gate(27, (ZLOX_UINT32)_zlox_isr_27, 0x08, 0x8E); zlox_idt_set_gate(28, (ZLOX_UINT32)_zlox_isr_28, 0x08, 0x8E); zlox_idt_set_gate(29, (ZLOX_UINT32)_zlox_isr_29, 0x08, 0x8E); zlox_idt_set_gate(30, (ZLOX_UINT32)_zlox_isr_30, 0x08, 0x8E); zlox_idt_set_gate(31, (ZLOX_UINT32)_zlox_isr_31, 0x08, 0x8E); _zlox_idt_flush((ZLOX_UINT32)&idt_ptr); } |
ZLOX_IDT_PTR idt_ptr; |
struct _ZLOX_IDT_PTR { ZLOX_UINT16 limit; ZLOX_UINT32 base; // The address of the first element in our ZLOX_IDT_ENTRY array. }__attribute__((packed)); typedef struct _ZLOX_IDT_PTR ZLOX_IDT_PTR; |
# This macro creates a stub for an ISR which does NOT pass it's own # error code (adds a dummy errcode byte). .macro _ZLOX_INT_ISR_NOERRCODE argNum .global _zlox_isr_\argNum _zlox_isr_\argNum: cli # Disable interrupts firstly. pushl $0 # Push a dummy error code. pushl $\argNum # Push the interrupt number. jmp _zlox_isr_common_stub .endm # This macro creates a stub for an ISR which passes it's own error code. .macro _ZLOX_INT_ISR_ERRCODE argNum .global _zlox_isr_\argNum _zlox_isr_\argNum: cli # Disable interrupts firstly. pushl $\argNum # Push the interrupt number. jmp _zlox_isr_common_stub .endm _ZLOX_INT_ISR_NOERRCODE 0 _ZLOX_INT_ISR_NOERRCODE 1 _ZLOX_INT_ISR_NOERRCODE 2 _ZLOX_INT_ISR_NOERRCODE 3 _ZLOX_INT_ISR_NOERRCODE 4 _ZLOX_INT_ISR_NOERRCODE 5 _ZLOX_INT_ISR_NOERRCODE 6 _ZLOX_INT_ISR_NOERRCODE 7 .............................. //省略N行代码 .............................. //省略N行代码 _ZLOX_INT_ISR_NOERRCODE 31 _zlox_isr_common_stub: pusha # Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax movw %ds,%ax # Lower 16-bits of eax = ds. pushl %eax # save the data segment descriptor movw $0x10,%ax # load the kernel data segment descriptor movw %ax,%ds movw %ax,%es movw %ax,%fs movw %ax,%gs call zlox_isr_handler # in zlox_isr.c pop %ebx # reload the original data segment descriptor movw %bx,%ds movw %bx,%es movw %bx,%fs movw %bx,%gs popa # Pops edi,esi,ebp... addl $8,%esp # Cleans up the pushed error code and pushed ISR number sti iret # pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP |
// This gets called from our ASM interrupt handler stub. ZLOX_VOID zlox_isr_handler(ZLOX_ISR_REGISTERS regs) { zlox_monitor_write("zenglox recieved interrupt: "); zlox_monitor_write_dec(regs.int_no); zlox_monitor_put('\n'); } |