Function to set up Page Directory/Tables returns to address 0x0

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
InfoBroad
Posts: 5
Joined: Sun Jul 19, 2026 3:30 pm

Function to set up Page Directory/Tables returns to address 0x0

Post by InfoBroad »

I am currently working on a higher-half kernel and I have tried to set up paging for a while now to no avail. I started out with the Meaty Skeleton base project and I added a few things like the GDT and Global Constructors, but I got stuck when I got to memory management.

I followed along with the details on the "x86 Paging" article on osdev.org. Looking at it in GDB shows that kernel_early_main returns to the address 0x0 instead of continuing on afterwards in boot.S. I would like to have higher half paging working so that execution can reach kernel_main. I removed most of the extra things that don't affect this problem. Also, all of the source code can be found at https://github.com/ResistingGuide4/rOSes

Boot.S

Code: Select all

# Declare constants for the multiboot 2 header.
.set FLAGS,    0  # this is the Multiboot 'flag' field
.set MAGIC,    0xE85250D6       # 'magic number' lets bootloader find the header
.set SIZE, header_end - header_start
.set CHECKSUM, -(MAGIC + FLAGS + SIZE) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot,"a",@progbits
.align 8
header_start:
.long MAGIC
.long FLAGS
.long SIZE
.long CHECKSUM
# Tags go here
# Ending tag
.align 8
.short 0
.short 0
.long 8
header_end:
# Reserve a stack for the initial thread.
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
.global stack_top
stack_top:

.section .data
.align 4096
.global page_directory_start
page_directory_start:
.skip 4096
.global page_table1_start
page_table1_start:
.skip 4096
.global page_table768_start
page_table768_start:
.skip 4096

# The kernel entry point.
.section .boot,"ax",@progbits
.global _start
.type _start, @function
_start:
   # Initialize Stack
	movl $stack_top, %esp

   # Push Data Requested from Bootloader to Stack
   pushl %eax
   pushl %ebx

	# Setup necessary components like Memory Management
	call kernel_early_main

   movl page_directory_start, %eax
   movl %eax, %cr3

   movl %cr0, %eax
   orl $0x80000001, %eax
   movl %eax, %cr0

   ljmp $0x08, $higher_half_start
.size _start, . - _start
.section .text
higher_half_start:
	# Call the global constructors.
	call _init

	# Transfer control to the main kernel.
	call kernel_main

	# Hang if kernel_main unexpectedly returns.
	cli
1:	hlt
	jmp 1b
.size higher_half_start, . - higher_half_start
earlyinit.c

Code: Select all

#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>

#include <kernel/multiboot2.h>

extern uint32_t stack_top;
extern uint32_t page_directory_start[1024];
extern uint32_t page_table1_start[1024];
extern uint32_t page_table768_start[1024];

void __attribute__((section(".boot"), used)) kernel_early_main() {
    for (int i = 0; i < 1024; i++) {
        page_table1_start[i] = (i * 0x1000) | 0x3;
        page_table768_start[i] = (i * 0x1000) | 0x3;
    }
    asm volatile("between:");
    page_directory_start[0] = (uint32_t)page_table1_start | 0x3;
    page_directory_start[768] = (uint32_t)page_table768_start | 0x3;
    page_directory_start[1023] = (uint32_t)page_directory_start | 0x3;
}
linker.ld

Code: Select all

/* The bootloader will look at this image and start execution at the symbol
   designated at the entry point. */
ENTRY(_start)

/* Tell where the various sections of the object files will be put in the final
   kernel image. */
SECTIONS
{
	/* Begin putting sections at 1 MiB, a conventional place for kernels to be
	   loaded at by the bootloader. */
	. = 1M;

	.boot BLOCK(4K) : ALIGN(4K)
	{
		. = ALIGN(8);
		KEEP(*(.multiboot))
		*(.boot)
	}

	/* First put the multiboot header, as it is required to be put very early
	   early in the image or the bootloader won't recognize the file format.
	   Next we'll put the .text section. */
	. = 0xC0000000;

	.text BLOCK(4K) : AT(ADDR(.text) - 0xC0000000) ALIGN(4K)
	{
		*(.text)
	} 

	/* Read-only data. */
	.rodata BLOCK(4K) : AT(ADDR(.rodata) - 0xC0000000) ALIGN(4K) 
	{
		*(.rodata)
	}

	/* Read-write data (initialized) */
	.data BLOCK(4K) : AT(ADDR(.data) - 0xC0000000) ALIGN(4K)
	{
		*(.data)
	} 

	/* Read-write data (uninitialized) and stack */
	.bss BLOCK(4K) : AT(ADDR(.bss) - 0xC0000000) 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. */
}
Octocontrabass
Member
Member
Posts: 6275
Joined: Mon Mar 25, 2013 7:01 pm

Re: Function to set up Page Directory/Tables returns to address 0x0

Post by Octocontrabass »

Do you see any compiler warnings when you build this code?
InfoBroad
Posts: 5
Joined: Sun Jul 19, 2026 3:30 pm

Re: Function to set up Page Directory/Tables returns to address 0x0

Post by InfoBroad »

No errors or warnings. The only output is what commands are run by the build script and info returned from QEMU.

Code: Select all

mkdir -p /home/########/Documents/rOSes/sysroot/usr/include
cp -R --preserve=timestamps include/. /home/########/Documents/rOSes/sysroot/usr/include/.
mkdir -p /home/########/Documents/rOSes/sysroot/usr/include
cp -R --preserve=timestamps include/. /home/########/Documents/rOSes/sysroot/usr/include/.
mkdir -p /home/########/Documents/rOSes/sysroot/usr/include
cp -R --preserve=timestamps include/. /home/########/Documents/rOSes/sysroot/usr/include/.
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c stdio/printf.c -o stdio/printf.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c stdio/putchar.c -o stdio/putchar.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c stdio/puts.c -o stdio/puts.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c stdlib/abort.c -o stdlib/abort.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c string/memcmp.c -o string/memcmp.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c string/memcpy.c -o string/memcpy.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c string/memmove.c -o string/memmove.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c string/memset.c -o string/memset.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c string/strlen.c -o string/strlen.libk.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_libc -Iinclude -D__is_libk 
i686-elf-ar rcs libk.a stdio/printf.libk.o stdio/putchar.libk.o stdio/puts.libk.o stdlib/abort.libk.o string/memcmp.libk.o string/memcpy.libk.o string/memmove.libk.o string/memset.libk.o string/strlen.libk.o
mkdir -p /home/########/Documents/rOSes/sysroot/usr/lib
cp libk.a /home/########/Documents/rOSes/sysroot/usr/lib
mkdir -p /home/########/Documents/rOSes/sysroot/usr/include
cp -R --preserve=timestamps include/. /home/########/Documents/rOSes/sysroot/usr/include/.
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c arch/i386/crti.S -o arch/i386/crti.o -O2 -g -ffreestanding -Wall -Wextra   -D__is_kernel -Iinclude 
OBJ=`i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -O2 -g -ffreestanding -Wall -Wextra    -print-file-name=crtbegin.o` && cp "$OBJ" arch/i386/crtbegin.o
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c arch/i386/boot.S -o arch/i386/boot.o -O2 -g -ffreestanding -Wall -Wextra   -D__is_kernel -Iinclude 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c arch/i386/tty.c -o arch/i386/tty.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_kernel -Iinclude 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c arch/i386/earlyinit.c -o arch/i386/earlyinit.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra  -fno-stack-protector  -D__is_kernel -Iinclude 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c kernel/kernel.c -o kernel/kernel.o -std=gnu11 -O2 -g -ffreestanding -Wall -Wextra   -D__is_kernel -Iinclude 
OBJ=`i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -O2 -g -ffreestanding -Wall -Wextra    -print-file-name=crtend.o` && cp "$OBJ" arch/i386/crtend.o
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -MD -c arch/i386/crtn.S -o arch/i386/crtn.o -O2 -g -ffreestanding -Wall -Wextra   -D__is_kernel -Iinclude 
i686-elf-gcc --sysroot=/home/########/Documents/rOSes/sysroot -isystem=/usr/include -T arch/i386/linker.ld -o roses.kernel -O2 -g -ffreestanding -Wall -Wextra    arch/i386/crti.o arch/i386/crtbegin.o arch/i386/boot.o arch/i386/tty.o arch/i386/earlyinit.o kernel/kernel.o   -nostdlib -lk -lgcc  arch/i386/crtend.o arch/i386/crtn.o 
grub2-file --is-x86-multiboot2 roses.kernel
mkdir -p /home/########/Documents/rOSes/sysroot/boot
cp roses.kernel /home/########/Documents/rOSes/sysroot/boot
xorriso 1.5.8.pl02 : RockRidge filesystem manipulator, libburnia project.

Drive current: -outdev 'stdio:roses.iso'
Media current: stdio file, overwriteable
Media status : is blank
Media summary: 0 sessions, 0 data blocks, 0 data, 4019m free
Added to ISO image: directory '/'='/tmp/grub.tXBkB7'
xorriso : UPDATE :     978 files added in 1 seconds
Added to ISO image: directory '/'='/home/########/Documents/rOSes/isodir'
xorriso : UPDATE :     982 files added in 1 seconds
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
ISO image produced: 14841 sectors
Written to medium : 14841 sectors at LBA 0
Writing to 'stdio:roses.iso' completed successfully.
Octocontrabass
Member
Member
Posts: 6275
Joined: Mon Mar 25, 2013 7:01 pm

Re: Function to set up Page Directory/Tables returns to address 0x0

Post by Octocontrabass »

Are you sure the "return" to address 0 is actually occurring at the RET instruction at the end of the function?
InfoBroad
Posts: 5
Joined: Sun Jul 19, 2026 3:30 pm

Re: Function to set up Page Directory/Tables returns to address 0x0

Post by InfoBroad »

Never mind. I found out it was jumping to 0x0 because the stack was in .data, which the linker script had mapped at a higher half virtual address, so it was stack_top was pointing to the wrong place entirely. Thanks for the help regardless.
Post Reply