detect vm86 and abort
[com32] / src / loader.asm
1         section .loader
2
3         extern startup
4         extern tmpsegbase
5
6         [bits 16]
7         global _start
8 _start:
9         cli
10         mov ax, cs
11         mov ds, ax
12         mov es, ax
13         mov fs, ax      ; this will store the original real mode segment
14         mov ss, ax
15         ; modify the return to real mode jump segment
16         mov [.jmpcs16 + 3], ax
17
18         xor ax, ax
19         mov sp, ax
20
21         ; check for VM86 and abort
22         mov eax, cr0
23         test ax, 1
24         jz .notvm86
25
26         mov si, str_errvm86
27         call printstr
28         jmp exit
29
30 .notvm86:
31         call enable_a20
32
33         ; calculate GDT linear address
34         xor eax, eax
35         mov ax, cs
36         shl eax, 4
37         mov [tmpsegbase], eax   ; save segment base
38         add eax, gdt
39         mov [gdt_base], eax
40
41         ; set tmp segment bases to match the linear address of our current seg
42         xor eax, eax
43         mov ax, cs
44         shl eax, 4
45         mov word [gdt + 18h + 2], ax    ; tmp pm code base
46         mov word [gdt + 20h + 2], ax    ; tmp pm data base
47         mov word [gdt + 28h + 2], ax    ; ret-to-realmode code
48         shr eax, 16
49         mov byte [gdt + 18h + 4], al
50         mov byte [gdt + 20h + 4], al
51         mov byte [gdt + 28h + 4], al
52
53         mov si, str_enterpm
54         call printstr
55
56         lgdt [gdt_lim]
57
58         mov eax, cr0
59         or eax, 1
60         mov cr0, eax
61
62         jmp 18h:.pm
63
64         [bits 32]
65 .pm:    mov ax, 20h     ; tmp data selector
66         mov ds, ax
67         mov ss, ax
68         mov ax, 10h     ; dest data selector
69         mov es, ax
70
71         mov esp, 200000h
72
73         call startup
74
75         ; return to real mode
76         jmp 28h:.rm
77
78         [bits 16]
79 .rm:    mov eax, cr0
80         and ax, 0xfffe
81         mov cr0, eax
82 .jmpcs16:
83         jmp 42h:.loadcs16       ; 42 seg is modifed at the start (TODO)
84 .loadcs16:
85         mov ax, fs
86         mov ds, ax
87         mov es, ax
88         mov ss, ax
89
90 exit:   mov ax, 4c00h
91         int 21h
92
93 str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0
94 str_enterpm db 'Entering 32bit protected mode ...',10,0
95
96 enable_a20:
97         call test_a20
98         jnc .ret
99
100         mov si, .infomsg
101         call printstr           ; print "Enable A20 line ... "
102
103         call enable_a20_kbd
104         call test_a20
105         jnc .done
106         call enable_a20_fast
107         call test_a20
108         jnc .done
109         mov si, .failstr
110         call printstr
111         mov ax, 4c00h
112         int 21h
113 .done:  mov si, .okstr
114         call printstr
115 .ret:   ret
116
117 .infomsg db 'Enable A20 line:',0
118 .failstr db ' failed.',10,0
119 .okstr  db ' success.',10,0
120
121         ; CF = 1 if A20 test fails (not enabled)
122 test_a20:
123         push ds
124         push es
125         xor ax, ax
126         mov ds, ax
127         not ax
128         mov es, ax
129         mov si, 420h
130         mov di, 430h
131         mov dl, [ds:si]
132         mov dh, [es:di]
133         mov [ds:si], al
134         not ax
135         mov [es:di], al
136         cmp al, [ds:si]
137         clc
138         jnz .done
139         stc
140 .done:  mov [ds:si], dl
141         mov [es:di], dh
142         pop es
143         pop ds
144         ret
145
146 enable_a20_fast:
147         mov si, .info
148         call printstr
149         in al, 92h
150         or al, 2
151         out 92h, al
152         ret
153 .info db ' fast ...',0
154
155 KBC_DATA_PORT equ 0x60
156 KBC_CMD_PORT equ 0x64
157 KBC_STATUS_PORT equ 0x64
158 KBC_CMD_WR_OUTPORT equ 0xd1
159
160 KBC_STAT_IN_FULL equ 2
161
162 enable_a20_kbd:
163         mov si, .info
164         call printstr
165         call kbc_wait_write
166         mov al, KBC_CMD_WR_OUTPORT
167         out KBC_CMD_PORT, al
168         call kbc_wait_write
169         mov al, 0xdf
170         out KBC_DATA_PORT, al
171         ret
172 .info db ' kbd ...',0
173
174 kbc_wait_write:
175         in al, KBC_STATUS_PORT
176         and al, KBC_STAT_IN_FULL
177         jnz kbc_wait_write
178         ret
179
180 printstr:
181         lodsb
182         test al, al
183         jz .end
184         cmp al, 10      ; check for line-feed and insert CR before it
185         jnz .nolf
186         push ax
187         mov dl, 13
188         mov ah, 2
189         int 21h
190         pop ax
191 .nolf:  mov ah, 2
192         mov dl, al
193         int 21h
194         jmp printstr
195 .end:   ret
196
197         
198         align 4
199 enterpm dd 0xbad00d     ; space for linear address for far jump to pmode
200 enterpm_sel dw 8        ; selector for far jump to protected mode
201         align 4
202 gdt_lim dw 47           ; GDT limit
203 gdt_base dd 0xbadf00d   ; space for GDT linear address
204
205         align 8
206 gdt:    ; 0: null segment
207         dd 0
208         dd 0
209         ; 1: code - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd
210         dd 0000ffffh
211         dd 00cf9a00h
212         ; 2: data - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw
213         dd 0000ffffh
214         dd 00cf9200h
215         ; 3: tmp code (will set base before entering pmode)
216         dd 0000ffffh
217         dd 00cf9a00h
218         ; 4: tmp data (will set base before entering pmode)
219         dd 0000ffffh
220         dd 00cf9200h
221         ; 5: return to real-mode 16bit code segment
222         dd 0000ffffh
223         dd 00009a00h
224
225
226 ; vi:set ts=8 sts=8 sw=8 ft=nasm: