Page 1 of 1

BSS Page fault when setting permission for kernel pages

Posted: Fri Jan 13, 2023 6:01 am
by nickname912
Hi,
I have started to build a kernel as a hobby project, I have followed the Meaty Skeleton and Higher Half Barebones (https://wiki.osdev.org/Higher_Half_x86_Bare_Bones) tutorials and everything gone smoothly. However, next I wanted to set the correct permissions for the different sections (read/write for data, common, stack and bss, read-only for the rest), but I ran in to some troubles

First i set symbols in the linker script marking start/end of writable sections (src files are attached)

Code: Select all

        ...
	.text ALIGN (4K) : AT (ADDR (.text) - HIGHER_HALF_ADDR)
	{
		*(.text)
	}
	.rodata ALIGN (4K) : AT (ADDR (.rodata) - HIGHER_HALF_ADDR)
	{
		*(.rodata)
	}
	_wdata_start = .;
	.data ALIGN (4K) : AT (ADDR (.data) - HIGHER_HALF_ADDR)
	{
		*(.data)
	}
	.bss ALIGN (4K) : AT (ADDR (.bss) - HIGHER_HALF_ADDR)
	{
		*(COMMON)
		*(.bss)
		*(.bootstrap_stack)
	}
	_wdata_end = .;
And the I set the permission depending if they are within the region

Code: Select all

_start:
    # Physical address of boot_page_table1.
    movl $(boot_page_table1 - HIGHER_HALF_ADDR), %edi
	
    # First physical address to map is address 0.
    movl $0, %esi

    # Page table setup loop	
table_loop:
    # Skip pages before multiboot (i.e. < 1 MiB section)
    cmpl $_kernel_start, %esi
    jl skip
	
    # Once the full kernel is mapped, exit loop
    cmpl $(_kernel_end - HIGHER_HALF_ADDR), %esi
    jge end

    # Set default permisisons, "present, read-only"
    movl $0x001, %ecx

   # If within are the write area (data, bss and stack), enable writing
   cmpl $(_wdata_start - HIGHER_HALF_ADDR), %esi
   jl  set_permission
   cmpl $(_wdata_end - HIGHER_HALF_ADDR), %esi
   jge  set_permission
   orl  $0x002, %ecx
	
set_permission:
	# Map physical address with specified permissions
	movl %esi, %edx
	orl  %ecx, %edx
	movl %edx, (%edi)

skip:
	addl $4096, %esi # Increment page address by 4 KiB page size
	addl $4, %edi    # Increment page table address by 4B (each entry is 32 bits)
	loop table_loop
end:
However, when later writing to the bss i a get page fault, and I'm quite confused why

Re: BSS Page fault when setting permission for kernel pages

Posted: Tue Jan 17, 2023 11:40 am
by Octocontrabass
What's the page fault error code? What values are in CR2 and the saved return address?

Which virtual machine are you using? QEMU has several debugging tools that will be helpful here, including the ability to dump exceptions ("-d int") and a monitor that can help you examine your page tables ("info tlb" and "info mem").

Re: BSS Page fault when setting permission for kernel pages

Posted: Thu Jan 19, 2023 12:18 am
by MichaelPetch
This looks wrong and won't properly loop as you expect:

Code: Select all

skip:
   addl $4096, %esi # Increment page address by 4 KiB page size
   addl $4, %edi    # Increment page table address by 4B (each entry is 32 bits)
   loop table_loop
end:
The problem is the `loop table_loop' line. That decrements ECX and checks if it is zero, and if zero it exits the loop. Looking at your code I think you meant 'jmp table_loop' to jump unconditionally back to label 'table_loop'. So it would look like:

Code: Select all

skip:
   addl $4096, %esi # Increment page address by 4 KiB page size
   addl $4, %edi    # Increment page table address by 4B (each entry is 32 bits)
   jmp table_loop
end:
Most likely whatever value was in ECX was low enough that it prevented enough iterations to occur and in turn you didn't map all the pages you wanted.

Since you are running 32-bit code (no 16-bit real mode code) debugging with QEMU and GDB can help find these kinds of problems. It was how I finally noticed the issue with `loop` vs `jmp`.

You can build your assembly and C/C++ files with the `-g` option to enable debugging information. There is a bit of an issue in your `boot.s` because the debugger may not see your `.mulitboot.text` section as executable because you forgot to use the `x` section option. This line:

Code: Select all

.section .multiboot.text, "a"
should be:

Code: Select all

.section .multiboot.text, "ax"
.
To build you could use commands like this:

Code: Select all

gcc -g -c  -m32 -fno-pic -ffreestanding  kernel.c -o kernel.o
as -g --32 boot.s -o boot.o
ld -melf_i386 -Tlinker.ld boot.o kernel.o -o kernel.elf
Then you can run QMEU and debug with GDB doing something like:

Code: Select all

qemu-system-i386 -kernel kernel.elf -no-shutdown -no-reboot -d int -D log.txt -S -s &

gdb kernel.elf \
        -ex 'target remote localhost:1234' \
        -ex 'layout src' \
        -ex 'layout regs' \
        -ex 'break *_start' \
        -ex 'continue'
Interrupt/exception info will be written to log.txt. In this case I have it stopping at label `_start` so that I could debug the code before `kernel_main`. In the QEMU window you can gain access to the monitor with control-alt-2. In the monitor you can type help for all the commands. What may be useful is to see the paging information or the TLB using the `info mem` and `info tlb` commands. To switch out of the monitor back to the virtual machine you can use control-alt-1. You can find additional information on the monitor here: https://en.wikibooks.org/wiki/QEMU/Monitor

You can find reference material/tutorials about using GDB to debug in Google. The GDB documentation is here: https://sourceware.org/gdb/current/onlinedocs/gdb.html/ Some of the useful commands are `ni` (next instruction); 'si' (step instruction); 'c' continue; 'b' (set breakpoints).

Re: BSS Page fault when setting permission for kernel pages

Posted: Sat Mar 25, 2023 4:42 am
by nickname912
Thanks for tips MichaelPetch, they really helped! Sorry for the very late reply, unfortunately life came between me and my os project for a while