Page 1 of 2

triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 1:22 pm
by Schol-R-LEA
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:

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
The transfer code is:

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
the file Kstart.asm is

Code: Select all

   extern kernel_main
   global kstart

[SECTION .text]
kstart:
        call kernel_main

   .halted_loop:
        hlt
        jmp short .halted_loop
Kernel.c is

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
and the linker script is.

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. */
}

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 2:09 pm
by Demindiro
A few tips:

Add "$@" to run.sh so you can add whatever arguments you want.
Use mon:stdio if you want to open the QEMU monitor in the terminal (my preference, at least).
Also remove -enable-kvm so we can use gdb later:

Code: Select all

qemu-system-x86_64 -boot order=a -fda "ordo.qcow2" \
-m 128M                                            \
-vga virtio -display sdl,gl=on                     \
-serial mon:stdio                                      \
-smp 1                                             \
-usb \
"$@"
Then run with `./run.sh -d int,cpu_reset -no-reboot`.

You'll have output like the following:

Code: Select all

check_exception old: 0xe new 0xd
     1: v=08 e=0000 i=0 cpl=0 IP=0008:00000000c0000007 pc=00000000c0000007 SP=0010:00000000c03fffff env->regs[R_EAX]=0000000080006407
EAX=80006407 EBX=00040c00 ECX=fffffff2 EDX=010ff001
ESI=0000b46e EDI=00000004 EBP=0000ffde ESP=c03fffff
EIP=c0000007 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     0000b5b6 00000100
IDT=     00000000 000003ff
CR0=80000011 CR2=c0400000 CR3=00040000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=80006407 CCO=EFLAGS
EFER=0000000000000000
check_exception old: 0x8 new 0xd
Triple fault
So the triple fault occurs at 0xc0000007. Run with `./run.sh -d int,cpu_reset -s -S`, attach gdb with `gdb -ex='target extended-remote localhost:1234`, set a breakpoint with `b *0xc0000007` and continue with `c`.

We'll see that an exception occurs at `pop $ebp` (with `layout asm`). Inspecting $esp with `p (void*)$esp` shows `0xc03fffff`, which looks wrong (not aligned). If you open the QEMU monitor (View > compactmonitor0 or Ctrl + A, C in terminal) and type `info mem` you'll also see that there is a boundary at 0xc0400000.

My initial hunch was that you're forgetting to set the stack pointer somewhere, but looking at the kernel code there is no pop at 0xc0000007. The assembly in gdb seems to roughly match what's present in kernel.elf though, so I guess it's a problem with whatever loads the kernel.

EDIT: I forgot I added `hlt` to `kstart`, so the numbers you get may vary.

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 2:54 pm
by Schol-R-LEA
Changing the starting stack top to 0xc03ffffc let's it run a bit further, but it still triple-faults a bit later:

Code: Select all

Servicing hardware INT=0x0e
check_exception old: 0xffffffff new 0xe
     0: v=0e e=0000 i=0 cpl=0 IP=0008:00000000c000005d pc=00000000c000005d SP=0010:00000000c0400000 CR2=00000000c0400000
EAX=00000001 EBX=00040c00 ECX=fffffff2 EDX=00000050
ESI=0000b46e EDI=00000004 EBP=00000000 ESP=c0400000
EIP=c000005d EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     0000b5b6 00000100
IDT=     00000000 000003ff
CR0=80000011 CR2=c0400000 CR3=00040000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000001 CCD=00000001 CCO=ADDL
EFER=0000000000000000
check_exception old: 0xe new 0xd
     1: v=08 e=0000 i=0 cpl=0 IP=0008:00000000c000005d pc=00000000c000005d SP=0010:00000000c0400000 env->regs[R_EAX]=0000000000000001
EAX=00000001 EBX=00040c00 ECX=fffffff2 EDX=00000050
ESI=0000b46e EDI=00000004 EBP=00000000 ESP=c0400000
EIP=c000005d EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     0000b5b6 00000100
IDT=     00000000 000003ff
CR0=80000011 CR2=c0400000 CR3=00040000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000001 CCD=00000001 CCO=ADDL
EFER=0000000000000000
check_exception old: 0x8 new 0xd
Triple fault
The reference to an interrupt is concerning to me, as I have not set up an IDT or any ISRs.

