Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- KERNEL_OFS EQU 0x8000 ; Origin point (VMA) of the kernel
- ; Offset form base of memory where kernel starts
- VIDEO_TEXT_ADDR EQU 0xb8000
- ; Hard code beginning of text video memory
- ATTR_WHITE_ON_MAGENTA EQU 0x57 ; White on magenta attribl
- EFLAGS_IF_BIT EQU 9 ; Interrupt Flag (IF) bit = 9
- org KERNEL_OFS ; Set origin point (VMA) of kernel
- ; PIC constants
- PIC1 EQU 0x20 ; IO base address for master PIC
- PIC2 EQU 0xA0 ; IO base address for slave PIC
- PIC1_COMMAND EQU PIC1
- PIC1_DATA EQU (PIC1+1)
- PIC2_COMMAND EQU PIC2
- PIC2_DATA EQU (PIC2+1)
- ICW1_ICW4 EQU 0x01 ; Indicates that ICW4 will be present
- ICW1_SINGLE EQU 0x02 ; Single (cascade) mode
- ICW1_INTERVAL4 EQU 0x04 ; Call address interval 4 (8)
- ICW1_LEVEL EQU 0x08 ; Level triggered (edge) mode
- ICW1_INIT EQU 0x10 ; Initialization - required!
- ICW4_8086 EQU 0x01 ; 8086/88 (MCS-80/85) mode
- ICW4_AUTO EQU 0x02 ; Auto (normal) EOI
- ICW4_BUF_SLAVE EQU 0x08 ; Buffered mode/slave
- ICW4_BUF_MASTER EQU 0x0C ; Buffered mode/master
- ICW4_SFNM EQU 0x10 ; Special fully nested (not)
- PIC1_BASE EQU 0x20
- PIC2_BASE EQU 0x28
- PIC_EOI EQU 0x20
- bits 16
- ; Kernel Entry point
- ; Upon entry these have all been set:
- ; Direction Flag (DF) = 0
- ; DS=ES=GS=FS=0x0000
- ; SS:SP = 0x0000:0x7c00
- kernel_start:
- mov si, not386_err ; Default error message to no 386 processor
- call check_386 ; Is this a 32-bit processor?
- jz .error ; If not print error and stop
- mov si, noa20_err ; Default error message to A20 enable error
- call a20_enable ; Enable A20 line
- jz .error ; If the A20 line isn't enabled then print error and stop
- jmp switch_protmode_32 ; Switch to 32-bit protected mode and
- ; and continue at label 'protmode32_entry'
- .error:
- call print_string_16 ; Print error message
- .end:
- cli ; Disable interrupts
- .endloop:
- hlt ; Halt CPU
- jmp .endloop ; Loop in case we get an NMI (non-maskable interrupt)
- ; Function: print_string_16
- ; Display a string to the console on display page 0
- ;
- ; Inputs: SI = Offset of address to print
- ; Clobbers: AX, BX, SI
- print_string_16:
- mov ah, 0x0e ; BIOS tty Print
- xor bx, bx ; Set display page to 0 (BL)
- jmp .getch
- .repeat:
- int 0x10 ; print character
- .getch:
- lodsb ; Get character from string
- test al,al ; Have we reached end of string?
- jnz .repeat ; if not process next character
- .end:
- ret
- ; Function: wait_8042_cmd
- ; Wait until the Input Buffer Full bit in the keyboard controller's
- ; status register becomes 0. After calls to this function it is
- ; safe to send a command on Port 0x64
- ;
- ; Inputs: None
- ; Clobbers: AX
- ; Returns: None
- KBC_STATUS_IBF_BIT EQU 1
- wait_8042_cmd:
- in al, 0x64 ; Read keyboard controller status register
- test al, 1 << KBC_STATUS_IBF_BIT
- ; Is bit 1 (Input Buffer Full) set?
- jnz wait_8042_cmd ; If it is then controller is busy and we
- ; can't send command byte, try again
- ret ; Otherwise buffer is clear and ready to send a command
- ; Function: wait_8042_data
- ; Wait until the Output Buffer Empty (OBE) bit in the keyboard controller's
- ; status register becomes 0. After a call to this function there is
- ; data available to be read on port 0x60.
- ;
- ; Inputs: None
- ; Clobbers: AX
- ; Returns: None
- KBC_STATUS_OBE_BIT EQU 0
- wait_8042_data:
- in al, 0x64 ; Read keyboard controller status register
- test al, 1 << KBC_STATUS_OBE_BIT
- ; Is bit 0 (Output Buffer Empty) set?
- jz wait_8042_data ; If not then no data waiting to be read, try again
- ret ; Otherwise data is ready to be read
- ; Function: a20_kbd_enable
- ; Enable the A20 line via the keyboard controller
- ;
- ; Inputs: None
- ; Clobbers: AX, CX
- ; Returns: None
- a20_kbd_enable:
- pushf
- cli ; Disable interrupts
- call wait_8042_cmd ; When controller ready for command
- mov al, 0xad ; Send command 0xad (disable keyboard).
- out 0x64, al
- call wait_8042_cmd ; When controller ready for command
- mov al, 0xd0 ; Send command 0xd0 (read output port)
- out 0x64, al
- call wait_8042_data ; Wait until controller has data
- in al, 0x60 ; Read data from keyboard
- mov cx, ax ; CX = copy of byte read
- call wait_8042_cmd ; Wait until controller is ready for a command
- mov al, 0xd1
- out 0x64, al ; Send command 0xd1 (write output port)
- call wait_8042_cmd ; Wait until controller is ready for a command
- mov ax, cx
- or al, 1 << 1 ; Write value back with bit 1 set
- out 0x60, al
- call wait_8042_cmd ; Wait until controller is ready for a command
- mov al, 0xae
- out 0x64, al ; Write command 0xae (enable keyboard)
- call wait_8042_cmd ; Wait until controller is ready for command
- popf ; Restore flags including interrupt flag
- ret
- ; Function: a20_fast_enable
- ; Enable the A20 line via System Control Port A
- ;
- ; Inputs: None
- ; Clobbers: AX
- ; Returns: None
- a20_fast_enable:
- in al, 0x92 ; Read System Control Port A
- test al, 1 << 1
- jnz .finished ; If bit 1 is set then A20 already enabled
- or al, 1 << 1 ; Set bit 1
- and al, ~(1 << 0) ; Clear bit 0 to avoid issuing a reset
- out 0x92, al ; Send Enabled A20 and disabled Reset to control port
- .finished:
- ret
- ; Function: a20_bios_enable
- ; Enable the A20 line via the BIOS function Int 15h/AH=2401
- ;
- ; Inputs: None
- ; Clobbers: AX
- ; Returns: None
- a20_bios_enable:
- mov ax, 0x2401 ; Int 15h/AH=2401 enables A20 on BIOS with this feature
- int 0x15
- ret
- ; Function: a20_check
- ; Determine if the A20 line is enabled or disabled
- ;
- ; Inputs: None
- ; Clobbers: AX, CX, ES
- ; Returns: ZF=1 if A20 enabled, ZF=0 if disabled
- a20_check:
- pushf ; Save flags so Interrupt Flag (IF) can be restored
- push ds ; Save volatile registers
- push si
- push di
- cli ; Disable interrupts
- xor ax, ax
- mov ds, ax
- mov si, 0x600 ; 0x0000:0x0600 (0x00600) address we will test
- mov ax, 0xffff
- mov es, ax
- mov di, 0x610 ; 0xffff:0x0610 (0x00600) address we will test
- ; The physical address pointed to depends on whether
- ; memory wraps or not. If it wraps then A20 is disabled
- mov cl, [si] ; Save byte at 0x0000:0x0600
- mov ch, [es:di] ; Save byte at 0xffff:0x0610
- mov byte [si], 0xaa ; Write 0xaa to 0x0000:0x0600
- mov byte [es:di], 0x55 ; Write 0x55 to 0xffff:0x0610
- xor ax, ax ; Set return value 0
- cmp byte [si], 0x55 ; If 0x0000:0x0600 is 0x55 and not 0xaa
- je .disabled ; then memory wrapped because A20 is disabled
- dec ax ; A20 Disable, set AX to -1
- .disabled:
- ; Cleanup by restoring original bytes in memory. This must be in reverse
- ; order from the order they were originally saved
- mov [es:di], ch ; Restore data saved data to 0xffff:0x0610
- mov [si], cl ; Restore data saved data to 0x0000:0x0600
- pop di ; Restore non-volatile registers
- pop si
- pop ds
- popf ; Restore Flags (including IF)
- test al, al ; Return ZF=1 if A20 enabled, ZF=0 if disabled
- ret
- ; Function: a20_enable
- ; Enable the A20 line
- ;
- ; Inputs: None
- ; Clobbers: AX, BX, CX, DX
- ; Returns: ZF=0 if A20 not enabled, ZF=1 if A20 enabled
- a20_enable:
- call a20_check ; Is A20 already enabled?
- jnz .a20_on ; If so then we're done ZF=1
- call a20_bios_enable ; Try enabling A20 via BIOS
- call a20_check ; Is A20 now enabled?
- jnz .a20_on ; If so then we're done ZF=1
- call a20_kbd_enable ; Try enabling A20 via keyboard controller
- call a20_check ; Is A20 now enabled?
- jnz .a20_on ; If so then we're done ZF=1
- call a20_fast_enable ; Try enabling A20 via fast method
- call a20_check ; Is A20 now enabled?
- jnz .a20_on ; If so then we're done ZF=1
- .a20_err:
- xor ax, ax ; If A20 disabled then return with ZF=0
- .a20_on:
- ret
- ; Function: check_386
- ; Check if this processor is at least a 386
- ;
- ; Inputs: None
- ; Clobbers: AX
- ; Returns: ZF=0 if Processor earlier than a 386, ZF=1 if processor is 386+
- check_386:
- xor ax, ax ; Zero EFLAGS
- push ax
- popf ; Push zeroed flags
- pushf
- pop ax ; Get the currently set flags
- and ax, 0xf000 ; if high 4 bits of FLAGS are not set then
- cmp ax, 0xf000 ; CPU is an 8086/8088/80186/80188
- je .error ; and exit with ZF = 0
- mov ax, 0xf000 ; Set the high 4 bits of FLAGS to 1
- push ax
- popf ; Update the FLAGS register
- pushf ; Get newly set FLAGS into AX
- pop ax
- and ax, 0xf000 ; if none of the high 4 bits are set then
- jnz .noerror ; CPU is an 80286. Return success ZF = 1
- ; otherwise CPU is a 386+
- .error:
- xor ax, ax ; Set ZF = 0 (Earlier than a 386)
- .noerror:
- ret
- ; Load the GDT and fix up each entry by swapping the high 8 bits of
- ; the base with the access byte. The GDT entries created by MAKE_GDT_DESC
- ; can't be used directly since the base is encoded in such a way that
- ; operations other than + or - aren't performed on it..
- load_gdt:
- mov cx, NUMGDTENTRIES
- mov si, gdt.start
- .fixentry:
- mov al, [si + 5] ; Get high 8 bits of the base
- mov bl, [si + 7] ; Get the Access byte
- mov [si + 5], bl ; Save the swapped values back
- mov [si + 7], al
- add si, 8 ; Advance to next entry
- loop .fixentry ; Repeat until all entries processed
- lgdt [gdt.gdtr] ; Load the GDT record
- ret
- ; Load the IDT and fix up each entry by swapping the high 16 bits of
- ; the handler offset with the selector value. The IDT entries created
- ; by MAKE_IDT_DESC can't be used directly since the offset of the
- ; handler is encoded in such a way that operations other than + or -
- ; aren't performed on it.
- load_idt:
- mov cx, NUMIDTENTRIES
- mov si, idt.start
- .fixentry:
- mov ax, [si + 2] ; Get upper 16 bits of handler's offset
- mov bx, [si + 6] ; Get the selector value
- mov [si + 2], bx ; Save the swapped values back
- mov [si + 6], ax
- add si, 8 ; Advance to next entry
- loop .fixentry ; Repeat until all entries processed
- lidt [idt.idtr] ; Load the IDT record
- ret
- ; Function: switch_protmode_32
- ; Switch processor to 32-bit protected
- ; - Enable Interrupts (IF=1)
- ; - Paging not enabled
- ; - Disable interrupts on the Master and Slave PICs
- ; - Flush any pending external interrupts
- ; - Jump to 32-bit protected mode at label `protmode32_entry`
- ;
- ; Clobbers: N/A
- ; Returns: Jumps to label 'protmode32_entry', doesn't return
- switch_protmode_32:
- push 1<<EFLAGS_IF_BIT | 1<<1
- ; Reset the EFLAG bits to 0 except IF=1 and reserved bit 1
- popf ; This put EFLAGS in a known state
- ; Disable IRQs on the Master and Slave PICs
- mov al, 0xFF ; Bits that are 1 disable interrupts, 0 = enable
- out PIC2_DATA, al ; Disable all interrupts on Slave PIC
- out PIC1_DATA, al ; Disable all interrupts on Master PIC
- ; Flush any pending IRQs
- ; Do a loop to allow pending interrupts to be processed.
- ; Execute enough instructions to process all 16 interrupts.
- mov ecx, 8
- .irqflush:
- dec ecx
- jnz .irqflush
- call load_idt ; Load IDT and perform fixups
- call load_gdt ; Load GDT and perform fixups
- ; Enter 32-bit protected mode without paging enabled
- movzx esp, sp ; Zero extend SP to ESP
- mov eax, cr0 ; Get current CR0
- or eax, 0x00000001 ; Enable protected mode bit
- mov cr0, eax ; Update CR0
- jmp CODE32_PL0_SEL:protmode32_entry
- ; Start executing code in 32-bit protected mode
- ; Also flushes the instruction prefetch queue
- noa20_err db "A20 line couldn't be enabled", 10, 13, 0
- not386_err db "Processor is not a 386+", 10, 13, 0
- ; Macro to build an initial GDT descriptor entry
- ; 4 parameters: base, limit, access, flags
- ;
- ; The generated descriptor entry will need to be fixed up at runtime
- ; before interrupts are enabled
- ;
- %macro MAKE_GDT_DESC 4
- dw (%2 & 0xffff)
- dd %1
- db ((%4 & 0x0f) << 4) | ((%2 >> 16) & 0x0f)
- db (%3 & 0xff)
- %endmacro
- ; Macro to build an initial IDT descriptor entry
- ; 3 parameters: offset, selector, access
- ;
- ; The generated descriptor entry will need to be fixed up at runtime
- ; before interrupts are enabled
- ;
- %macro MAKE_IDT_DESC 3
- dd %1
- db 0
- db (%3 & 0xFF)
- dw (%2 & 0xFFFF)
- %endmacro
- ; GDT structure
- align 8
- gdt:
- .start:
- .null: MAKE_GDT_DESC 0, 0, 0, 0
- ; Null descriptor
- .code32_pl0: MAKE_GDT_DESC 0, 0x000FFFFF, 10011011b, 1100b
- ; 32-bit code, PL0, acc=1, r/x, gran=page
- ; Lim=0xffffffff
- .data32_pl0: MAKE_GDT_DESC 0, 0x000FFFFF, 10010011b, 1100b
- ; 32-bit data, PL0, acc=1, r/w, gran=page
- ; Lim=0xffffffff
- .code32_pl3: MAKE_GDT_DESC 0, 0x000FFFFF, 11111011b, 1100b
- ; 32-bit code, PL3, acc=1, r/x, gran=page
- ; Lim=0xffffffff
- .data32_pl3: MAKE_GDT_DESC 0, 0x000FFFFF, 11110011b, 1100b
- ; 32-bit data, PL3, acc=1, r/w, gran=page
- .tss32: MAKE_GDT_DESC tss_entry, TSS_SIZE-1, 10001001b, 0000b
- ; 32-bit TSS, gran=byte, available, IOPL=0
- .end:
- NUMGDTENTRIES equ ((gdt.end - gdt.start) / 8)
- ; GDT record
- align 4
- dw 0 ; Padding align dd GDT in gdtr on 4 byte boundary
- .gdtr:
- dw .end - .start - 1 ; limit (Size of GDT - 1)
- dd .start ; base of GDT
- NULL_SEL_RPL0 EQU 0
- NULL_SEL_RPL1 EQU 1
- NULL_SEL_RPL2 EQU 2
- NULL_SEL_RPL3 EQU 3
- CODE32_PL0_SEL EQU gdt.code32_pl0 - gdt.start
- DATA32_PL0_SEL EQU gdt.data32_pl0 - gdt.start
- CODE32_PL3_SEL EQU gdt.code32_pl3 - gdt.start
- DATA32_PL3_SEL EQU gdt.data32_pl3 - gdt.start
- TSS32_SEL EQU gdt.tss32 - gdt.start
- align 16
- ; Create an IDT which handles exceptions
- idt:
- .start:
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- ; Set undefined instruction exception to test an exception in ring 3
- ; MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_ud2, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_error, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC exc_def_noerror, CODE32_PL0_SEL, 10001110b
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ0
- ; Set a keyboard handler for testing interrupts
- ; MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ1
- MAKE_IDT_DESC irq_keyboard, CODE32_PL0_SEL, 10001110b ; IRQ1
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ2
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ3
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ4
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ5
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ6
- MAKE_IDT_DESC irq_def_master, CODE32_PL0_SEL, 10001110b ; IRQ7
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ8
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ9
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ10
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ11
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ12
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ13
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ14
- MAKE_IDT_DESC irq_def_slave, CODE32_PL0_SEL, 10001110b ; IRQ15
- .end:
- NUMIDTENTRIES equ ((idt.end - idt.start) / 8)
- align 4
- .idtr:
- dw .end - .start - 1 ; limit (Size of IDT - 1)
- dd .start ; base of IDT
- TSS_IO_BITMAP_SIZE EQU 0
- align 4
- tss_entry:
- .back_link: dd 0
- .esp0: dd 0x90000 ; Kernel stack pointer used on ring transitions
- .ss0: dd 0x10 ; Kernel stack segment used on ring transitions
- .esp1: dd 0
- .ss1: dd 0
- .esp2: dd 0
- .ss2: dd 0
- .cr3: dd 0
- .eip: dd 0
- .eflags: dd 0
- .eax: dd 0
- .ecx: dd 0
- .edx: dd 0
- .ebx: dd 0
- .esp: dd 0
- .ebp: dd 0
- .esi: dd 0
- .edi: dd 0
- .es: dd 0
- .cs: dd 0
- .ss: dd 0
- .ds: dd 0
- .fs: dd 0
- .gs: dd 0
- .ldt: dd 0
- .trap: dw 0
- .iomap_base:dw TSS_SIZE ; IOPB offset
- ;.cetssp: dd 0 ; Need this if CET is enabled
- ; Insert any kernel defined task instance data here
- ; ...
- ; If using VME (Virtual Mode extensions) there need to be an additional 32 bytes
- ; available immediately preceding iomap. If using VME uncomment next 2 lines
- ;.vmeintmap: ; If VME enabled uncomment this line and the next
- ;TIMES 32 db 0 ; 32*8 bits = 256 bits (one bit for each interrupt)
- .iomap:
- TIMES TSS_IO_BITMAP_SIZE db 0x0
- ; IO bitmap (IOPB) size 8192 (8*8192=65536) representing
- ; all ports. An IO bitmap size of 0 would fault all IO
- ; port access if IOPL < CPL (CPL=3 with v8086)
- %if TSS_IO_BITMAP_SIZE > 0
- .iomap_pad: db 0xff ; Padding byte that has to be filled with 0xff
- ; To deal with issues on some CPUs when using an IOPB
- %endif
- TSS_SIZE EQU $-tss_entry
- BITS 32
- ; Entry point for 32-bit mode
- ; Upon entry these have all been set:
- ; - CPU is running at Current Privilege Level (CPL) = 0 aka kernel mode
- ; - Interrupts are enabled (IF=1)
- ; - Direction Flag clear (DF=0)
- protmode32_entry:
- mov eax, DATA32_PL3_SEL | 3 ; Set DS/ES/FS/GS/SS to a privilege level 3 data selector
- mov ds, eax
- mov es, eax
- mov fs, eax
- mov gs, eax
- mov eax, DATA32_PL0_SEL | 0 ; Set DS/ES/FS/GS/SS to a privilege level 0 data selector
- mov ss, eax
- ; Load the TSS to allow for interrupts/exceptions while in User Mode (Ring3)
- mov eax, TSS32_SEL
- ltr ax
- ; Remap Master PIC to 0x20, and Slave PIC to 0x28
- ; For testing disable all interrupts except keyboard
- mov al, ICW1_INIT | ICW1_ICW4
- out PIC1_COMMAND, al
- out PIC2_COMMAND, al
- ; ICW2
- mov al, PIC1_BASE ; Remap master PIC to 0x20
- out PIC1_DATA, al
- mov al, PIC2_BASE ; Remap slave PIC to 0x28
- out PIC2_DATA, al
- ; ICW3
- mov al, 4
- out PIC1_DATA, al
- mov al, 2
- out PIC2_DATA, al
- ; ICW4
- mov al, ICW4_8086
- out PIC1_DATA, al
- out PIC2_DATA, al
- mov al, 0xFF ; Bits that are 1 disable interrupts, 0 = enable
- out PIC2_DATA, al ; Disable all interrupts on Slave PIC
- mov al, 0xFD ; Bits that are 1 disable interrupts, 0 = enable
- out PIC1_DATA, al ; Disable all interrupts on Master PIC except IRQ1
- ; Change to ring 3 (user mode)
- mov eax, esp ; Save current ESP and use it as stack pointer in ring 3
- push DATA32_PL3_SEL | 3 ; User mode SS = 32 data segment with a DPL of 3, RPL=3
- push eax ; User mode ESP
- push 1<<EFLAGS_IF_BIT | 1<<1
- ; Reset all EFLAG bits to 0 except IF=1 and reserved bit 1
- push CODE32_PL3_SEL | 3 ; User mode CS = 32 code segment with a DPL of 3, RPL=3
- push .usermode ; User mode EIP - enter ring 3 at label '.usermode'
- iret ; Use IRET to perform ring transition from CPL 0 to CPL 3
- .usermode:
- ; Write the letters "PM" (protected mode) to upper left hand corner of display
- ; starting at text video memory address 0xb8000 using white on magenta attribute
- mov dword [VIDEO_TEXT_ADDR], (ATTR_WHITE_ON_MAGENTA << 8 | 'M') << 16 | \
- (ATTR_WHITE_ON_MAGENTA << 8 | 'P')
- ud2 ; Cause a #UD exception to test interrupts
- jmp $ ; Can't use HLT in Ring 3
- ; Undefined instruction exception for testing an exception
- exc_ud2:
- mov esi, .ud2str
- mov edi, VIDEO_TEXT_ADDR+80*2
- mov ah, ATTR_WHITE_ON_MAGENTA
- call print_string
- ; Skip the UD2 instruction that caused the fault
- add dword [esp], 2
- iret
- .ud2str: db "#UD exception", 0
- ; Default IRQ handler for IRQs >= 8
- irq_def_slave:
- ; Send EOI to slave and then master PIC for IRQs >= 8
- mov al, PIC_EOI
- out PIC2_COMMAND, al
- out PIC1_COMMAND, al
- iret
- ; Default IRQ handler for IRQs < 8
- irq_def_master:
- ; Send EOI to master PIC for IRQs < 8
- mov al, PIC_EOI
- out PIC1_COMMAND, al
- iret
- ; Test keyboard handler
- irq_keyboard:
- ; Read scancode from port 0x60 and throw it away
- ; You'd normally process the key, but this is just for testing
- in al, 0x60
- mov al, PIC_EOI
- out PIC1_COMMAND, al
- iret
- ; Default exception without an error code pushed by CPU
- exc_def_noerror:
- ; Do nothing and return
- iret
- ; Default exception with an error code pushed by CPU
- exc_def_error:
- ; Remove error code automatically pushed by CPU to stack
- add esp, 4
- iret
- ; Function: print_string (32-bit protected mode)
- ; Print a string directly to video memory
- ;
- ; Inputs: ESI = Offset of address to print
- ; EDI = Video memory address to write to
- ; AH = Attribute to use when writing a character
- ;
- ; Clobbers: EAX, ESI, EDI
- print_string:
- jmp .getch
- .repeat:
- stosw ; Print character with attribute
- .getch:
- lodsb ; Get character from string
- test al,al ; Have we reached end of string?
- jnz .repeat ; if not process next character
- .end:
- ret
- kernel_end:
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement