;stage2.asm ;This is the stage2 section of the HOS bootloader ;It is to be copied into the normal FAT12 filesystem to be loaded by stage1 ;Author: Josh Holtrop ;Date: 03/11/04 ;Modified: 05/21/04 %include "bootdef.inc" %define VESA_MODEINFO_SEG 0x0120 %define VESA_MODELIST_SEG 0x0140 %define GOOD_MODELIST_SEG 0x0160 [bits 16] org BOOT_STAGE2_ADD ;k now read root directory mov bx, BOOT_ROOT_SEG mov ds, bx xor si, si ;k now ds:si points to beginning of root directory mov es, si mov cx, 224 ;max root entries loop_compare: mov di, kernel push cx push si ;save pointer to root dir entry mov cx, 11 loop_name: cmpsb loopz loop_name jnz goon ;cx didn't get to zero, bad file pop si pop cx jmp found_file ;good file, ds:si points to start of root directory entry goon: pop si add si, 32 pop cx loop loop_compare error: jmp $ ;halt! no kernel file found! found_file: ;ds:si points to root dir entry of kernel file xor ax, ax mov gs, ax mov ax, [ds:si+26] mov bx, BOOT_FAT_SEG mov ds, bx ;ds points to beginning of FAT mov edi, BOOT_KERNEL_ADD readkernel_loop: cmp ax, 0xff7 jg readkernel_done push ax call getCHSfromCluster mov ax, 0x0201 mov dl, [gs:BOOT_DRIVE] mov bx, BOOT_KERNEL_SEG mov es, bx xor bx, bx int 0x13 mov cx, 256 xor ax, ax mov es, ax mov esi, BOOT_KERNEL_SEG*16 copykernel_loop: mov ax, [es:esi] mov [es:edi], ax inc esi inc esi inc edi inc edi loop copykernel_loop pop ax ;current logical cluster # mov cx, ax ;cx=logical cluster mov dx, 3 mul dx shr ax, 1 ;ax=logical cluster * 3 / 2 mov si, ax test cl, 1 ;is bit0 set? jnz odd_cluster even_cluster: lodsw and ax, 0x0fff jmp got_cluster odd_cluster: lodsw shr ax, 4 got_cluster: jmp readkernel_loop ;------------------------------------------------------ readkernel_done: ;-------------put more real mode init stuff here! ;----ask to load RD from floppy mov ax, 0xb800 mov es, ax xor ax, ax mov ds, ax xor di, di mov cx, 2000 mov ax, 0x0700 cls_loop: stosw loop cls_loop mov dx, 0x3d4 ;move cursor off screen... mov al, 0x0e out dx, al inc dx mov al, 0xff out dx, al dec dx mov al, 0x0f out dx, al inc dx out dx, al xor di, di mov si, txt_welcome mov ah, 0x1f call puts mov di, 160 mov si, txt_rd1 mov ah, 7 call puts mov si, txt_rd2 mov di, 160*2 call puts mov di, 160*3 mov si, txt_input call puts get_rd: xor ax, ax int 0x16 cmp al, '1' jz got_rd cmp al, '2' jnz get_rd got_rd: stosb sub al, '1' push ds mov bx, BOOT_DATA_SEG ;segment for data to send kernel mov ds, bx mov [ds:BOOT_HASRD], al pop ds ;ds=0 cmp al, 0 ;dont load rd jnz yes_rd jmp no_rd yes_rd: mov cx, 80 mov edi, 0xb8000+160*4 filler_loop: ;draw a red bar mov word [ds:edi], 0x0400+177 inc edi inc edi loop filler_loop mov cx, 80 ;80 cylinders to read xor si, si ;start at cylinder 0 mov edi, BOOT_RD_ADD ;ram disk address read_cylinder: push cx mov bx, 0x0100 ;read sectors from head 0 mov es, bx xor bx, bx ;es:bx = data buffer for read mov ax, 0x0212 ;ah = int 0x13 function 2, al = number of sectors to read mov cx, si ;what cyl. we are on is now in cx mov ch, cl ;ch = cyl. number mov cl, 1 ;cl = sector number 1-63 xor dx, dx ;dh = head number mov dl, [gs:BOOT_DRIVE] ;dl = drive number int 0x13 mov bx, 0x0100 ;now read sectors from head 1 mov es, bx mov bx, 9216 ;es:bx = data buffer for read mov ax, 0x0212 ;ah = int 0x13 function 2, al = number of sectors to read mov cx, si ;what cyl. we are on is now in cx mov ch, cl ;ch = cyl. number mov cl, 1 ;cl = sector number 1-63 mov dh, 1 ;dh = head number mov dl, [gs:BOOT_DRIVE] ;dl = drive number int 0x13 mov ebx, 0xb8000 add bx, si shl bl, 1 mov word [ds:ebx+160*4], 0x0200+219 push si mov esi, 0x1000 mov cx, 0x2400 copydisk_loop: mov ax, [ds:esi] inc esi inc esi mov [ds:edi], ax inc edi inc edi loop copydisk_loop pop si ;what cylinder# we are on... inc si pop cx loop read_cylinder ;------------------------------------------------------ no_rd: mov ax, BOOT_DATA_SEG mov es, ax mov di, BOOT_MEMMAP ;store memmap info in es:di for kernel xor edx, edx mov ax, 0x0100 mov ds, ax xor ebx, ebx getmemmap_loop: push es push di push edx mov ax, 0x0100 ;get memory map mov es, ax xor di, di mov eax, 0x0000E820 mov ecx, 0x00000014 ;mov ebx, 0x00000000 mov edx, 0x534D4150 ;'SMAP' int 0x15 jc getmemmap_carry cmp eax, 0x534D4150 ;eax should be 'SMAP' on return... jnz getmemmap_error cmp ebx, 0 jz getmemmap_done pop edx ;now, copy memmap entry to es:di on the stack pop di pop es xor si, si mov cx, 20 getmemmap_copymem_loop: lodsb stosb loop getmemmap_copymem_loop inc edx jmp getmemmap_loop getmemmap_error: mov ax, 0xb800 mov es, ax mov di, 160*24 xor ax, ax mov ds, ax mov si, txt_memerror mov ah, 0x04 call puts hlt jmp $ getmemmap_carry: dec edx getmemmap_done: pop edx pop di pop es xor si, si mov cx, 20 getmemmap_done_copymem_loop: lodsb stosb loop getmemmap_done_copymem_loop inc edx mov di, BOOT_MEMENTRIES mov [es:di], edx ;save # of memmap entries for kernel ;on to vesa info... xor ax, ax mov gs, ax mov ds, ax mov ax, 0xb800 mov es, ax mov di, 160 mov cx, 2000-80 mov ax, 0x0700 cls_vesa_loop: stosw loop cls_vesa_loop mov si, txt_vesa mov di, 160*1 mov ah, 7 call puts push di mov ax, 0x0100 mov es, ax xor di, di mov dword [es:di], "2EBV" mov ax, 0x4F00 int 0x10 pop di cmp ax, 0x004F jz vesa_good mov si, txt_novesa mov ax, 0xb800 mov es, ax mov ah, 7 call puts mov ax, BOOT_DATA_SEG mov ds, ax mov word [ds:BOOT_VESA], 0 jmp vesa_done vesa_good: mov ax, 0xb800 mov es, ax mov ax, 0x0100 mov ds, ax xor si, si mov bx, [4] mov al, bh call puthex mov al, '.' stosb mov al, 7 stosb mov al, bl call puthex add di, 4 cmp bh, 2 jge vesa_good2 xor ax, ax mov ds, ax mov si, txt_vesaold mov ah, 7 call puts mov ax, BOOT_DATA_SEG mov ds, ax mov word [ds:BOOT_VESA], 0 jmp vesa_done vesa_good2: mov ebx, [6] ;something like 0x00000E60 mov edx, [14] mov si, bx shr ebx, 16 mov ds, bx ;ds:si points to null-terminated OEM identification string mov ah, 2 push si call puts pop si mov ax, BOOT_DATA_SEG mov es, ax mov di, BOOT_VESA_OEM vesa_copyoem: lodsb stosb or al, al jnz vesa_copyoem mov ax, 0x0100 mov ds, ax xor si, si mov di, BOOT_VESA_VBE mov cx, 512 vesa_copyvbe: lodsb stosb loop vesa_copyvbe mov si, dx shr edx, 16 mov ds, dx ;ds:si points to video mode list mov ax, VESA_MODELIST_SEG mov es, ax xor di, di vesa_copymodes: lodsw stosw cmp ax, 0xffff jnz vesa_copymodes mov ax, GOOD_MODELIST_SEG mov es, ax xor di, di mov cx, 256 mov ax, 0xffff clear_good_mode_list_loop: stosw loop clear_good_mode_list_loop mov ax, VESA_MODELIST_SEG mov ds, ax xor si, si ;ds:si points to video mode list where we can edit it :) mov ax, GOOD_MODELIST_SEG mov es, ax xor di, di xor dx, dx ;dx=what good mode # we are on vesa_readmodeinfo_loop: lodsw cmp ax, 0xffff jz vesa_endofmodes push ax ;save mode# call checkmode cmp ax, 0 jz vesa_readmodeinfo_good pop ax jmp vesa_readmodeinfo_loop vesa_readmodeinfo_good: pop ax ;restore mode# stosw call vesa_showmodeinfo inc dx jmp vesa_readmodeinfo_loop vesa_endofmodes: ;here we have a list of good modes at GOOD_MODELIST_SEG:0 xor ax, ax mov ds, ax mov si, txt_consolemode mov ax, 0xb800 mov es, ax mov di, 160*2 mov ah, 7 call puts mov di, 160*3 mov cx, dx mov al, 'b' vesa_displaylabels: stosb push ax mov al, 7 stosb mov al, '.' stosb mov al, 7 stosb pop ax inc al add di, 160-4 loop vesa_displaylabels ;done drawing screen of VESA choices, now ask for one ;valid options are 'a' through (al-1) mov bl, al xor ax, ax mov ds, ax mov di, 160*24 mov si, txt_input mov ah, 14 call puts vesa_getchoice: xor ax, ax int 0x16 cmp al, 'a' jl vesa_getchoice cmp al, bl jge vesa_getchoice stosb push ax mov al, 14 stosb pop ax xor ah, ah sub ax, 'a' cmp ax, 0 jz vesa_consolemode_only mov cx, ax ;cx holds good mode# (1=first good vesa mode) dec cx mov ax, GOOD_MODELIST_SEG mov ds, ax shl cx, 1 mov si, cx ;ds:si points to word containing selected mode# lodsw mov cx, ax mov dx, ax ;cx and dx hold mode# mov ax, VESA_MODEINFO_SEG mov es, ax xor di, di mov ax, 0x4F01 int 0x10 call checkvesa mov ax, VESA_MODEINFO_SEG mov ds, ax xor si, si mov ax, BOOT_DATA_SEG mov es, ax mov di, BOOT_VESA_INFO mov cx, 256 vesa_copymodeinfo_loop: lodsb stosb loop vesa_copymodeinfo_loop ;store ModeInfoBlock for current mov [es:BOOT_VESA], dx ;store mode# for kernel ; mov ax, 0xb800 ; mov es, ax ; xor di, di ; mov al, dh ; call puthex2 ; mov al, dl ; call puthex2 mov bx, dx or bx, 0x4000 ;set "use LFB" bit of mode# mov ax, 0x4F02 int 0x10 ;switch to graphics mode!!! call checkvesa jmp vesa_done vesa_consolemode_only: mov ax, BOOT_DATA_SEG mov es, ax mov word [es:BOOT_VESA], 0 jmp vesa_done ;------------------------------------------------------ vesa_showmodeinfo: pusha push es push ds mov ax, VESA_MODEINFO_SEG mov ds, ax xor si, si mov ax, 0xb800 mov es, ax mov cx, dx mov ax, 160 mul dx ;ax=160*mod# add ax, 160*3+6 ;offset first line of modes and room on left for label mov di, ax mov ax, [ds:18] call console_putDec add di, 10 mov al, 'x' call console_putChar inc di inc di mov ax, [ds:20] call console_putDec add di, 10 mov al, 'x' call console_putChar inc di inc di xor ah, ah mov al, [ds:25] call console_putDec add di, 8 mov al, [ds:0] test al, 0x80 jz vesa_showmodeinfo_done mov al, 'L' call console_putChar mov al, 'F' call console_putChar mov al, 'B' call console_putChar inc di inc di mov ebx, [ds:40] mov eax, ebx shr eax, 24 call puthex2 mov eax, ebx shr eax, 16 call puthex2 mov al, bh call puthex2 mov al, bl call puthex2 vesa_showmodeinfo_done: pop ds pop es popa ret ;------------------------------------------------------ checkmode: push bx push cx push dx push es push ds push di push si mov cx, ax ;cx=modenumber mov ax, VESA_MODEINFO_SEG mov es, ax xor di, di mov ax, 0x4F01 int 0x10 call checkvesa xor di, di ;es:di -> ModeInfoBlock struc mov ax, [es:di] ;ModeAttributes test al, 1 ;mode supported jz vesa_modenogood test al, 8 ;color mode jz vesa_modenogood test al, 0x10 ;graphics mode jz vesa_modenogood test al, 0x80 ;Linear Frame Buffer supported jz vesa_modenogood mov al, [es:di+25] ;BitsPerPixel cmp al, 16 jz vesa_bppok cmp al, 24 jz vesa_bppok cmp al, 32 jnz vesa_modenogood vesa_bppok: mov ax, [es:di+18] ;XResolution mov bx, [es:di+20] ;YResolution cmp ax, 640 ;640x480 jnz res_goon1 cmp bx, 480 jnz vesa_modenogood jmp vesa_modegood res_goon1: cmp ax, 800 jnz res_goon2 cmp bx, 600 jnz vesa_modenogood jmp vesa_modegood res_goon2: cmp ax, 1024 jnz res_goon3 cmp bx, 768 jnz vesa_modenogood jmp vesa_modegood res_goon3: cmp ax, 1280 jnz res_goon4 cmp bx, 1024 jz vesa_modegood cmp bx, 960 jz vesa_modegood jmp vesa_modenogood res_goon4: cmp ax, 1600 jnz vesa_modenogood cmp bx, 1200 jnz vesa_modenogood vesa_modegood: pop si pop di pop ds pop es pop dx pop cx pop bx xor ax, ax ret vesa_modenogood: pop si pop di pop ds pop es pop dx pop cx pop bx mov ax, 0xffff ret ;------------------------------------------------------ vesa_done: ; xor ax, ax ;wait for keypress... ; int 0x16 jmp go_pm ;------------------------------------------------------ puts: lodsb or al, al jz puts_done stosb mov al, ah stosb jmp puts puts_done: ret ;------------------------------------------------------ checkvesa: cmp ax, 0x004F jnz vesaerror ret vesaerror: mov ax, 0xb800 mov es, ax xor ax, ax mov ds, ax mov si, txt_vesaerror mov di, 160*24 mov ah, 4 call puts cli hlt ;-------Function console_putDec ;input: ; AX = number to display ;output: ; number written in decimal to es:di console_putDec: pusha xor dx, dx xor bh, bh ;no characters written yet mov cx, 10000 div cx ;ax=quotiont, dx=remainder add ax, '0' cmp ax, '0' je .goon1 call console_putChar mov bh, 1 .goon1: mov ax, dx ;load remainder to ax xor dx, dx mov cx, 1000 div cx ;ax=quotiont, dx=remainder add ax, '0' cmp ax, '0' je .goon11 call console_putChar mov bh, 1 jmp .goon2 .goon11: cmp bh, 0 je .goon2 call console_putChar .goon2: mov ax, dx ;load remainder to ax xor dx, dx mov cx, 100 div cx ;ax=quotiont, dx=remainder add ax, '0' cmp ax, '0' je .goon21 call console_putChar mov bh, 1 jmp .goon3 .goon21: cmp bh, 0 je .goon3 call console_putChar .goon3: mov ax, dx ;load remainder to ax xor dx, dx mov cx, 10 div cx ;ax=quotiont, dx=remainder add ax, '0' cmp ax, '0' je .goon31 call console_putChar mov bh, 1 jmp .goon4 .goon31: cmp bh, 0 je .goon4 call console_putChar .goon4: ;here dx contains last remainder mov ax, dx add ax, '0' call console_putChar popa ret ;------------------------------------------------------ console_putChar: stosb mov al, 7 stosb ret ;------------------------------------------------------ puthex: ;es:di points to video memory ;al holds hex value push ax mov ah, al shr ax, 4 and al, 0x0F add al, '0' cmp al, '9' jle puthex_goon1 add al, 'A'-'9'-1 puthex_goon1: cmp al, '0' jz puthex_skipzero stosb mov al, 7 stosb puthex_skipzero: pop ax push ax and al, 0x0F add al, '0' cmp al, '9' jle puthex_goon2 add al, 'A'-'9'-1 puthex_goon2: stosb mov al, 7 stosb pop ax ret ;------------------------------------------------------ puthex2: ;es:di points to video memory, always displays 2 characters! ;al holds hex value push ax mov ah, al shr ax, 4 and al, 0x0F add al, '0' cmp al, '9' jle puthex2_goon1 add al, 'A'-'9'-1 puthex2_goon1: stosb mov al, 7 stosb pop ax push ax and al, 0x0F add al, '0' cmp al, '9' jle puthex2_goon2 add al, 'A'-'9'-1 puthex2_goon2: stosb mov al, 7 stosb pop ax ret ;------------------------------------------------------ txt_welcome: db " HOS bootloader v", VERSION, "! ", 0 txt_rd1: db "1. Do not load an initial ram disk", 0 txt_rd2: db "2. Load initial ram disk from boot media", 0 txt_input: db "Enter your selection: ", 0 txt_vesa: db "VESA version: ", 0 txt_vesaerror: db "VESA function call error! Halting system!", 0 txt_novesa: db "VESA not found. Starting in console mode...", 0 txt_vesaold: db "VESA version 2.0 required. Starting in console mode...", 0 txt_consolemode: db "a. Console mode only.", 0 txt_memerror: db "Extended Memory Map information unavailable! Halting system...", 0 ;------------------------------------------------------ getCHSfromCluster: ;input: ax=lba of sector on floppy (0-2879) add ax, 31 ;convert logical cluster# to lba# xor dx, dx ;lba->chs mov bx, 18 div bx inc dx mov cl, dl ;sector# (1-18) xor dx, dx mov bx, 2 div bx mov ch, al ;cylinder# (0-79) mov dh, dl ;head# (0-1) ret ;------------------------------------------------------- gdtr: dw gdt_end-gdt-1 dd gdt gdt: dd 0 dd 0 KERNEL_CODE equ $-gdt db 0xff ;limit 7:0 db 0xff ;limit 15:8 db 0x00 ;base 7:0 db 0x00 ;base 15:8 db 0x00 ;base 23:16 db 0x9a ;access db 0xcf ;flags / limit 19:16 db 0x00 ;base 31:24 KERNEL_DATA equ $-gdt db 0xff ;segment 16 = 4gb data db 0xff db 0x00 db 0x00 db 0x00 db 0x92 db 0xcf db 0x00 gdt_end: ;------------------------------------------------------- gdtr32: dw gdt_end32-gdt32-1 dd gdt32 gdt32: dd 0 dd 0 ;a base of 0x4000_0000, when added to 0xC000_0000 will produce 0x0000_0000 physical before paging in effect KERNEL_CODE32 equ $-gdt32 db 0xff ;limit 7:0 db 0xff ;limit 15:8 db 0x00 ;base 7:0 db 0x00 ;base 15:8 db 0x00 ;base 23:16 db 0x9a ;access db 0xcf ;flags / limit 19:16 db 0x40 ;base 31:24 KERNEL_DATA32 equ $-gdt32 db 0xff ;segment 16 = 4gb data db 0xff db 0x00 db 0x00 db 0x00 db 0x92 db 0xcf db 0x40 gdt_end32: ;------------------------------------------------------ go_pm: xor ax, ax mov ds, ax lgdt [gdtr] cli mov eax, cr0 inc eax mov cr0, eax jmp KERNEL_CODE:pmode bits 32 pmode: lgdt [gdtr32] jmp KERNEL_CODE32:pmode_offsetted+0xC0000000 pmode_offsetted: mov ax, KERNEL_DATA32 mov es, ax mov ds, ax mov fs, ax mov gs, ax jmp KERNEL_CODE:BOOT_KERNEL_ADD+0xC0000000 kernel: db "KERNEL BIN", 0