Issues with paging and virtual memory

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
cavosdev
Posts: 5
Joined: Fri Oct 20, 2023 7:16 am

Issues with paging and virtual memory

Post by cavosdev »

Hello everyone! I'm having issues with virtual memory and paging... I'm trying to map physical address 0x0 to virtual 0x0 (as a test)... I attached my paging source file, and linking script (grub, multiboot2 as bootloader). My main assembly file is like so:

Code: Select all

; Kernel boot file
; Copyright (C) 2023 Panagiotis

MBOOT_HEADER_MAGIC  equ 0xE85250D6
MBOOT_ARCH          equ 0x00000000
KERNEL_STACK_SIZE   equ 4096
global KERNEL_VIRTUAL_BASE
KERNEL_VIRTUAL_BASE equ 0xC0000000                  ; 3GB
KERNEL_PAGE_NUMBER  equ (KERNEL_VIRTUAL_BASE >> 22)  ; Page directory index of kernel's 4MB PTE.

; Legacy from multiboot 1
; MBOOT_PAGE_ALIGN    equ 1 << 0
; MBOOT_MEM_INFO      equ 1 << 1
; MBOOT_GRAPH_MODE    equ 1 << 2

; MBOOT_HEADER_FLAGS  equ MBOOT_ARCH | MBOOT_HEADER_LEN
; MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)

bits    32

section .multiboot_header
header_start:
        dd  MBOOT_HEADER_MAGIC
        dd  MBOOT_ARCH
        dd  header_end - header_start
        dd  0x100000000 - (MBOOT_HEADER_MAGIC + MBOOT_ARCH + (header_end - header_start))
        
; best -> 1024x768x32
align 8
    dw 5
    dw 1
    dd 20
    dd 1024
    dd 768
    dd 32

align 8
    dw 0
    dw 0
    dd 8
header_end:

; setup stack
global boot_pagetab1
section .bss
align 4
stack_bottom:
    resb KERNEL_STACK_SIZE ; 4 kib
stack_top:

section .boot
global _start
_start:
    mov ecx, (BootPageDirectory - KERNEL_VIRTUAL_BASE) ; 0x104000
    mov cr3, ecx                                       ; Load Page Directory Base Register.
 
    mov ecx, cr4
    or ecx, 0x00000010 ; Set PSE bit in CR4 to enable 4MB pages.
    mov cr4, ecx
 
    mov ecx, cr0
    or ecx, 0x80000000 ; Set PG bit in CR0 to enable paging.
    mov cr0, ecx
 
    lea ecx, [higher_half]
    jmp ecx

section .text
higher_half:
    ; Unmap the identity-mapped first 4MB of physical address space. It should not be needed
    ; anymore.
    mov dword [BootPageDirectory], 0
    invlpg [0]

    mov esp, stack_bottom

    add ebx, KERNEL_VIRTUAL_BASE ; make the address virtual
    ; add eax, KERNEL_VIRTUAL_BASE

    push ebx
    push eax
    ; push ebx ; multiboot mem info pointer

    extern kmain
    call kmain

global asmEnablePaging

asmEnablePaging:
; load page directory (eax has the address of the page directory) 
   mov eax, [esp+4]
   mov cr3, eax        

; enable 4MBpage
;	mov ebx, cr4        ; read current cr4 
;	or  ebx, 0x00000010 ; set PSE  - enable 4MB page
;	mov cr4, ebx        ; update cr4

; enable paging 
   mov ebx, cr0        ; read current cr0
   or  ebx, 0x80000000 ; set PG .  set pages as read-only for both userspace and supervisor, replace 0x80000000 above with 0x80010000, which also sets the WP bit.
   mov cr0, ebx        ; update cr0
   ret                 ; now paging is enabled

halt:
    hlt
    jmp halt

section .data
align 0x1000
global BootPageDirectory
BootPageDirectory:
    ; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
    ; All bits are clear except the following:
    ; bit 7: PS The kernel page is 4MB.
    ; bit 1: RW The kernel page is read/write.
    ; bit 0: P  The kernel page is present.
    ; This entry must be here -- otherwise the kernel will crash immediately after paging is
    ; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
    dd 0x00000083
    times (KERNEL_PAGE_NUMBER - 1) dd 0                 ; Pages before kernel space.
    ; This page directory entry defines a 4MB page containing the kernel.
    dd 0x00000083
    times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0  ; Pages after the kernel image.
The C file:

Code: Select all

#include "../../include/paging.h"
#include "../../include/types.h"
#include "../../include/util.h"

// Paging system thing
// Copyright (C) 2023 Panagiotis

enum page_size_t { FOUR_KB, FOUR_MB };
enum page_privilege_t { SUPERVISOR, USER };
enum page_permissions_t { READ_ONLY, READ_WRITE };

void invlpg(uint32_t addr) {
  asm volatile("invlpg (%0)" : : "b"(addr) : "memory");
}

uint32_t make_page_directory_entry(uint32_t         page_table_address,
                                   enum page_size_t page_size,
                                   bool cache_disabled, bool write_through,
                                   enum page_privilege_t   privelage,
                                   enum page_permissions_t permissions,
                                   bool                    present) {
  uint32_t entry = page_table_address;
  entry |= page_size << 7;
  entry |= cache_disabled << 4;
  entry |= write_through << 3;
  entry |= privelage << 2;
  entry |= permissions << 1;
  entry |= present;

  return entry;
}

uint32_t make_page_table_entry(uint32_t page_frame_address, bool global,
                               bool cache_disabled, bool write_through,
                               enum page_privilege_t   privelage,
                               enum page_permissions_t permissions,
                               bool                    present) {
  uint32_t entry = page_frame_address;
  entry |= global << 8;
  entry |= cache_disabled << 6;
  entry |= write_through << 3;
  entry |= privelage << 2;
  entry |= permissions << 1;
  entry |= present;

  return entry;
}

extern void asmEnablePaging(uint32_t loc);

uint32_t *initialize_page_directory() {
  uint32_t page_dir_virt_addr = &BootPageDirectory;
  uint32_t page_dir_phys_addr = (uint32_t)&BootPageDirectory - 0xC0000000;

  uint32_t page_table_virt_addr = &boot_pagetab1;
  uint32_t page_table_phys_addr = (uint32_t)&boot_pagetab1 - 0xC0000000;

  debugf("[pagedir] virt=%x phys=%x\n[pagetab] virt=%x phys=%x\n",
         page_dir_virt_addr, page_dir_phys_addr, page_table_virt_addr,
         page_table_phys_addr);

  uint32_t *page_dir_ptr = (uint32_t *)page_dir_virt_addr;
  uint32_t *page_table_ptr = (uint32_t *)page_table_virt_addr;

  page_dir_ptr[0] = make_page_directory_entry(
      page_table_phys_addr, FOUR_KB, false, true, SUPERVISOR, READ_WRITE, true);

  page_table_ptr = make_page_table_entry(0x0, true, false, true, SUPERVISOR,
                                         READ_WRITE, true);

  page_dir_ptr[1023] = (uint32_t)make_page_directory_entry(
      page_dir_phys_addr, FOUR_KB, false, false, SUPERVISOR, READ_WRITE, true);
  debugf("enabling paging...\n");
  asmEnablePaging(page_dir_phys_addr);
  // debugf("hi? the var you asked for: [%x]\n", *(uint32_t *)(0x0));

  uint32_t cr3;
  asm volatile("mov %%cr3, %0" : "=r"(cr3));
  debugf("scanned pagedir physical addr: %x\n", cr3);

  return page_dir_ptr;
}
The link.ld file:

Code: Select all

ENTRY(_start)

SECTIONS {
	. = 0x00100000;
	kernel_start = .;

	.multiboot_header ALIGN(4K) : {
		*(.multiboot)
	}

	.boot ALIGN(4K) : {
		*(.boot)
	}

	. += 0xC0000000;

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

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

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

	.bss ALIGN(4K) : AT(ADDR(.bss) - 0xC0000000) {
		*(COMMON)
		*(.bss)
	}

	kernel_end = .;
}
On the commented line which tries to access virtual memory location 0x0, a pagefault probably appears and the whole thing comes crashing down... Any help is largely appreciated, this thing has been bugging me for some time.
Octocontrabass
Member
Member
Posts: 5560
Joined: Mon Mar 25, 2013 7:01 pm

Re: Issues with paging and virtual memory

Post by Octocontrabass »

Code: Select all

   mov ebx, cr0        ; read current cr0
   or  ebx, 0x80000000 ; set PG .  set pages as read-only for both userspace and supervisor, replace 0x80000000 above with 0x80010000, which also sets the WP bit.
   mov cr0, ebx        ; update cr0
   ret                 ; now paging is enabled
Paging is already enabled before you call this function. EBX is a callee-saved register in the System V i386 psABI.

Code: Select all

  page_table_ptr = make_page_table_entry(0x0, true, false, true, SUPERVISOR,
Your compiler should have told you what's wrong with this line. Don't ignore compiler warnings.

Code: Select all

  // debugf("hi? the var you asked for: [%x]\n", *(uint32_t *)(0x0));
A pointer set to 0 is a null pointer, and dereferencing a null pointer is undefined behavior.
cavosdev wrote:a pagefault probably appears
"Probably"? You're not sure? Your virtual machine can tell you what kind of fault it is.
cavosdev
Posts: 5
Joined: Fri Oct 20, 2023 7:16 am

Re: Issues with paging and virtual memory

Post by cavosdev »

After some time I managed to make a working virtual memory manager. What helped greatly that I was not using at the time were QEMU's "-d" flags that showed me what was happening under the hood (mostly page faults and invalid memory location access). What I'd suggest to anyone in a similar spot with memory allocation is the wiki page on QEMU and to use "info mem" to see which memory regions are mapped and with which flags. Thanks for the help!
Post Reply