I have a 2 stage bootloader and a c kernel function. I am trying to do this without using an initial file system.
The problem is that it's not making it to the second stage for entry and also not making it to the c function or the kernel. I put a char test in both the first and second stage, in order to test it by output, but its not printing the second stage char.
This lets me know there is an issue with the jmp from the first to the second stage because its not even making it to the first label in the second stage. There is also a print test in the c kernel function named _core and its not printing output either. Which of course it would not work, because control is lost before the first label in the second stage, so there's no reason to believe it would make it to the kernel call which is after.
The first stage is a regular .bin file and since I used the extern directive, the second stage, is an .o file. I separated them like its suppose to but I am not sure how everything is suppose to connect.
I mean the osboot.asm is getting assembled alone so there doesn't seem to be a connection to the linker, even doe I have a jmp problem, with no address to jmp to. Then how does the linker work, with the second stage .o file and the c file .o together, in order to call the kernel? What is the proper way of handling this?
I am assume the linker should be providing all the addresses needed.
osboot.asm - first stage
[BITS 16]
org 0x7C00
osboot:
mov ah, 0x0e ; BIOS teletype function
mov al, 'B' ; Character to print
int 0x10 ; Call BIOS interrupt
call read_osload
jmp 0:0x7e00
%include "read_osload.inc"
times 510-($-$$) db 0 ; Limit the sector to 510 bytes
dw 0xAA55 ; Boot signature and last 2 bytes of the first sector
osload.asm - second stage
[BITS 16]
section .osload
global _start
_start: jmp switch_pmode
%include "gdt.inc"
switch_pmode:
mov ah, 0x0e ; BIOS teletype function
mov al, 'L' ; Character to print
int 0x10 ; Call BIOS interrupt
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp code_seg:p_mode
[BITS 32]
p_mode:
mov ax, cs
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
[EXTERN _core]
call _core
hlt
core.c - kernel
void _core()
{
char* video_memory = (char*)0xb8000;
video_memory[0] = 'E';
video_memory[1] = 'L';
video_memory[2] = 'L';
video_memory[3] = 'E';
video_memory[4] = 'O';
video_memory[5] = 'S';
}
read_osload.inc
[BITS 16]
read_osload:
mov ah, 0 ; reset the drive function
mov dl, 0 ; drive 0 is the floppy drive
int 0x13 ; call the BIOS interrupt to reset drive
mov ax, 0x7E00 ; read address 0x7E00 so we can jmp to the second stage
mov es, ax
xor bx, bx
mov ah, 0x02 ; read sector function
mov al, 1 ; read 1 sector
mov ch, 0 ; 0 = track 1 (18 sectors per track) no need to move from track 0
mov cl, 2 ; read sector 2
mov dh, 0 ; head number
mov dl, 0 ; drive number. drive 0 is the floppy drive
int 0x13 ; call the BIOS interrupt to read drive
ret
gdt.inc
; null segment descriptor
gdt_start:
dq 0x0
; code segment descriptor
gdt_code:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x08 ; segment base, bits 24-31
; data segment descriptor
gdt_data:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10010010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x10 ; segment base, bits 24-31
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit)
dd gdt_start ; address (32 bit)
code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start
link.ld
ENTRY(_core)
OUTPUT_FORMAT("binary")
OUTPUT(core.bin)
SECTIONS
{
. = 0x7C00; /* osboot.asm starts at 0x7C00*/
.osboot : AT(0x7C00)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
. = 0x7E00; /* osload.asm starts at 0x7E00? we need a place to jmp to from osboot */
.osload : AT(0x7E00)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
. = 0x1000; /* core.c starts at 0x1000? we need a place to call kernel _core */
._core : AT(0x1000)
{
*(.text)
*(.rodata)
*(.data)
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
kernel_sectors = (SIZEOF(._core) + 511) / 512;
. = ALIGN(4); /* Ensure alignment between sections */
/DISCARD/ : {
*(.eh_frame)
}
}
makefile
all: elle.flp
osboot.bin: osboot.asm
nasm -f bin osboot.asm -o osboot.bin
osload.o: osload.asm
nasm osload.asm -f elf32 -o osload.o
core.o: core.c
gcc -m32 -fno-pie -Wall -ffreestanding --no-builtin -c core.c -o core.o
core.bin: core.o osload.o
ld -T link.ld -melf_i386 -o core.bin core.o osload.o
elle.flp: osboot.bin core.bin
cat osboot.bin core.bin > elle.flp
clean:
rm -f osboot.bin osload.o core.o load_core.o core.bin elle.flp
I have tried altering the link.ld in many different ways. I have tried altering the make file in many ways. I've tried compiling the osload.o and core.o, separately then combining them into a .bin, before linking them. I've tried compiling the osload.o and core.o separately then linking them separately.
It is possible to arrange the code using the linker script and have it place the boot signature in the appropriate place. The following linker script with some cleaned up code to fix a variety of bugs is below and assumes the kernel is loaded starting at physical address 0x8000. There were also issues with:
link.ld
:
ENTRY(_core)
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x7C00; /* osboot.asm starts at 0x7C00*/
.osboot : AT(0x7C00) SUBALIGN(4)
{
osboot.o(.text)
osboot.o(.*)
}
. = 0x7DFE;
.bootsig : {
SHORT(0xaa55);
}
. = 0x7E00; /* osload.asm starts at 0x7E00? we need a place to jmp to from osboot */
.osload : AT(0x7E00) SUBALIGN(4)
{
osload.o(.text)
osload.o(.*)
}
. = 0x8000; /* core.c starts at 0x1000? we need a place to call kernel _core */
._core : SUBALIGN(4)
{
*(.text*)
*(.rodata*)
*(.data*)
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss*);
}
. = ALIGN(4); /* Round BSS up to 4 byte boundary */
__bss_end = .;
kernel_sectors = (SIZEOF(._core) + 511) / 512;
/DISCARD/ : {
*(.eh_frame)
*(.comment)
}
}
gdt.inc
:
; null segment descriptor
gdt_start:
dq 0x0
; code segment descriptor
gdt_code:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x00 ; segment base, bits 24-31
; data segment descriptor
gdt_data:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10010010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x00 ; segment base, bits 24-31
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit)
dd gdt_start ; address (32 bit)
code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start
read_osload.inc
:
[BITS 16]
read_osload:
mov ah, 0 ; reset the drive function
; mov dl, 0 ; drive in DL passed by the BIOS is the boot drive
int 0x13 ; call the BIOS interrupt to reset drive
xor ax, ax
mov es, ax
mov bx, 0x7E00 ; read address 0x7E00 so we can jmp to the second stage
mov ah, 0x02 ; read sector function
mov al, 15 ; read 15 sectors - this will need to be redone when the
; kernel gets larger
mov ch, 0 ; 0 = track 1 (18 sectors per track) no need to move from track 0
mov cl, 2 ; read sector 2
mov dh, 0 ; head number
; mov dl, 0 ; drive in DL passed by the BIOS is the boot drive
int 0x13 ; call the BIOS interrupt to read drive
ret
osboot.asm
:
[BITS 16]
extern stage2
osboot:
xor ax, ax ; Initialize seg regsisters to 0 since
; linker script will be placing bootloader at
; org 0x7c00
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00 ; Place stack just below bootloader
; at 0x0000:0x7c00
mov ah, 0x0e ; BIOS teletype function
mov al, 'B' ; Character to print
int 0x10 ; Call BIOS interrupt
call read_osload
jmp 0:stage2
%include "read_osload.inc"
osload.asm
:
[BITS 16]
global stage2
stage2: jmp switch_pmode
%include "gdt.inc"
switch_pmode:
mov ah, 0x0e ; BIOS teletype function
mov al, 'L' ; Character to print
int 0x10 ; Call BIOS interrupt
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp code_seg:p_mode
[BITS 32]
p_mode:
mov ax, data_seg
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
[EXTERN _core]
call _core
hlt
core.c
:
void _core()
{
volatile char* video_memory = (volatile char*)0xb8000;
video_memory[0] = 'E';
video_memory[2] = 'L';
video_memory[4] = 'L';
video_memory[6] = 'E';
video_memory[8] = 'O';
video_memory[10] = 'S';
}
Makefile
:
all: elle.flp
osboot.o: osboot.asm
nasm -f elf32 osboot.asm -o osboot.o
osload.o: osload.asm
nasm osload.asm -f elf32 -o osload.o
core.o: core.c
gcc -m32 -fno-pie -Wall -ffreestanding --no-builtin -c core.c -o core.o
core.bin: core.o osload.o osboot.o
ld -T link.ld -melf_i386 -o core.elf core.o osload.o osboot.o
objcopy -O binary core.elf core.bin
elle.flp: core.bin
dd if=/dev/zero of=elle.flp bs=1K count=1440
dd if=core.bin of=elle.flp conv=notrunc
clean:
rm -f osboot.o osload.o core.o load_core.o core.bin elle.flp core.elf