i started to make my os some 1 year ago, it's already at a fairly advanced stage, it can handle pci, usb (only hid for now), vesa/vga, image decompression and filtering, my own ABI that support dynamic linking, audio drivers and network througth ndis interface, file system basics, memory allocation and ttf font and it already has a nice set of feature for the moment
it use protected mode with segment, not the virtual paging mode, like this i can have physical address= virtual address at least for the kernel space, which can be rather handy to handle dma transfer, security is not a big issue for me for the moment, and i can always handle the old fahsioned multi tasking with segment/lgdt if i need securised process handling
now the first thing i'd ask is on the boot loader stage, i'm sure there are plenty of good article about on there, and i already have browsed this forum and the wiki for quite some time and didn't find a general outlook on this
the thing is i wonder mostly is how to handle properly the first stage of image loading, i'd rather not use a second stage boot loader, and for the moment the boot loader parse all drive and read the kernel from an iso file system present on one of the drive the bios can access , then load the kernel in memory
the problem is that the kernel then need to access the filesystem from the protected mode, and it's here i'm a bit wondering how to do this properly without if possible a second stage boot loader
for now the kernel use the bios int13 function from protected mode to load the iso image from the disk in memory, the whole iso is about 1Mo, and it's read at the beginning of the kernel loading process, so it's not big deal to load it entierly as a ram disk using bios int 13, the boot loader handle lba/chs mode with the good sector size, and there is no interupt or anything going on at this stage, technically the bootloader can boot from a floppy and autodetect the iso on another floppy or cdrom drive, but it's still too big to be loaded entierly from the real mode in the first stage boot loader, for now it just load the kernel from it and then jump to it
another option could be to do like in linux, to include all the necessary drivers files as binaries in the kernel to be loaded directly from the boot stage, and then initializing the devices needed to handle the disk drives, but currently the bus device drivers loading system use the file system to scan a folder to find all the drivers for a particular bus (usb, pci etc), and it's not designed to be able to use drivers included as binaries in the kernel, or maybe a reduced subset of drivers present on the iso could be loaded somewhere in the real mode memory before the switch to protected mode, but it would need some kind of organisation to be able to match the drivers loaded like this with a bus type, which is not really handled for the moment, as the bus drivers just scan the folder of the file system to find all drivers, and even so it's not that much a good solution if i need to load all the kernel directly from the first stage boot loader as size is an issue, even if for now the whole kernel is about 400k un compressed (including the libc, zlib and a lib for base system things ,it can load dynamically linked exectuable with my own ABI), and i guess the whole thing of the bus mannager, pci and usb bus drivers, and a reduced subset of device drivers like uhci/ohci/ata could fit in the real mode memory, but i'd rather avoid to have drivers included directly as binaries in the kernel
i'd rather avoid a second stage boot loader for many reason, because it make duplicate for all the code for device scan, because second stage boot loader are not that much reliable in case disk change of address or other, i don't really like the way grub and lilo works, and i don't want to have 3 kind of boot loader like iso linux, grub or other to handle different kind of booting mode which i can already do with only a first stage boot loader, the only problem is that it can only load limited amount of data from real mode, and that the kernel then need to either use bios funcs or i need to find a system to be able to load the iso where the drivers are from a disk from a kernel that has to be loaded in real mode memory
first i wonder if there is a major drawback about using bios function from protected mode, given it's mostly to load the iso image as a ramdisk at the first stage of the kernel intialisation, that there is no relevant interuption running at that moment, i know bios can be pretty buggy, and can be also hell to debug, but there are already some pretty good docs documenting all the bugs and possible quirk for different bioses, for just basic sector reading, provided the switch to 16 bit real mode and the pic is programmed correctly before each call it shouldn't be a problem ? i have a problem with this routine on some version of virtual box and bochs, it work fine on some version of virtual box and qemu and on real hardware, so i don't know if i'm doing something wrong there, or if there is really a major reason not to use the bios int 13h func at least in the first stage, to load the base image into protected mode memory
here is the base of the code to call a bios interupt from protected mode
even if it's slow as hell, i don't really care cause it's only to be used at the very first stage, and there is nothing else going on at the same time, and it's just to load a few megs from a cdrom or usbkey or whatever other media the iso containing the drivers are on, the corresponding code segment ( 0x30 ) is set with the 16 bit flags and start at 0x10000 which is the address at which the kernel is loaded, so i can easily translate address from 32 bits protected mode to 16 bit real mode and vice versa, normally it's supposed to work and it works fine, but it doesn't seem to work on boschs and some version of virtual box for some reason
Code: Select all
%define kern_base 0x10000
%define kern_org 0x10000
%define start_seg 0x00000
%define addr_to_real_mode +start_seg
%define real_mode_to_addr -kern_org
[BITS 16]
save_eax:dd 0
test_fn_16bits:
push eax
mov eax,cr0
and ax ,~1
mov cr0, eax ; PE set to 0 (CR0)
mov ax , 0x1000
mov ds , ax
mov es , ax
pop eax
mov [save_eax real_mode_to_addr], eax
xor ax , ax
mov ss , ax
mov esp , 0xFFFF
mov eax , [save_eax real_mode_to_addr]
jmp word 0x1000:(here_bios_call real_mode_to_addr)
here_bios_call:
pushf
push word 0x1000
push word (addr_ret real_mode_to_addr)
jmp far [addr_real_mode_interupt real_mode_to_addr]
addr_ret:
pushfd
pop dword [cpu_eflags real_mode_to_addr]
mov [ret_ax real_mode_to_addr],ax
mov eax,cr0
or ax ,1
mov cr0,eax ; PE mis a 1 (CR0)
mov ax , 0x10 ; segment de donne
mov es , ax
mov ds , ax
jmp dword 0x08:(fn_here)
ret
[BITS 32]
call_real_mode_interupt_vector_c:
;pushf
;pop dword[flags_save]
cli
pusha
call _init_pic_real_mode
lidt [orig_idt]
popa
jmp 0x08:testttt
testttt:
mov [safe_esp],esp
mov esp,0xFFFF
jmp dword 0x30:(test_fn_16bits real_mode_to_addr )
fn_here:
mov ax , 0x18
mov ss , ax
mov esp , [safe_esp]
lidt [idtptr]
jmp 0x08:testtttt
testtttt:
pusha
call _init_pic
popa
push dword [cpu_eflags]
popfd
;test word[flags_save],(1<<9)
;jz bios_call_no_sti
; sti
;bios_call_no_sti:
ret
load_real_mode_interupt_vector_c:
mov ax ,0x40
mov ds ,ax
mov eax ,[esp+4] ;interupt number
shl eax ,2
add eax ,[orig_idt+2] ; (start of vector list)
mov ebx ,dword [eax]
;shl ebx ,4
;and ebx ,0x0FFFFFFF
mov ax ,0x10
mov ds ,ax
mov [addr_real_mode_interupt] ,ebx
mov [addr_real_mode_int] ,ebx
;mov esi ,text_load_i
;call _draw_cars
;mov ecx,[addr_real_mode_interupt]
;call _draw_dword_hex
;mov edx ,' '
;call _draw_car
;mov ecx,[addr_real_mode_int]
;call _draw_dword_hex
;mov edx ,10
;call _draw_car
ret
here is the code for the bootloader for now, it scan all drive first sectors to find an iso , not sure if it's 100% compliant or if there can be some bug in the way i handle some things, i just past it like that, normally it's supposed to work ok to scan an iso present on any drive the bios can handle and load the kernel from it, it's pretty tightly packed for the 512 bytes but it should do the job
it use addresses that are beyond the 512 bytes for unitinalized data, it should point to the memory area after the place where the bios loaded the bootloader , i hope it's not supposed to be an issue, it's not supposed to be loaded as part of the image, but just pointers to data in the area after the place where the bootloader has been loaded, because there is not a single byte free anymore, and everything is packed as much as i can, if someone know how to reduce the code size of this i take the more space = more room for test/check and debugging
Code: Select all
[BITS 16]
[ORG 0x0]
%define BIOS_START_ADDR 0x7C00
%define BIOS_START_SEG (BIOS_START_ADDR>>4)
start_boot:
mov ax ,BIOS_START_SEG ; 0x07C0
mov ds ,ax
mov es ,ax
mov [BOOT_DEVICE],dl
mov ax ,0x0000 ; setup stack
mov ss ,ax
mov sp ,0x1000
mov ax,03H
int 10H
;mov al,'1'
;call print_message_bios
;reset disk
xor ax ,ax
xor dx ,dx
int 13h
loop_dsk:
;-----------------------------------------
;test for lba device
;-----------------------------------------
mov ah,41h
mov dl,[BOOT_DEVICE]
mov bx,0x55AA
int 13h
jc no_ext_lba
;cmp bx,0xAA55
;jne no_ext_lba
test cx,1
jz no_ext_lba
;-----------------------------------------
;get lba sector size
;-----------------------------------------
mov ah,48h
mov dl,[BOOT_DEVICE]
mov si,data_available
int 13h
mov ax,[data_available+18h]
mov word [sector_size] ,ax
mov byte [has_lba] ,1
jmp short geom_done
no_ext_lba:
;-----------------------------------------
;chs device
;-----------------------------------------
mov word [sector_size] ,512
mov byte [has_lba] ,0
;-----------------------------------------
;get chs disk geometry
;-----------------------------------------
xor ax,ax
mov es,ax
xor di,di
mov ah,0x08
mov dl,[BOOT_DEVICE]
int 13h
jc next_disk
test cl,cl
jz next_disk
;-----------------------------------------
;store result
;-----------------------------------------
inc dh
mov [disk_nhds] ,dh
mov [disk_ncyls] ,ch
mov al,cl
and al ,0x3F
mov [disk_nsecs] ,al
shr cl ,6
mov [disk_ncyls+1] ,cl
inc word [disk_ncyls]
mov al ,dh
mul byte [disk_nsecs]
mov [disk_hdsec],ax
geom_done:
;------------------------------------------------------------
;check for iso file system at sector [iso_start]
;------------------------------------------------------------
xor ax,ax
mov word [iso_start],ax
mov cx,2
next_iso_test:
call parse_iso
test al,al
jnz iso_found
inc byte [iso_start]
loopnz next_iso_test
next_disk:
inc byte[BOOT_DEVICE]
jmp loop_dsk
iso_found:
;mov al,'2'
;call print_message_bios
mov word [jmp_addr+0] ,0x0
mov word [jmp_addr+2] ,0x1000
mov dl,[BOOT_DEVICE]
jmp far [jmp_addr]
read_disk_sec:
push es
movzx eax , word [esp+4]
add ax , [iso_start]
test byte [has_lba],1
jz read_no_lba
mov dl ,[BOOT_DEVICE]
xor ecx,ecx
push ds
push ecx
push ecx
push ecx
push dword eax
push word es
push word bx
push word 1
push word 18h
retry_lba:
mov ax , ss
mov ds , ax
mov si , sp
mov ah , 0x42 ;read sector
int 13h
jc retry_lba
add sp , 24
pop ds
jmp short read_disk_done
read_no_lba:
;LBA = ( (cylinder * heads_per_cylinder + heads ) * sectors_per_track ) + sector - 1
;cylinder = LBA / (heads_per_cylinder * sectors_per_track)
;temp = LBA % (heads_per_cylinder * sectors_per_track)
;head = temp / sectors_per_track
;sector = temp % sectors_per_track + 1
push bp
sub sp,4
mov bp,sp
xor dx,dx
div word [disk_hdsec]
mov [bp] , ax ;cylinder
mov ax , dx
div byte [disk_nsecs]
mov [bp+2] , al ;head
inc ah
mov [bp+3] , ah ;sector
;- the parameters in CX change depending on the number of cylinders;
; the track/cylinder number is a 10 bit value taken from the 2 high
; order bits of CL and the 8 bits in CH (low order 8 bits of track):
; |F|E|D|C|B|A|9|8|7|6|5-0| CX
; | | | | | | | | | | `----- sector number
; | | | | | | | | `--------- high order 2 bits of track/cylinder
; `------------------------ low order 8 bits of track/cyl number
retry_chs_read:
mov ah,2 ;read sector
mov ch,[bp] ;cyl number
mov dh,[bp+2] ;head number
mov cl,[bp+3] ;sector number
mov dl,[BOOT_DEVICE] ;drive number
mov al,1 ;read 1 sector
int 0x13
jc retry_chs_read
add sp,4
pop bp
read_disk_done:
pop es
ret 2
iso_to_hard_sec:
cmp word [sector_size],512
jne read_sec_has_2048
shl ax,2
read_sec_has_2048:
ret
parse_iso:
push cx
push es
;-----------------------------------------------------------------
;setup pointer to target memory
;-----------------------------------------------------------------
mov ax , 0x1000
mov es , ax
xor bx , bx
;-----------------------------------------------------------------
;setup stack space
;-----------------------------------------------------------------
sub sp , 16
mov bp , sp
;bp + 0 (word) => kern_file_start
;bp + 2 (dword)=> kern_file_size
;bp + 6 (word)=> cnt
;-----------------------------------------------------------------
;read file system entry
;-----------------------------------------------------------------
mov ax,16
call iso_to_hard_sec
push ax
call read_disk_sec
xor al,al
cmp word[es:1],0x4443
jne end_loop_entries
;128 = volume size ofset
;mov eax , [es:128]
;mov [volume_size] , eax
;mov [bp] , eax
;158 = root entry ofset
;mov ax , [es:158]
;shl eax , 11
;mov [ss:bp+4] , eax
;mov [root_entry_ext] , eax
;158+8 = root entry size
;mov eax ,[es:158+8]
;mov [root_entry_sz] ,eax
;mov [ss:bp+8] ,eax
;-----------------------------------------------------------------
;read root entry
;-----------------------------------------------------------------
xor bx , bx
mov ax,[es:158]
call iso_to_hard_sec
push ax
call read_disk_sec
;-----------------------------------------------------------------
;parse root childs entry
;-----------------------------------------------------------------
xor di ,di
loop_entries:
xor al,al
cmp byte [es:di], 0x00
je end_loop_entries
test byte[es:di+25],0x02
jnz not_file
;-----------------------------------------------------------------
;read file
;-----------------------------------------------------------------
mov ax , word[es:di+2]
call iso_to_hard_sec
mov [bp] , ax
mov eax , dword[es:di+10]
shr eax , 9
inc ax
mov [bp+2] , ax
mov word [bp+6] , 0
loop_load_kern:
mov dx , [bp+2]
mov ax , [bp+6]
mov bx , ax
cmp word [sector_size] , 512
jne read_kern_has_2048_sec
shr ax ,7 ;128 sectors = 1 segment (65536/512)
and bx , 0x7F ;sector ofset
shl bx , 9
jmp short read_kern_sec_done
read_kern_has_2048_sec:
shr ax ,5 ;32 sectors = 1 segment (65536/2048)
and bx , 0x1F ;sector ofset
shl bx , 11
shr dx , 2
read_kern_sec_done:
shl ax , 12 ;es = segment * 4096 (segment * (65536/16) )
add ax , 0x1000
mov es , ax
mov [bp+8] , dx
push word [bp]
call read_disk_sec
inc word [bp]
inc word [bp+6]
mov ax,word [bp+6]
cmp ax,[bp+8]
jl loop_load_kern
mov al,1
jmp short end_loop_entries
not_file:
movzx bx ,byte [es:di]
add di ,bx
jmp short loop_entries
end_loop_entries:
add sp , 16
pop es
pop cx
ret
;print_message_bios:
; mov bl,7
; mov ah,0eh
; int 10h
;ret
the_end:
TIMES 510-($-$$) DB 0
dw 0xAA55
BOOT_DEVICE :db 0
sector_size :dw 0
iso_start :dw 0
has_lba :db 0
disk_nhds :db 0
disk_nsecs :db 0
disk_ncyls :dw 0
disk_hdsec :dw 0
jmp_addr :dd 0
data_available :
TIMES 1024-($-$$) DB 0