Build Your Own OS #7

Virtual Memory

How Much Memory is There?

First we need to know how much memory is available on the computer the OS is running on. The easiest way to do this is to read it from the multiboot structure passed to us by GRUB. GRUB collects the information we need about the memory — what is reserved, I/O mapped, read-only etc. We must also make sure that we don’t mark the part of memory used by the kernel as free (since GRUB doesn’t mark this memory as reserved). One way to know how much memory the kernel uses is to export labels at the beginning and the end of the kernel binary from the linker script:

ENTRY(loader)           /* the name of the entry symbol */    . = 0xC0100000          /* the code should be relocated to 3 GB + 1 MB */    /* these labels get exported to the code files */
kernel_virtual_start = .;
kernel_physical_start = . - 0xC0000000; /* align at 4 KB and load at 1 MB */
.text ALIGN (0x1000) : AT(ADDR(.text)-0xC0000000)
{
*(.text) /* all text sections from all files */
} /* align at 4 KB and load at 1 MB + . */
.rodata ALIGN (0x1000) : AT(ADDR(.rodata)-0xC0000000)
{
*(.rodata*) /* all read-only data sections from all files */
} /* align at 4 KB and load at 1 MB + . */
.data ALIGN (0x1000) : AT(ADDR(.data)-0xC0000000)
{
*(.data) /* all data sections from all files */
} /* align at 4 KB and load at 1 MB + . */
.bss ALIGN (0x1000) : AT(ADDR(.bss)-0xC0000000)
{
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
} kernel_virtual_end = .;
kernel_physical_end = . - 0xC0000000;
extern kernel_virtual_start
extern kernel_virtual_end
extern kernel_physical_start
extern kernel_physical_end ; ... push kernel_physical_end
push kernel_physical_start
push kernel_virtual_end
push kernel_virtual_start call kmain
void kernel_virtual_start(void);    /* ... */    unsigned int vaddr = (unsigned int) &kernel_virtual_start;

Managing Available Memory

How do we know which page frames are in use? The page frame allocator needs to keep track of which are free and which aren’t. There are several ways to do this: bitmaps, linked lists, trees, the Buddy System (used by Linux) etc. For more information about the different algorithms see the article on OSDev [38].

How Can We Access a Page Frame?

The page frame allocator returns the physical start address of the page frame. This page frame is not mapped in — no page table points to this page frame. How can we read and write data to the frame?

(768 << 22) | (1023 << 12) | 0x000 = 0xC03FF000
global enablePaging                                               enablePaging:                       ; 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

A Kernel Heap

So far we’ve only been able to work with fixed-size data, or directly with raw memory. Now that we have a page frame allocator we can implement malloc and free to use in the kernel.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store