triple-fault on jumping to kernel
Posted: Tue Aug 23, 2022 1:22 pm
I've written some simple code print to the text buffer (though I know it has some problems which I will fix later), and added it to my kernel build. I then finally tested the code to transfer control to the loaded kernel. However, as soon as it jumps, I get a triple fault, even if the terminal-handling code is unused.
There are a number of possible places where this could be failing. There could be a fault in the code which copies the kernel code; there could be a fault either the Makefile or the linker script (or both); there could be a problem with the kernel itself; there could even be a problem with how I am transferring control. I will try to post all of the relevant code here, though if I missed anything, I can post it on request. The whole project can be viewed here.
The code which loads the executable sections of the ELF file is:
The transfer code is:
the file Kstart.asm is
Kernel.c is
The Makefile is:
and the linker script is.
There are a number of possible places where this could be failing. There could be a fault in the code which copies the kernel code; there could be a fault either the Makefile or the linker script (or both); there could be a problem with the kernel itself; there could even be a problem with how I am transferring control. I will try to post all of the relevant code here, though if I missed anything, I can post it on request. The whole project can be viewed here.
The code which loads the executable sections of the ELF file is:
Code: Select all
load_kernel_data:
mov dx, word [bp - stg2_parameters.drive]
mov [kdata_offset - KData.drive], edx
lea ax, [kdata_offset - KData.fat - fat_size]
memcopy_rm ax, [bp - stg2_parameters.fat_0], fat_size
write newline
load_kernel_code:
push ax
push es
mov si, kernel_filename
mov di, word [bp - stg2_parameters.directory_buffer]
mov cx, Root_Entries
mov bx, dir_entry_size
call near seek_directory_entry
cmp di, word 0
jnz .read_directory
write no_kernel
jmp local_halt_loop
.read_directory:
call read_directory_details
write kernel_file_found
; reset the disk drive
call near reset_disk
mov di, word [bp - stg2_parameters.fat_0]
mov ax, kernel_raw_base
mov es, ax
mov si, kernel_raw_offset
call near fat_to_file
pop es
pop ax
write kernel_loaded
find_kernel_code_block:
push gs
mov ax, kernel_raw_base
mov gs, ax
mov al, byte gs:[kernel_raw_offset + ELF32_Header.magic]
cmp al, byte ELF_Magic
je .test_signature
write invalid_elf_magic
jmp local_halt_loop
.test_signature:
mov cx, 3
mov di, kernel_raw_offset + ELF32_Header.sig
push es
mov ax, kernel_raw_base
mov es, ax
mov si, ELF_Sig
repe cmpsb
pop es
je .test_elf_endianness
write invalid_elf_sig
jmp local_halt_loop
.test_elf_endianness:
mov al, byte gs:[kernel_raw_offset + ELF32_Header.endianness]
cmp al, ELF_little_endian
je .test_elf_isa
write elf_big_endian
jmp local_halt_loop
.test_elf_isa:
mov al, byte gs:[kernel_raw_offset + ELF32_Header.isa]
cmp al, ELF_ISA_x86
je .test_elf_executable
write elf_not_x86
jmp local_halt_loop
.test_elf_executable:
mov ax, word gs:[kernel_raw_offset + ELF32_Header.type]
cmp ax, ELF_type_executable
je .read_elf_header_table
write non_executable_elf_file
jmp local_halt_loop
.read_elf_header_table:
write valid_elf_file
; set up an offset for the code sections in memory
mov [section_offset_buffer], word kcode_offset
mov cx, gs:[kernel_raw_offset + ELF32_Header.program_table_entry_count]
write number_of_sections
mov ax, cx
call print_decimal_word
write newline
.program_header_loop:
mov bx, gs:[kernel_raw_offset + ELF32_Header.program_header_table]
add bx, kernel_raw_offset
mov ax, gs:[bx + ELF32_Program_Header.p_type]
cmp ax, ELF_Header_loadable_type
jne .loop_continue
; first, clear the region of memory to load to
push es
mov ax, kernel_base
mov es, ax
mov dx, gs:[bx + ELF32_Program_Header.p_memsz]
add [section_offset_buffer], dx ; dx = total size to allocate to the kernel code memory area
push bx
memset_rm 0, bx, dx
pop bx
; move the code section of the file to the kernel code memory area
push ds
mov ax, gs
mov ds, ax
memcopy_rm [section_offset_buffer], [bx + ELF32_Program_Header.p_offset], [bx + ELF32_Program_Header.p_filesz]
pop ds
pop es
.loop_continue:
; advance the pointer through the header array
add bx, ELF32_Program_Header_size
loop .program_header_loop
pop gs
Code: Select all
bits 32
PModeMain:
; set the segment selectors
mov ax, system_data_selector
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov esp, 0x00090000
call init_page_directory
mov eax, cr0
or eax, Paging ; set Paging bit in CR0 (Control Register 0)
mov cr0, eax
mov esp, 0xc03fffff
; write 'Kernel started' to text buffer
write32 kernel_start, 7
jmp Kernel_linear_addr
Code: Select all
extern kernel_main
global kstart
[SECTION .text]
kstart:
call kernel_main
.halted_loop:
hlt
jmp short .halted_loop
Code: Select all
/* kernel.c */
#include "terminal.h"
void kernel_main()
{
/* clear_screen();
kprint("Starting Kernel...", 0x07); */
}
The Makefile is:
Code: Select all
ASM = nasm -w+all
COPY = dd
FORMAT = mkfs.msdos -F 12 -n "ORDO"
REIMAGE=qemu-img
SYS_INSTALL = ~/Deployments/ms-sys-2.5.3/bin/ms-sys --fat12
BOOTPATH=Verbum/src/PC-x86/nasm/fat12
BOOT = verbum
STAGE_TWO = stagetwo
DISKTARGET = boot.img
DISKSIZE = 1440
CC=i686-elf-gcc
LD=i686-elf-ld
LINK_SCRIPT=linker.ld
CFLAGS=-Wall -Werror -Wpedantic -std=c11 -ffreestanding
C_SRC=src
C_INCLUDES=-I $(C_SRC)
OBJPATH=obj
KERNEL=kernel
KSTART=kstart
TERMINAL=terminal
install: boot stage2 link
$(COPY) if=/dev/zero of=$(OBJPATH)/$(DISKTARGET) count=$(DISKSIZE) bs=1k
$(FORMAT) $(OBJPATH)/$(DISKTARGET)
$(COPY) if=$(OBJPATH)/$(BOOT).bin of=$(OBJPATH)/$(DISKTARGET) count=1 conv=notrunc
mkdir temp
sudo mount $(OBJPATH)/$(DISKTARGET) temp
sudo cp $(OBJPATH)/$(STAGE_TWO).bin temp/STAGETWO.SYS
sudo cp $(OBJPATH)/$(KERNEL).elf temp/KERNEL.SYS
sudo umount temp
rmdir temp
$(REIMAGE) convert -f raw -O qcow2 $(OBJPATH)/$(DISKTARGET) ordo.qcow2
link: kstart kernel terminal
$(LD) -T $(LINK_SCRIPT) $(OBJPATH)/$(KERNEL).o $(OBJPATH)/$(TERMINAL).o -o $(OBJPATH)/$(KERNEL).elf
kernel: terminal
$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(KERNEL).c -o $(OBJPATH)/$(KERNEL).o
terminal:
$(CC) $(CFLAGS) $(C_INCLUDES) -c $(C_SRC)/$(TERMINAL).c -o $(OBJPATH)/$(TERMINAL).o
kstart:
$(ASM) -f elf32 $(C_SRC)/$(KSTART).asm -o $(OBJPATH)/$(KSTART).o -l $(OBJPATH)/$(KSTART).lst
boot:
$(ASM) -f bin -I$(BOOTPATH) $(BOOTPATH)/$(BOOT).asm -o $(OBJPATH)/$(BOOT).bin -l $(OBJPATH)/$(BOOT).lst
stage2:
$(ASM) -f bin -I$(BOOTPATH) $(BOOTPATH)/$(STAGE_TWO).asm -o $(OBJPATH)/$(STAGE_TWO).bin -l $(OBJPATH)/$(STAGE_TWO).lst
Code: Select all
/* The bootloader will look at this image and start execution at the symbol
designated at the entry point. */
ENTRY(kernel_main)
OUTPUT_FORMAT(elf32-i386)
STARTUP(obj/kstart.o)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at the higher half. */
. = 0xC0000000;
/* the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, put them in the proper place in
in this file, if you'd like to include them in the final kernel. */
}