Crash when enabling paging - but memdump looks fine?
Posted: Tue Jun 20, 2023 12:27 am
I'm attempting to set up a simple paging system using a combination of information from here and the Intel manual, and it was initially working well until I tried to move the kernel to the higher half. Now it just immediately closes, and although I've tried the -d flags with qemu, I'm not getting much of anything.
After running the code, cr3->103000, so I dump the memory at that address:
So it seems to be working properly, the page table's first entry is 0x104000, the next 4096 bytes, and the 768th entry is 0x105000, the next 4096 bytes after that.
Taking a look at one of the page tables:
That... also looks right. That's the first entry, which should identity map the first 4MB. The same with the next page table, which maps the same addresses at a 3GB offset, so it looks like both the directory and table work fine. So I'm lost, the memory looks exactly like the manual says it should, the PG and PE bits are enabled, so I don't see what's causing it to crash. Any help is much appreciated!
Now for the code:
And the linker file, since we're dealing with higher-half stuff:
After running the code, cr3->103000, so I dump the memory at that address:
Code: Select all
(qemu) xp/769xw 0x103000
0000000000103000: 0x00104000 0x00000000 0x00000000 0x00000000
0000000000103010: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000103020: 0x00000000 0x00000000 0x00000000 0x00000000
[...many zeroes follow]
0000000000103be0: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000103bf0: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000103c00: 0x00105000
Taking a look at one of the page tables:
Code: Select all
(qemu) xp/500xw 0x104000
0000000000104000: 0x00000003 0x00001003 0x00002003 0x00003003
0000000000104010: 0x00004003 0x00005003 0x00006003 0x00007003
0000000000104020: 0x00008003 0x00009003 0x0000a003 0x0000b003
0000000000104030: 0x0000c003 0x0000d003 0x0000e003 0x0000f003
0000000000104040: 0x00010003 0x00011003 0x00012003 0x00013003
0000000000104050: 0x00014003 0x00015003 0x00016003 0x00017003
0000000000104060: 0x00018003 0x00019003 0x0001a003 0x0001b003
[...]
Now for the code:
Code: Select all
.section .data
.align 4096
boot_page_directory:
# skip 4096 bytes
.skip 4096
boot_page_table1:
# skip 4096 bytes
.skip 4096
boot_page_table2:
# skip 4096 bytes
.skip 4096
# The kernel entry point.
.section .text
.global _start
_start:
# Set up the stack.
mov $stack_top, %esp
subl $VIRTUAL_BASE, %esp
# Set up paging
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
# %eax = physical address of page directory
movl $boot_page_directory, %eax
subl $VIRTUAL_BASE, %eax
andl $0xFFFFF000, %eax # align to 4 KiB
# %ebx = physical address of page table 1
movl $boot_page_table1, %ebx
subl $VIRTUAL_BASE, %ebx
andl $0xFFFFF000, %ebx # align to 4 KiB
# Move the page table into the first entry of the page directory.
movl %ebx, (%eax)
# Fill the page table with entries
# %eax = physical address of current page table entry, starting at 0x00000000 and incrementing by 4096
# %ecx = number of pages to fill (looping with loop, fill 1024 pages)
# Since we have the address of the page table in ebx, we can increment that by 4 each time.
movl $0x0, %eax
movl $1023, %ecx
1:
# Add the flags to the address
addl $0x3, %eax
# Move the entry into the table
movl %eax, (%ebx)
# Remove the flags
subl $0x3, %eax
# Move to the next table entry
addl $0x4, %ebx
# Increase the physical address set by 4096
addl $4096, %eax
# Loop
loop 1b
# %ebx = physical address of page table 2
movl $boot_page_table2, %ebx
subl $VIRTUAL_BASE, %ebx
andl $0xFFFFF000, %ebx # align to 4 KiB
# This part is a little tricky - we need to set it to entry 768 (0x300) in the page directory to offset it by 3 GiB.
# We can do this by adding 0x300 * 4 to the page directory address.
# %eax = physical address of page directory
movl $boot_page_directory, %eax
subl $VIRTUAL_BASE, %eax
andl $0xFFFFF000, %eax # align to 4 KiB
# Add 0x300 * 4 to the page directory address
addl $0xC00, %eax
# %ebx = physical address of page table 2
movl $boot_page_table2, %ebx
subl $VIRTUAL_BASE, %ebx
andl $0xFFFFF000, %ebx # align to 4 KiB
# Move the second page table into the 768th entry of the page directory.
movl %ebx, (%eax)
# Fill the page table with entries
# %eax = physical address of current page table entry, starting at 0x00000000 and incrementing by 4096
# %ecx = number of pages to fill (looping with loop, fill 1024 pages)
# Since we have the address of the page table in ebx, we can increment that by 4 each time.
movl $0x0, %eax
movl $1023, %ecx
2:
# Add the flags to the address
addl $0x3, %eax
# Move the entry into the table
movl %eax, (%ebx)
# Remove the flags
subl $0x3, %eax
# Move to the next table entry
addl $0x4, %ebx
# Increase the physical address set by 4096
addl $4096, %eax
# Loop
loop 2b
# Move the directory address into cr3
movl $boot_page_directory, %eax
subl $VIRTUAL_BASE, %eax
andl $0xFFFFF000, %eax # align to 4 KiB
movl %eax, %cr3
# Enable PG and PE
movl %cr0, %eax
orl $0x80000001, %eax
stop:
jmp stop # in case you're actually running this, stop here
movl %eax, %cr0
# Anything after enabling paging crashes. I can't tell if it's actually enabling
# paging that does it, or if it's trying to run code *after* paging, but anything
# after this point crashes the system.
Code: Select all
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* A few definitions the assembly code can use for physical addresses. */
PHYSICAL_BASE = 0x00100000;
VIRTUAL_BASE = 0xC0000000;
/* 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, and add C0000000 (3GB) for paging. */
. = 0xC0100000;
/* 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. */
.text ALIGN (0x1000) : AT(ADDR(.text)-0xC0000000)
{
*(.multiboot)
*(.text)
}
/* Read-only data. */
.rodata ALIGN (0x1000) : AT(ADDR(.rodata)-0xC0000000)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data ALIGN (0x1000) : AT(ADDR(.data)-0xC0000000)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss ALIGN (0x1000) : AT(ADDR(.bss)-0xC0000000)
{
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}