reorganize source
[com32] / src / loader.asm
1         section .loader
2
3         extern startup
4         extern _ldr_main_start
5         extern _main_start
6         extern _main_size
7         extern boot_mem_map
8         extern boot_mem_map_size
9
10 %include 'macros.inc'
11
12         [bits 16]
13         global _start
14 _start:
15         cli
16         mov ax, cs
17         mov ds, ax
18         mov es, ax
19         mov fs, ax      ; this will store the original real mode segment
20         mov ss, ax
21         ; modify the return to real mode jump segment
22         mov [.jmpcs16 + 3], ax
23
24         xor ax, ax
25         mov sp, ax
26
27         ; check for VM86 and abort
28         mov eax, cr0
29         test ax, 1
30         jz .notvm86
31
32         mov si, str_errvm86
33         call printstr
34         jmp exit
35
36 .notvm86:
37 %ifdef BUG_WARNING
38         call warning    ; issue a warning and allow the user to abort
39 %endif
40
41 %ifdef CON_SERIAL
42         call setup_serial
43 %endif
44         call enable_a20
45         call detect_memory
46
47         ; calculate GDT linear address
48         xor eax, eax
49         mov ax, cs
50         shl eax, 4
51         add eax, gdt
52         mov [gdt_base], eax
53
54         ; set tmp segment bases to match the linear address of our current seg
55         xor eax, eax
56         mov ax, cs
57         shl eax, 4
58         mov word [gdt + 18h + 2], ax    ; tmp pm code base
59         mov word [gdt + 20h + 2], ax    ; tmp pm data base
60         mov word [gdt + 28h + 2], ax    ; ret-to-realmode code
61         shr eax, 16
62         mov byte [gdt + 18h + 4], al
63         mov byte [gdt + 20h + 4], al
64         mov byte [gdt + 28h + 4], al
65
66         mov si, str_enterpm
67         call printstr
68
69         lgdt [gdt_lim]
70
71         mov eax, cr0
72         or eax, 1
73         mov cr0, eax
74
75         jmp 18h:.pm
76
77         [bits 32]
78 .pm:    mov ax, 20h     ; tmp data selector
79         mov ds, ax
80         mov ax, 10h     ; dest data selector
81         mov es, ax
82
83         ; copy main program high
84         cld
85         mov esi, _ldr_main_start
86         mov edi, _main_start
87         mov ecx, _main_size + 3
88         shr ecx, 2
89         rep movsd
90
91         ; copy memory map
92         mov eax, [mem_map_size]
93         mov [es:boot_mem_map_size], eax
94         mov esi, mem_map
95         mov edi, boot_mem_map
96         mov ecx, 32     ; 128 bytes
97         rep movsd
98
99         mov ax, 10h
100         mov ds, ax
101         mov ss, ax
102         mov esp, _main_start
103
104         call 8:startup
105         cli
106
107         ; return to real mode
108         jmp 28h:.rm
109
110         [bits 16]
111 .rm:    mov eax, cr0
112         and ax, 0xfffe
113         mov cr0, eax
114 .jmpcs16:
115         jmp 42h:.loadcs16       ; 42 seg is modifed at the start
116 .loadcs16:
117         mov ax, fs
118         mov ds, ax
119         mov es, ax
120         mov ss, ax
121
122         ; restore real-mode IVT
123         lidt [rmidt]
124         sti
125
126         mov ax, 3
127         int 10h
128 exit:   mov ax, 4c00h
129         int 21h
130
131 str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0
132 str_enterpm db 'Entering 32bit protected mode ...',10,0
133
134
135 %ifdef BUG_WARNING
136         ; warns the user about the experimental and buggy state of this
137         ; protected mode system, and allows aborting if having to reboot later
138         ; is not acceptable.
139 warning:
140         mov si, str_warnmsg
141         call printstr
142         WAITKEY
143         dec al
144         jz exit
145         ret
146
147 str_warnmsg:
148         db 'WARNING: this program uses an experimental protected mode kernel, which is ',10
149         db 'still in a very early stage of development, and will probably not be able to ',10
150         db 'return cleanly to DOS on exit. You might be forced to reboot after quitting!',10
151         db 'If this is not acceptable, press ESC now to abort.',10,10
152         db 'Press ESC to abort and return to DOS, any other key to continue...',10,0
153 %endif  ; BUG_WARNING
154
155
156 ; ---------------------- A20 address line enable -----------------------
157
158 enable_a20:
159         call test_a20
160         jnc .ret
161
162         mov si, .infomsg
163         call printstr           ; print "Enable A20 line ... "
164
165         call enable_a20_kbd
166         call test_a20
167         jnc .done
168         call enable_a20_fast
169         call test_a20
170         jnc .done
171         mov si, .failstr
172         call printstr
173         mov ax, 4c00h
174         int 21h
175 .done:  mov si, .okstr
176         call printstr
177 .ret:   ret
178
179 .infomsg db 'Enable A20 line:',0
180 .failstr db ' failed.',10,0
181 .okstr  db ' success.',10,0
182
183         ; CF = 1 if A20 test fails (not enabled)
184 test_a20:
185         push ds
186         push es
187         xor ax, ax
188         mov ds, ax
189         not ax
190         mov es, ax
191         mov si, 420h
192         mov di, 430h
193         mov dl, [ds:si]
194         mov dh, [es:di]
195         mov [ds:si], al
196         not ax
197         mov [es:di], al
198         cmp al, [ds:si]
199         clc
200         jnz .done
201         stc
202 .done:  mov [ds:si], dl
203         mov [es:di], dh
204         pop es
205         pop ds
206         ret
207
208 enable_a20_fast:
209         mov si, .info
210         call printstr
211         in al, 92h
212         or al, 2
213         out 92h, al
214         ret
215 .info db ' fast ...',0
216
217 KBC_DATA_PORT equ 0x60
218 KBC_CMD_PORT equ 0x64
219 KBC_STATUS_PORT equ 0x64
220 KBC_CMD_WR_OUTPORT equ 0xd1
221
222 KBC_STAT_IN_FULL equ 2
223
224 enable_a20_kbd:
225         mov si, .info
226         call printstr
227         call kbc_wait_write
228         mov al, KBC_CMD_WR_OUTPORT
229         out KBC_CMD_PORT, al
230         call kbc_wait_write
231         mov al, 0xdf
232         out KBC_DATA_PORT, al
233         ret
234 .info db ' kbd ...',0
235
236 kbc_wait_write:
237         in al, KBC_STATUS_PORT
238         and al, KBC_STAT_IN_FULL
239         jnz kbc_wait_write
240         ret
241
242
243 ; ---------------------- memory detection -----------------------
244
245 detect_memory:
246         mov si, memdet_detram
247         call printstr
248         mov si, memdet_e820_msg
249         call printstr
250         call detect_mem_e820
251         jnc .done
252         mov si, str_fail
253         call printstr
254
255         mov si, memdet_detram
256         call printstr
257         mov si, memdet_e801_msg
258         call printstr
259         call detect_mem_e801
260         jnc .done
261         mov si, str_fail
262         call printstr
263
264         mov si, memdet_detram
265         call printstr
266         mov esi, memdet_88_msg
267         call printstr
268         call detect_mem_88
269         jnc .done
270         mov esi, str_fail
271         call printstr
272
273         mov si, memdet_detram
274         call printstr
275         mov esi, memdet_cmos_msg
276         call printstr
277         call detect_mem_cmos
278         jnc .done
279         mov esi, str_fail
280         call printstr
281
282         mov si, memdet_fail_msg
283         call printstr
284         jmp exit
285 .done:
286         mov si, str_ok
287         call printstr
288         ret
289
290 str_ok db 'OK',10,0
291 str_fail db 'failed',10,0
292 memdet_fail_msg db 'Failed to detect available memory!',10,0
293 memdet_detram   db 'Detecting RAM '
294 memdet_e820_msg db '(BIOS 15h/0xe820)... ',0
295 memdet_e801_msg db '(BIOS 15h/0xe801)... ',0
296 memdet_88_msg   db '(BIOS 15h/0x88, max 64mb)... ',0
297 memdet_cmos_msg db '(CMOS)...',0
298
299         ; detect extended memory using BIOS call 15h/e820
300 detect_mem_e820:
301         mov dword [mem_map_size], 0
302
303         mov edi, .buffer
304         xor ebx, ebx
305         mov edx, 534d4150h
306
307 .looptop:
308         mov eax, 0xe820
309         mov ecx, 24
310         int 15h
311         jc .fail
312         cmp eax, 534d4150h
313         jnz .fail
314
315         ; skip areas starting above 4GB as we won't be able to use them
316         cmp dword [di + 4], 0
317         jnz .skip
318
319         ; only care for type 1 (usable ram), otherwise ignore
320         cmp dword [di + 16], 1
321         jnz .skip
322
323         mov eax, [.buffer]
324         mov esi, mem_map
325         mov ebp, [mem_map_size]
326         mov [ebp * 8 + esi], eax
327
328         ; skip areas with 0 size (also clamp size to 4gb)
329         ; test high 32bits
330         cmp dword [edi + 12], 0
331         jz .highzero
332         ; high part is non-zero, make low part ffffffff
333         xor eax, eax
334         not eax
335         jmp .skiph0
336
337 .highzero:
338         ; if both high and low parts are zero, ignore
339         mov eax, [di + 8]
340         test eax, eax
341         jz .skip
342
343 .skiph0:mov [ebp * 8 + esi + 4], eax
344         inc dword [mem_map_size]
345
346 .skip:
347         ; terminate the loop if ebx was reset to 0
348         test ebx, ebx
349         jz .done
350         jmp .looptop
351 .done:
352         clc
353         ret
354
355 .fail:  ; if size > 0, then it's not a failure, just the end
356         cmp dword [mem_map_size], 0
357         jnz .done
358         stc
359         ret
360
361 .buffer times 32 db 0
362
363         ; detect extended memory using BIOS call 15h/e801
364 detect_mem_e801:
365         mov si, mem_map
366         mov ebp, [mem_map_size]
367         mov dword [ebp], 0
368
369         xor cx, cx
370         xor dx, dx
371         mov ax, 0xe801
372         int 15h
373         jc .fail
374
375         test cx, cx
376         jnz .foo1
377         test ax, ax
378         jz .fail
379         mov cx, ax
380         mov dx, bx
381
382 .foo1:  mov dword [si], 100000h
383         movzx eax, cx
384         ; first size is in KB, convert to bytes
385         shl eax, 10
386         jnc .foo2
387         ; overflow means it's >4GB, clamp to 4GB
388         mov eax, 0xffffffff
389 .foo2:  mov [si + 4], eax
390         inc dword [mem_map_size]
391         test dx, dx
392         jz .done
393         mov dword [si + 8], 1000000h
394         movzx eax, dx
395         ; second size is in 64kb blocks, convert to bytes
396         shl eax, 16
397         jnc .foo3
398         ; overflow means it's >4GB, clamp to 4GB
399         mov eax, 0xffffffff
400 .foo3:  mov [si + 12], eax
401         inc dword [mem_map_size]
402 .done:
403         clc
404         ret
405 .fail:
406         stc
407         ret
408
409 detect_mem_88:
410         ; reportedly some BIOS implementations fail to clear CF on success
411         clc
412         mov ah, 88h
413         int 15h
414         jc .fail
415
416         test ax, ax
417         jz .fail
418
419         ; ax has size in KB, convert to bytes in eax
420         and eax, 0xffff
421         shl eax, 10
422
423         mov esi, mem_map
424         mov dword [si], 100000h
425         mov [si + 4], eax
426
427         mov dword [mem_map_size], 1
428         clc
429         ret
430 .fail:  stc
431         ret
432
433 detect_mem_cmos:
434         mov al, 31h
435         out 70h, al
436         in al, 71h
437         mov ah, al
438         mov al, 30h
439         out 70h, al
440         in al, 71h
441
442         test ax, ax
443         jz .fail
444
445         ; ax has size in KB, convert to bytes in eax
446         and eax, 0xffff
447         shl eax, 10
448
449         mov esi, mem_map
450         mov dword [si], 100000h
451         mov [si + 4], eax
452         mov dword [mem_map_size], 1
453         clc
454         ret
455 .fail:  stc
456         ret
457
458
459         align 4
460 mem_map_size dd 0
461 mem_map times 128 db 0
462
463
464 ; ----------------------- serial console ------------------------
465
466 %ifdef CON_SERIAL
467 setup_serial:
468         ; set clock divisor
469         mov dx, UART_BASE + 3   ; LCTL
470         mov al, 80h             ; DLAB
471         out dx, al
472         mov dx, UART_BASE       ; DIVLO
473         mov al, UART_DIVISOR & 0xff
474         out dx, al
475         inc dx                  ; DIVHI
476         mov al, UART_DIVISOR >> 8
477         out dx, al
478         mov dx, UART_BASE + 3   ; LCTL
479         mov al, 3               ; 8n1
480         out dx, al
481         inc dx                  ; MCTL
482         mov al, 0xb             ; DTR/RTS/OUT2
483         out dx, al
484         ret
485
486 ser_putchar:
487         SER_PUTCHAR
488         ret
489
490 ser_printstr:
491         lodsb
492         test al, al
493         jz .end
494         cmp al, 10
495         jnz .nolf
496         push ax
497         mov al, 13
498         call ser_putchar
499         pop ax
500 .nolf:  call ser_putchar
501         jmp ser_printstr
502 .end:   ret
503
504 %endif ; def CON_SERIAL
505
506
507 printstr:
508         lodsb
509         test al, al
510         jz .end
511         cmp al, 10      ; check for line-feed and insert CR before it
512         jnz .nolf
513         push ax
514 %ifdef CON_SERIAL
515         mov al, 13
516         call ser_putchar
517 %endif
518         mov dl, 13
519         mov ah, 2
520         int 21h
521         pop ax
522 .nolf:
523 %ifdef CON_SERIAL
524         call ser_putchar
525 %endif
526         mov ah, 2
527         mov dl, al
528         int 21h
529         jmp printstr
530 .end:   ret
531
532
533         align 4
534 enterpm dd 0xbad00d     ; space for linear address for far jump to pmode
535 enterpm_sel dw 8        ; selector for far jump to protected mode
536         align 4
537 gdt_lim dw 47           ; GDT limit
538 gdt_base dd 0xbadf00d   ; space for GDT linear address
539
540         align 8
541 gdt:    ; 0: null segment
542         dd 0
543         dd 0
544         ; 1: code - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd (sel: 8)
545         dd 0000ffffh
546         dd 00cf9a00h
547         ; 2: data - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw (sel: 10h)
548         dd 0000ffffh
549         dd 00cf9200h
550         ; 3: tmp code (will set base before entering pmode) (sel: 18h)
551         dd 0000ffffh
552         dd 00cf9a00h
553         ; 4: tmp data (will set base before entering pmode) (sel: 20h)
554         dd 0000ffffh
555         dd 00cf9200h
556         ; 5: return to real-mode 16bit code segment (sel: 28h)
557         dd 0000ffffh
558         dd 00009a00h
559
560         ; pseudo IDTR descriptor for real-mode IVT at address 0
561         align 4
562         dw 0
563 rmidt:  dw 3ffh         ; IVT limit (1kb / 256 entries)
564         dd 0            ; IVT base 0
565
566 ; vi:set ts=8 sts=8 sw=8 ft=nasm: