2 ; Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
4 ; This program is free software: you can redistribute it and/or modify
5 ; it under the terms of the GNU General Public License as published by
6 ; the Free Software Foundation, either version 3 of the License, or
7 ; (at your option) any later version.
9 ; This program is distributed in the hope that it will be useful,
10 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ; GNU General Public License for more details.
14 ; You should have received a copy of the GNU General Public License
15 ; along with this program. If not, see <http://www.gnu.org/licenses/>.
46 ; This will set up our new segment registers. We need to do
47 ; something special in order to set CS. We do what is called a
48 ; far jump. A jump that includes a segment as well as an offset.
49 ; This is declared in C as 'extern void gdt_flush();'
52 IMP gp
; Says that '_gp' is in another file
54 lgdt [gp
] ; Load the GDT with our '_gp' which is a special pointer
55 mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
60 jmp 0x08:flush2
; 0x08 is the offset to our code segment: Far jump!
62 ret ; Returns back to the C code!
66 lgdt [gp
] ; Load the GDT with our '_gp' which is a special pointer
67 jmp 0x08:ccode
; 0x08 is the offset to our code segment: Far jump!
72 ; Loads the IDT defined in '_idtp' into the processor.
73 ; This is declared in C as 'extern void idt_load();'
79 ; 0: Divide By Zero Exception
93 ; 2: Non Maskable Interrupt Exception
114 ; 5: Out of Bounds Exception
121 ; 6: Invalid Opcode Exception
128 ; 7: Coprocessor Not Available Exception
135 ; 8: Double Fault Exception (With Error Code!)
141 ; 9: Coprocessor Segment Overrun Exception
148 ; 10: Bad TSS Exception (With Error Code!)
154 ; 11: Segment Not Present Exception (With Error Code!)
160 ; 12: Stack Fault Exception (With Error Code!)
166 ; 13: General Protection Fault Exception (With Error Code!)
172 ; 14: Page Fault Exception (With Error Code!)
178 ; 15: Reserved Exception
185 ; 16: Floating Point Exception
192 ; 17: Alignment Check Exception
199 ; 18: Machine Check Exception
298 ; We call a C function in here. We need to let the assembler know
299 ; that '_fault_handler' exists in another file
302 ; This is our common ISR stub. It saves the processor state, sets
303 ; up for kernel mode segments, calls the C-level fault handler,
304 ; and finally restores the stack frame.
319 mov eax, fault_handler
495 ; syscall with 0 args
499 ; push ds ; switch to kernel space
505 ; call syscall_handler ; make call
507 ; pop es ; back to userland
533 call syscall_handler
; make call
570 ; mov eax, syscall_handler
587 pmode32: mov ax,[gp
] ; now we are in protected mode
588 mov ds,ax ; set all selector limits to 4 GB
593 mov eax,cr0
; restore real mode
597 ;db 0eah ; far jump to rmode label
605 ;rmode: clc ; now we are back in real mode, zero carry
606 ; sti ; to indicate ok and enable interrupts
616 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 ; return to real mode procedure, modified from section 14.5 of 386INTEL.TXT
618 ; (...continued from below...)
619 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
621 ; 4. load SS with selector to descriptor that is "appropriate for real mode":
622 ; Limit = 64K (FFFFh) Byte-granular (G=0)
623 ; Expand-up (E=0) Writable (W=1)
624 ; Present (P=1) Base address = any value
625 ; You may leave the other data segment registers with limits >64K if you
626 ; want 'unreal mode', otherwise load them with a similar selector.
629 mov ax,_16BIT_DATA_SEL
636 ; 5. Clear the PE bit in register CR0
641 ; 6. Jump to a 16:16 real-mode far address
645 ; 7. Load all other segment registers (SS, DS, ES, FS, GS)
653 ; 8. Use the LIDT instruction to load an IDT appropriate for real mode,
654 ; with base address = 0 and limit = 3FFh. Use the 32-bit operand size
655 ; override prefix so all 32 bits of the IDT base are set (otherwise,
656 ; only the bottom 24 bits will be set).
659 ; 9. Zero the high 16 bits of 32-bit registers. If the register value is
660 ; not important, just zero the entire 32-bit register, otherwise use 'movzx'
670 ; 10. Enable interrupts
674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
676 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
678 ALIGN 512 ; so NDISASM knows where the 32-bit code starts
681 GLOBAL pmode
; so "objdump --source ..." can find it
690 ; LTR is illegal in real mode, so do it now
694 ; zero the kernel BSS
696 mov edi,bss
; BSS starts here
698 sub ecx,edi ; BSS size is (g_end - g_bss) bytes
705 ; return to real mode procedure, modified from section 14.5 of 386INTEL.TXT
706 ; 1. Disable interrupts (already done)
707 ; 2. If paging is enabled... (it isn't)
708 ; 3. Jump to a segment with limit 64K-1. CS must have this limit
709 ; before you return to real mode.
711 jmp _16BIT_CODE_SEL:pmode16
716 ; save registers used by C: EBP, EBX, ESI, EDI
717 ; Actually, just use PUSHA to save them all
720 ; copy V86 mode registers to top of stack
722 mov esi,[esp + 128] ; (uregs_t *regs)
724 mov ecx,92 ; sizeof(uregs_t)
729 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
731 ; action: unified IRQ/interrupt/exception handler
732 ; in: IRET frame, error code, and exception number
733 ; all pushed on stack
735 ; modifies: (nothing)
736 ; notes: prototype for C-language interrupt handler:
737 ; int fault(uregs_t *regs);
739 ; *** WARNING: The order in which registers are pushed and popped here
740 ; must agree with the layout of the uregs_t structure in the C code.
741 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
744 push gs ; push segment registers
748 pusha ; push EDI...EAX (7 regs; 8 dwords)
750 mov ds,eax ; put known-good values in segment registers
754 push esp ; push pointer to stacked registers
760 ; *** FALL-THROUGH to to_user ***
762 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
764 ; action: kernel exit code
765 ; in: uregs_t frame on kernel stack
767 ; modifies: (nothing)
769 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
773 ; IRET to Ring 3 pmode will pop these values
774 ; 8 GP regs, 4 seg regs, int_num/err_code, EIP/CS/EFLAGS/ESP/SS
775 ; making the kernel stack 76 bytes smaller
777 ; IRET to V86 mode...
778 ;test dword [esp + 64],20000h ; test VM bit in stacked EFLAGS
779 test byte [esp + 66],2
782 ; ...will pop these values
783 ; 8 GP regs, 4 seg regs, int_num/err_code, EIP/CS/EFLAGS/ESP/SS/ES/DS/FS/GS
784 ; making the kernel stack 92 bytes smaller
787 ; For IRET to Ring 0 pmode, _tss_esp0 is not used,
788 ; so this value doesn't matter
792 popa ; pop EAX...EDI (7 regs; 8 dwords)
793 pop es ; pop segment registers
798 add esp,byte 8 ; drop int_num and err_code
800 iret ; pop EIP...GS (9 regs)
802 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
804 ; action: cleans up after virtual 8086 mode session;
805 ; returns to pmode kernel
806 ; in: (see start_v86, above)
807 ; out: registers in uregs_t may be modified
808 ; modifies: registers in uregs_t may be modified
810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
814 ; get modified V86 mode registers
821 ; remove V86 registers from stack
824 ; restore C registers and return to pmode kernel
830 ; We need it for halt pc
836 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
838 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
843 db "32-bit CPU required", 10, 13, 0
846 db "CPU already in Virtual-8086 mode "
847 db "(Windows DOS box or EMM386 loaded?)"
856 ; 16:16 far address for return to real mode. Don't change the order
857 ; of '_real_ip' and '_real_cs', and don't put anything between them,
858 ; because these two values are the operand of a far JMP
873 ; most of the TSS is unused
874 ; I need only ESP0, SS0, and the I/O permission bitmap
879 dw KRNL_DATA_SEL
, 0 ; SS0, reserved
882 dw 0, 0 ; SS1, reserved
885 dw 0, 0 ; SS2, reserved
888 dd 0, 0 ; EIP, EFLAGS
889 dd 0, 0, 0, 0 ; EAX, ECX, EDX, EBX
890 dd 0, 0, 0, 0 ; ESP, EBP, ESI, EDI
891 dw 0, 0 ; ES, reserved
892 dw 0, 0 ; CS, reserved
893 dw 0, 0 ; SS, reserved
894 dw 0, 0 ; DS, reserved
895 dw 0, 0 ; FS, reserved
896 dw 0, 0 ; GS, reserved
897 dw 0, 0 ; LDT, reserved
898 dw 0, g_tss_iopb
- _tss
; debug, IO permission bitmap base
904 ; The TSS notes in section 12.5.2 of volume 1 of
905 ; "IA-32 Intel Architecture Software Developer's Manual"
906 ; are confusing as hell. I think they mean simply that the IOPB
907 ; must contain an even number of bytes; so pad here if necessary.
919 TSS_SEL
EQU ($
- _gdt
)
921 dw g_tss_end
- _tss
- 1
924 db 89h ; ring 0 available 32-bit TSS
928 KRNL_DATA_SEL
EQU ($
- _gdt
)
931 dw 0 ; base; gets set above
933 db 92h ; present, ring 0, data, expand-up, writable
937 KRNL_CODE_SEL
EQU ($
- _gdt
)
940 dw 0 ; base; gets set above
942 db 9Ah ; present, ring 0, code, non-conforming, readable
946 USER_DATA_SEL
EQU (($
- _gdt
) |
3) ; RPL=3 (ring 3)
951 db 0F2h
; present, ring 3, data, expand-up, writable
955 USER_CODE_SEL
EQU (($
- _gdt
) |
3)
958 dw 0 ; base; gets set above
960 db 0FAh
; present, ring 3, code, non-conforming, readable
964 LINEAR_DATA_SEL
EQU ($
- _gdt
)
968 db 92h ; present, ring 0, data, expand-up, writable
972 LINEAR_CODE_SEL
EQU ($
- _gdt
)
976 db 9Ah ; present, ring 0, code, non-conforming, readable
980 _16BIT_DATA_SEL
EQU ($
- _gdt
)
983 dw 0 ; base; gets set above
985 db 92h ; present, ring 0, data, expand-up, writable
989 _16BIT_CODE_SEL
EQU ($
- _gdt
)
992 dw 0 ; base; gets set above
994 db 9Ah ; present, ring 0, code, non-conforming, readable
1000 dw _gdt_end
- _gdt
- 1 ; GDT limit
1001 dd _gdt
; linear adr of GDT; gets set above
1008 ; 48 ring 0 interrupt gates
1009 times
48 dw 0, KRNL_CODE_SEL
, 8E00h
, 0
1011 ; one ring 3 interrupt gate for syscalls (INT 30h)
1013 dw KRNL_CODE_SEL
; selector
1014 db 0 ; (always 0 for interrupt gates)
1015 db 0EEh
; present, ring 3, '386 interrupt gate
1020 dw _idt_end
- _idt
- 1 ; IDT limit
1021 dd _idt
; linear, physical address of IDT (patched)