I think you are probably right in saying that the real fault is in the loading code.

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 3:08 pm
by Demindiro
Something else I noticed: When breaking at 0xc0000000 the instructions are:

Code: Select all

│B+>0xc0000000  add    eax,0x1ff8                                                             │
│   0xc0000005  nop                                                                           │
│   0xc0000006  pop    rbp                                                                    │
│   0xc0000007  ret                                                                           │
│   0xc0000008  push   rbp                                                                    │
│   0xc0000009  mov    ebp,esp                                                                │
│   0xc000000b  call   0xc00001b0 
When checking kernel.elf:

Code: Select all

c0000000 <kstart>:
c0000000:       e8 03 00 00 00          call   c0000008 <kernel_main>

c0000005 <kstart.halted_loop>:
c0000005:       f4                      hlt    
c0000006:       eb fd                   jmp    c0000005 <kstart.halted_loop>

c0000008 <kernel_main>:
c0000008:       55                      push   ebp
c0000009:       89 e5                   mov    ebp,esp
c000000b:       e8 b0 01 00 00          call   c00001c0 <__x86.get_pc_thunk.ax>
c0000010:       05 f8 1f 00 00          add    eax,0x1ff8
c0000015:       90                      nop
c0000016:       5d                      pop    ebp
c0000017:       c3                      ret    

c0000018 <advance_cursor>:
c0000018:       55                      push   ebp
c0000019:       89 e5                   mov    ebp,esp
c000001b:       e8 a0 01 00 00          call   c00001c0 <__x86.get_pc_thunk.ax>
So it looks like there is a erroneous +/- 0x10 offset somewhere. kcode_offset seems suspect to me since it's the only constant that has 0x10 AFAICT.

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 5:23 pm
by Schol-R-LEA
I am not exactly fluent with GDB, but by running the code to the breakpoint at 0xC0000000, and then disassembling the code, I get the following:

Code: Select all

Breakpoint 1, 0x00000000c0000000 in ?? ()
(gdb) disas 0xc0000000, 0xc00000f0 
Dump of assembler code from 0xc0000000 to 0xc00000f0:
=> 0x00000000c0000000:	add    %eax,(%rax)
   0x00000000c0000002:	add    %al,0x76a08ec(%rbx)
   0x00000000c0000008:	push   $0xffffffffc0001000
   0x00000000c000000d:	call   0xc0000100
   0x00000000c0000012:	add    $0x10,%esp
   0x00000000c0000015:	nop
   0x00000000c0000016:	leave  
   0x00000000c0000017:	ret    
   0x00000000c0000018:	push   %rbp
   0x00000000c0000019:	mov    %esp,%ebp
   0x00000000c000001b:	movabs 0xa302c083c0002004,%eax
   0x00000000c0000024:	add    $0x20,%al
   0x00000000c0000026:	add    %al,%al
   0x00000000c0000028:	movzwl -0x3fffcffe(%rip),%eax        # 0x80003031
   0x00000000c000002f:	mov    $0x50,%edx
   0x00000000c0000034:	cmp    %dx,%ax
   0x00000000c0000037:	jae    0xc000004b
   0x00000000c0000039:	movzwl -0x3fffcffe(%rip),%eax        # 0x80003042
   0x00000000c0000040:	add    $0x1,%eax
   0x00000000c0000043:	movabs %ax,0xc7662aebc0003002
   0x00000000c000004d:	add    $0xc0003002,%eax
   0x00000000c0000052:	add    %al,(%rax)
   0x00000000c0000054:	movzwl -0x3fffd000(%rip),%eax        # 0x8000305b
   0x00000000c000005b:	mov    $0x19,%edx
   0x00000000c0000060:	cmp    %dx,%ax
   0x00000000c0000063:	jae    0xc0000075
   0x00000000c0000065:	movzwl -0x3fffd000(%rip),%eax        # 0x8000306c
   0x00000000c000006c:	add    $0x1,%eax
   0x00000000c000006f:	movabs %ax,0x55c35d90c0003000
   0x00000000c0000079:	mov    %esp,%ebp
   0x00000000c000007b:	sub    $0x18,%esp
   0x00000000c000007e:	mov    0x8(%rbp),%edx
   0x00000000c0000081:	mov    0xc(%rbp),%eax
   0x00000000c0000084:	mov    %dx,-0x14(%rbp)
   0x00000000c0000088:	mov    %ax,-0x18(%rbp)
   0x00000000c000008c:	mov    $0x19,%eax
   0x00000000c0000091:	movzwl %ax,%edx
   0x00000000c0000094:	mov    $0x50,%eax
   0x00000000c0000099:	movzwl %ax,%eax
   0x00000000c000009c:	imul   %edx,%eax
   0x00000000c000009f:	add    $0xb8000,%eax
   0x00000000c00000a4:	mov    %eax,-0x4(%rbp)
   0x00000000c00000a7:	movabs 0x458bc289c0002004,%eax
   0x00000000c00000b0:	cld    
   0x00000000c00000b1:	add    %edx,%eax
   0x00000000c00000b3:	movabs %eax,0xec45b70fc0002004
   0x00000000c00000bc:	movabs %ax,0xe845b70fc0003002
   0x00000000c00000c6:	movabs %ax,0x55c3c990c0003000
   0x00000000c00000d0:	mov    %esp,%ebp
   0x00000000c00000d2:	sub    $0x8,%esp
   0x00000000c00000d5:	mov    0x8(%rbp),%edx
   0x00000000c00000d8:	mov    0xc(%rbp),%eax
   0x00000000c00000db:	mov    %dl,-0x4(%rbp)
   0x00000000c00000de:	mov    %al,-0x8(%rbp)
   0x00000000c00000e1:	movabs 0xfc55b60fc0002004,%eax
   0x00000000c00000ea:	mov    %dl,(%rax)
   0x00000000c00000ec:	movabs 0xf855b60fc0002004,%eax
End of assembler dump.
Frankly, this looks like just garbage memory. I can only conclude that my code for loading the executable portions of the ELF file is broken.

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 6:15 pm
by Octocontrabass
It looks like mostly-reasonable 32-bit code to me. It only looks like garbage because GDB is trying to disassemble it as 64-bit code. Unfortunately it has been a while since I've used GDB with QEMU, so I don't remember how to make it display a reasonable disassembly.

There's still something wrong. The instruction at address 0xC0000000 is cut in half. I can't tell how far away it is from being correct without a copy of the kernel binary, though.

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 8:07 pm
by Schol-R-LEA
I tried the 'set architecture' option but got the following:

Code: Select all

(gdb) set archi
Requires an argument. Valid arguments are i386, i386:x86-64, i386:x64-32, i8086, i386:intel, i386:x86-64:intel, i386:x64-32:intel, auto.
(gdb) set archi i386
warning: Selected architecture i386 is not compatible with reported target architecture i386:x86-64
Architecture `i386' not recognized.
The target architecture is set to "auto" (currently "i386:x86-64").
It says that i386 is a valid option, but then rejects it on the basis that it isn't the reported system architecture. I switched to using qemu-system-i386 and it worked, but the disassembly was more or less the same:

Code: Select all

(gdb) set archi i386
The target architecture is set to "i386".
(gdb) disas 0xc0000000, 0xc00000f0
Dump of assembler code from 0xc0000000 to 0xc00000f0:
=> 0xc0000000:	add    %eax,(%eax)
   0xc0000002:	add    %al,0x76a08ec(%ebx)
   0xc0000008:	push   $0xc0001000
   0xc000000d:	call   0xc0000100
   0xc0000012:	add    $0x10,%esp
   0xc0000015:	nop
   0xc0000016:	leave  
   0xc0000017:	ret    
   0xc0000018:	push   %ebp
   0xc0000019:	mov    %esp,%ebp
   0xc000001b:	mov    0xc0002004,%eax
   0xc0000020:	add    $0x2,%eax
   0xc0000023:	mov    %eax,0xc0002004
   0xc0000028:	movzwl 0xc0003002,%eax
   0xc000002f:	mov    $0x50,%edx
   0xc0000034:	cmp    %dx,%ax
   0xc0000037:	jae    0xc000004b
   0xc0000039:	movzwl 0xc0003002,%eax
   0xc0000040:	add    $0x1,%eax
   0xc0000043:	mov    %ax,0xc0003002
   0xc0000049:	jmp    0xc0000075
   0xc000004b:	movw   $0x0,0xc0003002
   0xc0000054:	movzwl 0xc0003000,%eax
   0xc000005b:	mov    $0x19,%edx
   0xc0000060:	cmp    %dx,%ax
   0xc0000063:	jae    0xc0000075
   0xc0000065:	movzwl 0xc0003000,%eax
   0xc000006c:	add    $0x1,%eax
   0xc000006f:	mov    %ax,0xc0003000
   0xc0000075:	nop
   0xc0000076:	pop    %ebp
   0xc0000077:	ret    
   0xc0000078:	push   %ebp
   0xc0000079:	mov    %esp,%ebp
   0xc000007b:	sub    $0x18,%esp
   0xc000007e:	mov    0x8(%ebp),%edx
   0xc0000081:	mov    0xc(%ebp),%eax
   0xc0000084:	mov    %dx,-0x14(%ebp)
   0xc0000088:	mov    %ax,-0x18(%ebp)
   0xc000008c:	mov    $0x19,%eax
   0xc0000091:	movzwl %ax,%edx
   0xc0000094:	mov    $0x50,%eax
   0xc0000099:	movzwl %ax,%eax
   0xc000009c:	imul   %edx,%eax
   0xc000009f:	add    $0xb8000,%eax
   0xc00000a4:	mov    %eax,-0x4(%ebp)
   0xc00000a7:	mov    0xc0002004,%eax
   0xc00000ac:	mov    %eax,%edx
   0xc00000ae:	mov    -0x4(%ebp),%eax
   0xc00000b1:	add    %edx,%eax
   0xc00000b3:	mov    %eax,0xc0002004
   0xc00000b8:	movzwl -0x14(%ebp),%eax
   0xc00000bc:	mov    %ax,0xc0003002
   0xc00000c2:	movzwl -0x18(%ebp),%eax
   0xc00000c6:	mov    %ax,0xc0003000
   0xc00000cc:	nop
   0xc00000cd:	leave  
   0xc00000ce:	ret    
   0xc00000cf:	push   %ebp
   0xc00000d0:	mov    %esp,%ebp
   0xc00000d2:	sub    $0x8,%esp
   0xc00000d5:	mov    0x8(%ebp),%edx
   0xc00000d8:	mov    0xc(%ebp),%eax
   0xc00000db:	mov    %dl,-0x4(%ebp)
   0xc00000de:	mov    %al,-0x8(%ebp)
   0xc00000e1:	mov    0xc0002004,%eax
   0xc00000e6:	movzbl -0x4(%ebp),%edx
   0xc00000ea:	mov    %dl,(%eax)
   0xc00000ec:	mov    0xc0002004,%eax
End of assembler dump.
Note that I did make some changes to the ELF loading code, so I should post those changes before proceeding.

Code: Select all

        ; bx = pointer to the first program header
        mov bx, gs:[kernel_raw_offset + ELF32_Header.program_header_table]
        add bx, kernel_raw_offset                  ; bx = pointer to the first program header
    .program_header_loop:
        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]

        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 
        add [section_offset_buffer], dx            ; dx = total size to allocate to the kernel code memory area
        loop .program_header_loop
Where section_offset_buffer is now defined as

Code: Select all

section_offset_buffer        dw kcode_offset               ; initialize to the start of the code area

Re: triple-fault on jumping to kernel

Posted: Tue Aug 23, 2022 10:11 pm
by Octocontrabass
Schol-R-LEA wrote:the disassembly was more or less the same
There's still a cut-in-half instruction at the top, so there's still an offset. Everything below it looks correct.

Out of curiosity, what physical address does QEMU say you've mapped to virtual address 0xC0000000?

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 12:00 am
by Schol-R-LEA
Octocontrabass wrote:
Schol-R-LEA wrote:the disassembly was more or less the same
There's still a cut-in-half instruction at the top, so there's still an offset. Everything below it looks correct.

Out of curiosity, what physical address does QEMU say you've mapped to virtual address 0xC0000000?
I'm not sure how I would get that information from QEMU.

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 10:25 am
by Octocontrabass
Use "info mem" and "info tlb" in the QEMU monitor, or "monitor info mem" and "monitor info tlb" in GDB.

They don't report the same information, so it's a good idea to always check both. (And they may have some bugs that cause the displayed page permissions to not match what an actual CPU would calculate. This only affects the display, the emulated CPU properly obeys all page permissions.)

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 11:10 am
by Schol-R-LEA
Ah, OK, thank you.

This is what info mem returns in gdb.

Code: Select all

Breakpoint 1, 0xc0000000 in ?? ()
(gdb) monitor info mem
0000000000000000-0000000000100000 0000000000100000 -r-
00000000c0000000-00000000c0400000 0000000000400000 -r-
The display from info tlb is rather longer, but the relevant sections appear to be:

Code: Select all

00000000000ff000: 00000000000ff000 ---------
00000000c0000000: 0000000000100000 ---------
00000000c0001000: 0000000000101000 ---------
00000000c0002000: 0000000000102000 ---------
00000000c0003000: 0000000000103000 ---------
00000000c0004000: 0000000000104000 ---------
00000000c0005000: 0000000000105000 ---------
00000000c0006000: 0000000000106000 ---------
00000000c0007000: 0000000000107000 ---------
00000000c0008000: 0000000000108000 ---------
00000000c0009000: 0000000000109000 ---------
00000000c000a000: 000000000010a000 ---------

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 11:22 am
by Octocontrabass
Looks good. That means the problem is the code that copies the .text section to 0x100000.

You're still doing that part in real mode too, using the segment:offset pair FFFF:0010 to represent linear address 0x10000, right?

In that case, wouldn't this commit break it by changing the segment:offset pair to FFFF:0000?

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 12:05 pm
by Schol-R-LEA
Octocontrabass wrote:Looks good. That means the problem is the code that copies the .text section to 0x100000.

You're still doing that part in real mode too, using the segment:offset pair FFFF:0010 to represent linear address 0x10000, right?

In that case, wouldn't this commit break it by changing the segment:offset pair to FFFF:0000?
Yes, and yes. However, I reverted that change some time back (though I didn't commit it yet), and the results didn't notably change.

Give me a moment and I'll push the current version of the code.

One thing I found is that, for some reason, the ELF program header is giving the memory footprint of the sections (at ELF32_Program_Header.p_memsz) as 0x1004, which is puzzling.

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 12:12 pm
by Octocontrabass
That doesn't sound right. What does readelf or objdump say about the program headers?

Re: triple-fault on jumping to kernel

Posted: Wed Aug 24, 2022 12:22 pm
by Schol-R-LEA
Based on readelf, that is in fact the size of the memory footprint. This is concerning to me, I wonder if this reflects a problem in the linker script.

It is also showing the entry point as 0xc0000008, which is definitely not what I intended. Or am I mistaken about that? Is it showing the entry point for kernel_main() rather than kstart?

Code: Select all

$ readelf obj/kernel.elf -l

Elf file type is EXEC (Executable file)
Entry point 0xc0000008
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0xc0000000 0xc0000000 0x01004 0x01004 R E 0x1000
  LOAD           0x003000 0xc0002000 0xc0002000 0x00008 0x01004 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata 
   01     .data .bss 
Checking with objdump gets the same results.

Code: Select all

$ objdump -x obj/kernel.elf 

obj/kernel.elf:     file format elf32-i386
obj/kernel.elf
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0xc0000008

Program Header:
    LOAD off    0x00001000 vaddr 0xc0000000 paddr 0xc0000000 align 2**12
         filesz 0x00001004 memsz 0x00001004 flags r-x
    LOAD off    0x00003000 vaddr 0xc0002000 paddr 0xc0002000 align 2**12
         filesz 0x00000008 memsz 0x00001004 flags rw-

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000179  c0000000  c0000000  00001000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       00000004  c0001000  c0001000  00002000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00000008  c0002000  c0002000  00003000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  3 .bss          00000004  c0003000  c0003000  00003008  2**12
                  ALLOC
  4 .comment      0000002a  00000000  00000000  00003008  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    df *ABS*	00000000 src/kstart.asm
c0000005 l       .text	00000000 kstart.halted_loop
00000000 l    df *ABS*	00000000 kernel.c
00000000 l    df *ABS*	00000000 terminal.c
c000000e g     F .text	00000060 advance_cursor
c0003002 g     O .bss	00000002 currh
c0002000 g     O .data	00000004 text_buffer
c0001002 g     O .rodata	00000002 MAXV
c0000008 g     F .text	00000006 kernel_main
c00000f6 g     F .text	00000048 kprint
c0002004 g     O .data	00000004 text_cursor
c0001000 g     O .rodata	00000002 MAXH
c0000000 g       .text	00000000 kstart
c00000c5 g     F .text	00000031 kprintc
c0003000 g     O .bss	00000002 currv
c000006e g     F .text	00000057 gotoxy
c000013e g     F .text	0000003b clear_screen