flat assembler
Message board for the users of flat assembler.

Index > OS Construction > How does bootloader call kernel stub?

Author
Thread Post new topic Reply to topic
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
Hi, as I progress I found myself a totally newbie- I am trying to create a basic OS kernel.

I do not understand how bootloader calls kernel stub?

For example, in the following code: (I refer to someone's code)
Code:
CODE_SEG equ gdt_code - gdt_start
.....
.....
load32:
    mov eax, 1          ; starting sector
    mov ecx, 100        ; total number of sectors
    mov edi, 0x0100000 ;1 meg
    call ata_lba_read
    jmp CODE_SEG:0x0100000    


Where is the 0x0100000?

His kernel stub has following line:

Code:
    mov ebp, 0x00200000
    mov esp, ebp
    


So I guess his 0x0100000 and 0x00200000 must have something to do with the memory location of kernel stub??
Post 10 Oct 2021, 05:04
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18201
Location: In your JS exploiting you and your system
revolution
Data from the disk is loaded to 0x0100000 and it then blindly jumps to the first address.

The stack is blindly placed at 0x00200000, and assumes the memory is already mapped. I guess the bootloader has properly mapped all the memory pages so it all works.
Post 10 Oct 2021, 05:24
View user's profile Send private message Visit poster's website Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
revolution wrote:
Data from the disk is loaded to 0x0100000 and it then blindly jumps to the first address.

The stack is blindly placed at 0x00200000, and assumes the memory is already mapped. I guess the bootloader has properly mapped all the memory pages so it all works.


Thanks for pointing this out.

I still have doubt, suppose I have two binary image:
1) boot.img
2) kernel.img

How do I transfer call to kernel.img from boot.img? If I understand correctly, boot.img is at sector 0, and kernel.img is at sector 1? Or above 1MB is protected mode?

I try to combine both boot.asm and kernel.asm together, but the VM keeps booting itself.

Looks like I am missing something obvious here.[/code]
Post 10 Oct 2021, 06:16
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18201
Location: In your JS exploiting you and your system
revolution
If boot.img is one sector, then it's job is to load all sectors of kernel.img to somewhere in memory, then jump to the first address where it loaded kernel.img.

You showed this in the first post where the code loads 100 sectors to 0x0100000 and jump to it.
Post 10 Oct 2021, 06:19
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 144
Location: Russia
macomics
revolution wrote:
If boot.img is one sector, then it's job is to load all sectors of kernel.img to somewhere in memory, then jump to the first address where it loaded kernel.img.

You showed this in the first post where the code loads 100 sectors to 0x0100000 and jump to it.
and reads sectors from the disk starting from the 1st. (e.g. 1, 2, ..., 101), respectively by addresses (1 to 0x00100000, 2 to 0x001000200, ..., 101 to 0x00100C800) it turns out a linear block of memory read from disk - 0x0010000 - 0x00100C9FF
Post 10 Oct 2021, 08:16
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
revolution wrote:
If boot.img is one sector, then it's job is to load all sectors of kernel.img to somewhere in memory, then jump to the first address where it loaded kernel.img.

You showed this in the first post where the code loads 100 sectors to 0x0100000 and jump to it.


Thanks.

macomics wrote:

and reads sectors from the disk starting from the 1st. (e.g. 1, 2, ..., 101), respectively by addresses (1 to 0x00100000, 2 to 0x001000200, ..., 101 to 0x00100C800) it turns out a linear block of memory read from disk - 0x0010000 - 0x00100C9FF


Thanks.

Hi , I changed the code, I use Int 13h to read the first two sectors (512-byte boot.img and 512-byte kernel.img combined).

An inconsistent behavior is observed,

Code:
    mov ax,1        ;start sector
    mov bx, 0x7c00  ;offset of buffer
    mov cx,2        ;number of sectors (512 bytes each)
    xor dx, dx

    call _load   ; load some sectors from disk to a buffer in memory
    jmp 0:0x7e00    ;2nd stage    


Sometimes, I get the Welcome message (successful) but most of the times I don't .

If it succeeded, BIOS will print 4-digit hex value (as shown in one of the screenshot).

I am not sure which segment:offset I should call the second-stage module after reading from disk?
Code:
jmp 0:0x7c00+512  ; or 0x7e00
    


Is this correct?


Description: Failed second-stage
Filesize: 9.85 KB
Viewed: 355 Time(s)

Screenshot from 2021-10-10 17-12-25.png


Description: Successful second-stage
Filesize: 10.09 KB
Viewed: 355 Time(s)

Screenshot from 2021-10-10 17-01-46.png


Post 10 Oct 2021, 09:21
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18201
Location: In your JS exploiting you and your system
revolution
The BIOS has already loaded the first sector (your boot sector) to 0:7c00 so if you overwrite that address block then bad things will happen. Your boot code is already running at that address.
Post 10 Oct 2021, 09:26
View user's profile Send private message Visit poster's website Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
revolution wrote:
The BIOS has already loaded the first sector (your boot sector) to 0:7c00 so if you overwrite that address block then bad things will happen. Your boot code is already running at that address.


Thank you so much, it saves my effort!!

Code:
    mov ax,1        ;start sector
    mov bx, 0x7e00  ;offset of buffer
    mov cx,1        ;number of sectors (512 bytes each)
    xor dx, dx

    call _load
    jmp 0:0x7e00    ;2nd stage    


Now it works wonder. (But I can only read one sector, otherwise error )

Very Happy
Post 10 Oct 2021, 09:33
View user's profile Send private message Visit poster's website Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
I think I'll just paste it on here as backup (still no value to publish on GitHub):

Some of the code were taken (and modified a little) from Redox OS GitLab repo (bootsector.asm and print.asm):

Code:
;
; An experimental bootloader for x86 with second stage
; by FlierMate (Oct 10, 2021)
;
; With reference to Redox OS bootsector.asm
;
format binary as 'img'
org 7C00h

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00
    sti

    mov [disk], dl
    mov byte [0b8000h],'W'
    mov byte [0b8000h+1],10
    mov byte [0b8000h+2],'o'
    mov byte [0b8000h+3],10
    mov byte [0b8000h+4],'w'
    mov byte [0b8000h+5],10
    mov byte [0b8000h+6],32
    mov byte [0b8000h+7],7
    mov byte [0b8000h+8],'O'
    mov byte [0b8000h+9],10
    mov byte [0b8000h+10],'S'
    mov byte [0b8000h+11],10  
    mov byte [0b8000h+12],32
    mov byte [0b8000h+13],7    
    mov byte [0b8000h+14],32
    mov byte [0b8000h+15],7  
    
    mov ax,1        ;start sector
    mov bx, 0x7e00  ;offset of buffer
    mov cx,1        ;number of sectors (512 bytes each)
    xor dx, dx

    call _load
    jmp 0:0x7e00    ;2nd stage
     
;Reference: Redox OS bootsector.asm

; load some sectors from disk to a buffer in memory
; buffer has to be below 1MiB
; IN
;   ax: start sector
;   bx: offset of buffer
;   cx: number of sectors (512 Bytes each)
;   dx: segment of buffer
; CLOBBER
;   ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)

;Reference: Redox OS bootsector.asm
_load:
    mov dword [DAPACK.addr], eax
    mov [DAPACK.buf], bx
    mov [DAPACK.count], cx
    mov [DAPACK.seg], dx

    call print_dapack

    mov dl, [disk]
    mov si, DAPACK
    mov ah, 0x42
    int 0x13
    jc error
    ret

;Reference: Redox OS bootsector.asm    
print_dapack:
    mov bx, [DAPACK.addr + 2]
    call print_hex
    mov bx, [DAPACK.addr]
    call print_hex
    mov al, ' '
    call print_char
    mov bx, [DAPACK.count]
    call print_hex
    mov al, ' '
    call print_char
    mov bx, [DAPACK.seg]
    call print_hex
    mov al, ' '
    call print_char
    mov bx, [DAPACK.buf]
    call print_hex
    ret

;Reference: Redox OS bootsector.asm
; print a character
print_char:
    pusha
    mov bx, 7
    mov ah, 0x0e
    int 0x10
    popa
    ret

;Reference: Redox OS bootsector.asm
; print a number in hex
print_hex:
    mov cx, 4
.lp:
    mov al, bh
    shr al, 4
    cmp al, 0xA
    jb .below_0xA
    add al, 'A' - 0xA - '0'
.below_0xA:
    add al, '0'
    call print_char
    shl bx, 4
    loop .lp
    ret    
    
error:
    mov byte [0b8000h+2000],'E'
    mov byte [0b8000h+2001],12
    mov byte [0b8000h+2002],'r'
    mov byte [0b8000h+2003],12
    mov byte [0b8000h+2004],'r'
    mov byte [0b8000h+2005],12
    mov byte [0b8000h+2006],'o'
    mov byte [0b8000h+2007],12
    mov byte [0b8000h+2008],'r'
    mov byte [0b8000h+2009],12
    mov byte [0b8000h+2010],32
    mov byte [0b8000h+2011],7  
    mov byte [0b8000h+2012],32
    mov byte [0b8000h+2013],7    
    mov byte [0b8000h+2014],32
    mov byte [0b8000h+2015],7  

;Reference: Redox OS bootsector.asm
.halt:
    cli
    hlt
    jmp .halt
        
disk   db 0

;Reference: Redox OS bootsector.asm
DAPACK:
        db 0x10
        db 0
.count  dw 0 ; int 13 resets this to # of blocks actually read/written
.buf    dw 0 ; memory buffer destination address (0:7c00)
.seg    dw 0 ; in memory page zero
.addr   dw 0 ; put the lba to read in this spot
.addr2  dw 0
                   
db 7C00h+510-$ DUP (0)
dw 0AA55h

;db 0x0100000-($ - $$) DUP (0)

;
; 2nd sector
;

start2:
    ;Reference: Redox OS bootsector.asm
    ; enable A20-Line via IO-Port 92, might not work on all motherboards
    in al, 0x92
    or al, 2
    out 0x92, al

    ;call kernel_main
    
    mov byte [0b8000h+2000],'W'
    mov byte [0b8000h+2001],10
    mov byte [0b8000h+2002],'e'
    mov byte [0b8000h+2003],10
    mov byte [0b8000h+2004],'l'
    mov byte [0b8000h+2005],10
    mov byte [0b8000h+2006],'c'
    mov byte [0b8000h+2007],10
    mov byte [0b8000h+2008],'o'
    mov byte [0b8000h+2009],10
    mov byte [0b8000h+2010],'m'
    mov byte [0b8000h+2011],10  
    mov byte [0b8000h+2012],'e'
    mov byte [0b8000h+2013],10    
    mov byte [0b8000h+2014],32
    mov byte [0b8000h+2015],7  
    jmp $
          
db 512-($ - start2) DUP (0)

    


Description: Successful second-stage loaded to 0x7e00
Filesize: 9.98 KB
Viewed: 346 Time(s)

Screenshot from 2021-10-10 17-42-03.png


Post 10 Oct 2021, 09:45
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 144
Location: Russia
macomics
Code:
    mov byte [0b8000h],'W'
    mov byte [0b8000h+1],10
    mov byte [0b8000h+2],'o'
    mov byte [0b8000h+3],10
    mov byte [0b8000h+4],'w'
    mov byte [0b8000h+5],10
    mov byte [0b8000h+6],32
    mov byte [0b8000h+7],7
    mov byte [0b8000h+8],'O'
    mov byte [0b8000h+9],10
    mov byte [0b8000h+10],'S'
    mov byte [0b8000h+11],10  
    mov byte [0b8000h+12],32
    mov byte [0b8000h+13],7    
    mov byte [0b8000h+14],32
    mov byte [0b8000h+15],7
...
    mov byte [0b8000h+2000],'E'
    mov byte [0b8000h+2001],12
    mov byte [0b8000h+2002],'r'
    mov byte [0b8000h+2003],12
    mov byte [0b8000h+2004],'r'
    mov byte [0b8000h+2005],12
    mov byte [0b8000h+2006],'o'
    mov byte [0b8000h+2007],12
    mov byte [0b8000h+2008],'r'
    mov byte [0b8000h+2009],12
    mov byte [0b8000h+2010],32
    mov byte [0b8000h+2011],7  
    mov byte [0b8000h+2012],32
    mov byte [0b8000h+2013],7    
    mov byte [0b8000h+2014],32
    mov byte [0b8000h+2015],7  
...
    mov byte [0b8000h+2000],'W'
    mov byte [0b8000h+2001],10
    mov byte [0b8000h+2002],'e'
    mov byte [0b8000h+2003],10
    mov byte [0b8000h+2004],'l'
    mov byte [0b8000h+2005],10
    mov byte [0b8000h+2006],'c'
    mov byte [0b8000h+2007],10
    mov byte [0b8000h+2008],'o'
    mov byte [0b8000h+2009],10
    mov byte [0b8000h+2010],'m'
    mov byte [0b8000h+2011],10  
    mov byte [0b8000h+2012],'e'
    mov byte [0b8000h+2013],10    
    mov byte [0b8000h+2014],32
    mov byte [0b8000h+2015],7        

I do not know who wrote it, but after loading you are in real mode, and even more so after
Code:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax    
the limit of all data segments should be reset to 0x0000FFFF. Therefore, on properly functioning equipment, commands to access addresses further than 0x00010000 in real mode will not give anything. If you want to access the video buffer, then its base segment address (0xB0000) should be loaded into one of the segment registers. Access addresses within 64 kb (0x8000; or segment 0xB800 and offset 0x0000).

In order to avoid such errors, it is worth starting by studying the operation of processors in various memory addressing modes.

P. S. And each of the commands using direct 32-bit addressing is very wasteful to the extremely limited space of the boot sector
Code:
mov byte [0b8000h+2000],'W' ; 5 byte each
; total: 155 bytes or 7 bytes prepare + 3 + 120 bytes all = 130 bytes total
push 0xB800 ; 3 byte single
pop ds     ; 1 byte single
mov bx, 2000 ; 3 byte single
mov [ds:bx+0],"W" ; 3 byte single
mov [ds:bx+1],10 ; 4 byte each
; or 12 bytes + 10 bytes + 13 bytes = 35 bytes total
  mov si, msg0 ; 3 byte double
  call tty_write ; 3 byte double
  @@:
  mov ah, 14 ; 2 byte single
  int 16       ; 2 byte single
tty_write:
  lodsb      ; 1 byte single
  test al, al  ; 2 byte single
  jnz @b   ; 2 byte single
  retn          ; 1 byte single

msg0 db 'Wow OS',0 ; 7 bytes single
msg1 db 'Error', 0 ; 6 bytes single

; or 6 bytes + 12 bytes + 7 bytes + 13 bytes = 39 bytes
  push 0xB800 ; 3 bytes single
  pop es        ; 1 byte single
  xor di, di      ; 2 bytes single
  mov ah, 7
  mov si, msg0 ; 3 bytes double
  call write_buf  ; 3 bytes double

@@:
  stosw     ; 1 byte
 write_buf:
  lodsb        ; 1 byte
  test al, al  ; 2 bytes
  jnz @b  ; 2 bytes
  retn     ; 1 byte

msg0 db 'Wow OS',0 ; 7 bytes single
msg1 db 'Error', 0 ; 6 bytes single    


Last edited by macomics on 10 Oct 2021, 14:07; edited 1 time in total
Post 10 Oct 2021, 13:23
View user's profile Send private message Reply with quote
FlierMate



Joined: 21 Jan 2021
Posts: 126
FlierMate
macomics wrote:

I do not know who wrote it, but after loading you are in real mode, and even more so after


I saw it also on this forum section (https://board.flatassembler.net/topic.php?t=21367) , but maybe edfed's code was executed after enabling protected mode? (I am still newbie to OS development)

macomics wrote:

the limit of all data segments should be reset to 0x0000FFFF. Therefore, on properly functioning equipment, commands to access addresses further than 0x00010000 in real mode will not give anything.


I think that's 1MB limit of real mode.

macomics wrote:

If you want to access the video buffer, then its base segment address (0xB0000) should be loaded into one of the segment registers. Access addresses within 64 kb (0x8000; or segment 0xB800 and offset 0x0000).


Good to know this. I didn't know that 0xB8000 is equivalent to B800:0.

macomics wrote:

In order to avoid such errors, it is worth starting by studying the operation of processors in various memory addressing modes.


Part of me always want to experiment first, i.e. learn by doing. Razz

macomics wrote:

P. S. And each of the commands using direct 32-bit addressing is very wasteful to the extremely limited space of the boot sector
Code:
mov byte [0b8000h+2000],'W' ; 5 byte each
; total: 151 bytes or 7 bytes prepare + 45 bytes all = 52 bytes total
push 0xB800 ; 3 byte single
pop ds     ; 1 byte single
mov bx, 2000 ; 3 byte single
mov [ds:bx+0],"W" ; 3 byte each    


That's nice. I did this in DOS programming, but somehow when come to OS development (real and protected mode), my focus is on something else. And I also thought I could optimize the code later.

Thank you, macomics, for your frank comments, I like your correction.

I still have another important question, how do I call kernel stub that is not in the same binary image?

E.g. I plan to mix Assembly (bootsector) and Pascal (kernel). How do I transfer call from bootloader to OS kernel written in HLL?
I refer to https://wiki.osdev.org/Pascal_Bare_Bones, but it doesn't explain how to move from bootloader to kernel stub, though, I found the linker script:

Code:
ENTRY(kstart)
SECTIONS
{
  .text  0x100000 :
  {
    text = .; _text = .; __text = .;
    *(.text)
    . = ALIGN(4096);
  }
  .data  :
  {
    data = .; _data = .; __data = .;
    *(.data)
....
....
    


Do you know how to do this using FASM? I see it reserves 1MB for real mode at the beginning, and I guess the Pascal kernel is right after that?

Sorry for asking noob question.
Post 10 Oct 2021, 14:05
View user's profile Send private message Visit poster's website Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 144
Location: Russia
macomics
FlierMate wrote:
I saw it also on this forum section (https://board.flatassembler.net/topic.php?t=21367) , but maybe edfed's code was executed after enabling protected mode? (I am still newbie to OS development)

...

I think that's 1MB limit of real mode.
1 MB is the maximum possible addressable memory space in real addressing mode. The 16-bit segment address is shifted by 4 digits and a 16-bit offset is added to it. A total of 20 bits of addressing. 0x000FFFF0 + 0x0000FFFF = 0x0010FFEF is the maximum addressable address, but for compatibility, A20 was invented - limiting physical addresses to 0x000FFFFF.

ADD:
Code:
.code=$-gdt                     ; first entry in gdt (8*1)
dw -1,0                         ;   4Gbytes, start at linear 0
db 0,9ah,0cfh,0                 ;   granularity = 64Kbytes, code segment, ring 0, read only,etc...
.data=$-gdt                     ; second entry in gdt (8*2)
dw -1,0                         ;   4Gbytes, start at linear 0
db 0,92h,0cfh,0                 ;   granularity = 64Kbytes, data segment, ring 0, read/write,etc...    
Of course. He quickly organizes himself a selector table describing two simple 4 Gb segments for code and data. And
Code:
 lgdt fword [gdt.size]    
sets its address for the processor before switching to protected mode.


Last edited by macomics on 10 Oct 2021, 14:51; edited 3 times in total
Post 10 Oct 2021, 14:13
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 144
Location: Russia
macomics
FlierMate wrote:
I still have another important question, how do I call kernel stub that is not in the same binary image?
And here it is impossible to do without studying the output format generated by Pascal. To transfer control, you need to find an address in memory that will correspond to the address of the entry point specified in the program header. But if you use COM then everything is somewhat simplified. It is enough to call an address with offset 0x0100 and the same (loading) segment.
Post 10 Oct 2021, 14:20
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 144
Location: Russia
macomics
Judging by the code already available in this topic
Code:
load32:
    mov eax, 1          ; starting sector
    mov ecx, 100        ; total number of sectors
    mov edi, 0x0100000 ;1 meg
    call ata_lba_read
    jmp CODE_SEG:0x0100000 
...
mov ebp, 0x00200000
    mov esp, ebp    

it is enough to simply load the code to the address 1 MB and transfer control to it. But here
Code:
 in al, 0x92
or al, 2
out 0x92, al     
I would put before transferring control to addresses exceeding the limit of 1 Mb,
Code:
  mov edi, 0x0100000 ;1 meg
...
jmp CODE_SEG:0x0100000     
because A20 just restricts the processor in accessing such addresses and this restriction is implemented on external hardware.
Post 10 Oct 2021, 14:28
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.