;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 32-bit kernel startup code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %include "asm.inc" SECTION .text ;SEGMENT _TEXT USE32 CLASS=CODE ;BITS 32 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; entry point ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GLOBAL entry entry: ; check if data segment linked, located, and loaded properly mov eax,[ds_magic] cmp eax,DS_MAGIC je ds_ok ; display a blinking white-on-blue 'D' and freeze mov word [0B8000h],9F44h jmp short $ ds_ok: ; stop using bootloader GDT, and load new GDT lgdt [gdt_ptr] mov ax,LINEAR_DATA_SEL mov ds,ax mov es,ax mov ss,ax mov fs,ax mov gs,ax jmp LINEAR_CODE_SEL:sbat sbat: ; zero the C language BSS ; 'bss' and 'end' are defined in the linker script file EXTERN bss, end mov edi,bss mov ecx,end sub ecx,edi xor eax,eax rep stosb mov esp,stack ; set up interrupt handlers, then load IDT register mov ecx,(idt_end - idt) >> 3 ; number of exception handlers mov edi,idt mov esi,isr0 do_idt: mov eax,esi ; EAX=offset of entry point mov [edi],ax ; set low 16 bits of gate offset shr eax,16 mov [edi + 6],ax ; set high 16 bits of gate offset add edi,8 ; 8 bytes/interrupt gate add esi,(isr1 - isr0) ; bytes/stub loop do_idt lidt [idt_ptr] ; GRUB 0.90 leaves the NT bit set in EFLAGS. The first IRET we attempt ; will cause a TSS-based task-switch, which will cause Exception 10. ; Let's prevent that: push dword 2 popf IMP main call main ; call C code jmp $ ; freeze ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Multiboot header for GRUB bootloader. This must be in the first 8K ; of the kernel file. We use the aout kludge so it works with ELF, ; DJGPP COFF, Win32 PE, or other formats. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; these are in the linker script file EXTERN code, bss, end ALIGN 4 mboot: dd MULTIBOOT_HEADER_MAGIC dd MULTIBOOT_HEADER_FLAGS dd MULTIBOOT_CHECKSUM ; aout kludge. These must be PHYSICAL addresses dd mboot dd code dd bss dd end dd entry ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; interrupt/exception handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IMP fault ; I shouldn't have to do this! %macro PUSHB 1 db 6Ah db %1 %endmacro %macro INTR 1 ; (byte offset from start of stub) isr%1: push byte 0 ; ( 0) fake error code PUSHB %1 ; ( 2) exception number push gs ; ( 4) push segment registers push fs ; ( 6) push es ; ( 8) push ds ; ( 9) pusha ; (10) push GP registers mov ax,LINEAR_DATA_SEL ; (11) put known-good values... mov ds,eax ; (15) ...in segment registers mov es,eax ; (17) mov fs,eax ; (19) mov gs,eax ; (21) mov eax,esp ; (23) push eax ; (25) push pointer to regs_t .1: ; setvect() changes the operand of the CALL instruction at run-time, ; so we need its location = 27 bytes from start of stub. We also want ; the CALL to use absolute addressing instead of EIP-relative, so: mov eax,fault ; (26) call eax ; (31) jmp all_ints ; (33) %endmacro ; (38) %macro INTR_EC 1 isr%1: nop ; error code already pushed nop ; nop+nop=same length as push byte PUSHB %1 ; ( 2) exception number push gs ; ( 4) push segment registers push fs ; ( 6) push es ; ( 8) push ds ; ( 9) pusha ; (10) push GP registers mov ax,LINEAR_DATA_SEL ; (11) put known-good values... mov ds,eax ; (15) ...in segment registers mov es,eax ; (17) mov fs,eax ; (19) mov gs,eax ; (21) mov eax,esp ; (23) push eax ; (25) push pointer to regs_t .1: ; setvect() changes the operand of the CALL instruction at run-time, ; so we need its location = 27 bytes from start of stub. We also want ; the CALL to use absolute addressing instead of EIP-relative, so: mov eax,fault ; (26) call eax ; (31) jmp all_ints ; (33) %endmacro ; (38) ; the vector within the stub (operand of the CALL instruction) ; is at (isr0.1 - isr0 + 1) all_ints: pop eax popa ; pop GP registers pop ds ; pop segment registers pop es pop fs pop gs add esp,8 ; drop exception number and error code iret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: getvect ; action: reads interrupt vector ; in: [EBP + 12] = vector number ; out: vector stored at address given by [EBP + 8] ; modifies: EAX, EDX ; minimum CPU: '386+ ; notes: C prototype: ; typedef struct ; { unsigned access_byte, eip; } vector_t; ; getvect(vector_t *v, unsigned vect_num); ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EXP getvect push ebp mov ebp,esp push esi push ebx mov esi,[ebp + 8] ; get access byte from IDT[i] xor ebx,ebx mov bl,[ebp + 12] shl ebx,3 mov al,[idt + ebx + 5] mov [esi + 0],eax ; get handler address from stub mov eax,isr1 sub eax,isr0 ; assume stub size < 256 bytes mul byte [ebp + 12] mov ebx,eax add ebx,isr0 mov eax,[ebx + (isr0.1 - isr0 + 1)] mov [esi + 4],eax pop ebx pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: setvect ; action: writes interrupt vector ; in: [EBP + 12] = vector number, ; vector stored at address given by [EBP + 8] ; out: (nothing) ; modifies: EAX, EDX ; minimum CPU: '386+ ; notes: C prototype: ; typedef struct ; { unsigned access_byte, eip; } vector_t; ; getvect(vector_t *v, unsigned vect_num); ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EXP setvect push ebp mov ebp,esp push esi push ebx mov esi,[ebp + 8] ; store access byte in IDT[i] mov eax,[esi + 0] xor ebx,ebx mov bl,[ebp + 12] shl ebx,3 mov [idt + ebx + 5],al ; store handler address in stub mov eax,isr1 sub eax,isr0 ; assume stub size < 256 bytes mul byte [ebp + 12] mov ebx,eax add ebx,isr0 mov eax,[esi + 4] mov [ebx + (isr0.1 - isr0 + 1)],eax pop ebx pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; interrupt/exception stubs ; *** CAUTION: these must be consecutive, and must all be the same size. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; INTR 0 ; zero divide (fault) INTR 1 ; debug/single step INTR 2 ; non-maskable interrupt (trap) INTR 3 ; INT3 (trap) INTR 4 ; INTO (trap) INTR 5 ; BOUND (fault) INTR 6 ; invalid opcode (fault) INTR 7 ; coprocessor not available (fault) INTR_EC 8 ; double fault (abort w/ error code) INTR 9 ; coproc segment overrun (abort; 386/486SX only) INTR_EC 0Ah ; bad TSS (fault w/ error code) INTR_EC 0Bh ; segment not present (fault w/ error code) INTR_EC 0Ch ; stack fault (fault w/ error code) INTR_EC 0Dh ; GPF (fault w/ error code) INTR_EC 0Eh ; page fault INTR 0Fh ; reserved INTR 10h ; FP exception/coprocessor error (trap) INTR 11h ; alignment check (trap; 486+ only) INTR 12h ; machine check (Pentium+ only) INTR 13h INTR 14h INTR 15h INTR 16h INTR 17h INTR 18h INTR 19h INTR 1Ah INTR 1Bh INTR 1Ch INTR 1Dh INTR 1Eh INTR 1Fh ; isr20 through isr2F are hardware interrupts. The 8259 programmable ; interrupt controller (PIC) chips must be reprogrammed to make these work. INTR 20h ; IRQ 0/timer interrupt INTR 21h ; IRQ 1/keyboard interrupt INTR 22h INTR 23h INTR 24h INTR 25h INTR 26h ; IRQ 6/floppy interrupt INTR 27h INTR 28h ; IRQ 8/real-time clock interrupt INTR 29h INTR 2Ah INTR 2Bh INTR 2Ch INTR 2Dh ; IRQ 13/math coprocessor interrupt INTR 2Eh ; IRQ 14/primary ATA ("IDE") drive interrupt INTR 2Fh ; IRQ 15/secondary ATA drive interrupt ; syscall software interrupt INTR 30h ; the other 207 vectors are undefined %assign i 31h %rep (0FFh - 30h) INTR i %assign i (i + 1) %endrep ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SECTION .data ;SEGMENT _DATA USE32 CLASS=DATA ds_magic: dd DS_MAGIC gdt: ; NULL descriptor dw 0 ; limit 15:0 dw 0 ; base 15:0 db 0 ; base 23:16 db 0 ; type db 0 ; limit 19:16, flags db 0 ; base 31:24 ; unused descriptor dw 0 dw 0 db 0 db 0 db 0 db 0 LINEAR_DATA_SEL equ $-gdt dw 0FFFFh dw 0 db 0 db 92h ; present, ring 0, data, expand-up, writable db 0CFh ; page-granular (4 gig limit), 32-bit db 0 LINEAR_CODE_SEL equ $-gdt dw 0FFFFh dw 0 db 0 db 9Ah ; present,ring 0,code,non-conforming,readable db 0CFh ; page-granular (4 gig limit), 32-bit db 0 gdt_end: gdt_ptr: dw gdt_end - gdt - 1 dd gdt ; 256 ring 0 interrupt gates idt: %rep 256 dw 0 ; offset 15:0 dw LINEAR_CODE_SEL ; selector db 0 ; (always 0 for interrupt gates) db 8Eh ; present,ring 0,'386 interrupt gate dw 0 ; offset 31:16 %endrep idt_end: idt_ptr: dw idt_end - idt - 1 ; IDT limit dd idt ; linear adr of IDT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SECTION .bss ;SEGMENT _BSS USE32 CLASS=BSS resd 1024 stack: