Build Your Own OS #8

Niroshan Pushparaj
5 min readSep 11, 2021

Hello friends, welcome to 8th part of OS making series. Today we talking about page frame allocation

OSDev wiki

Page Frame Allocation

First, we need to know how much memory is accessible on the PC the OS is running on. The least demanding approach to do this is to peruse it from the multiboot structure passed to us by GRUB. GRUB gathers the data we need about the memory — what is saved, I/O planned, read-just and so on We should likewise ensure that we don’t stamp the piece of memory utilized by the portion as free (since GRUB doesn’t check this memory as saved). One approach to know how much memory the part utilizes is to send out names toward the start and the finish of the bit paired 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;

These labels can directly be read from assembly code and pushed on the stack to make them available to C code:

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

This way we get the labels as arguments to kmain. If you want to use C instead of assembly code, one way to do it is to declare the labels as functions and take the addresses of these functions:

void kernel_virtual_start(void);    /* ... */    unsigned int vaddr = (unsigned int) &kernel_virtual_start;

If you use GRUB modules you need to make sure the memory they use is marked as reserved as well.

Note that the accessible memory shouldn’t be coterminous. In the initial 1 MB, there are a few I/O-planned memory segments, just as memory utilized by GRUB and the BIOS. Different pieces of the memory may be likewise inaccessible.

It’s helpful to isolate the memory areas into complete page outlines, as we can’t plan part of pages into memory.

Managing Available Memory

How would we realize which page outlines are being used? The page outline allocator needs to monitor which are free and which aren’t. There are a few different ways to do this: bitmaps, connected records, trees, the Buddy System (utilized by Linux), and so on For more data about the various calculations see the article on OSDev.

Bitmaps are very simple to execute. The slightest bit is utilized for each page edge and (at least one) page outlines are committed to storing the bitmap. (Note that this is only one approach to do it, different plans may be better as well as more amusing to carry out.)

How Might We Access a Page Frame?

The page outline allocator returns the actual beginning location of the page outline. This page outline isn’t planned in — no page table focuses to this page outline. How might we peruse and compose information to the casing?

We need to plan the page outline into virtual memory, by refreshing the PDT and additionally PT utilized by the portion. Imagine a scenario where all accessible page tables are full. Then, at that point, we can’t plan the page outline into memory, since we’d need another page table — which takes up a whole page outline — and to keep in touch with this page outline we’d need to plan its page outline… Somehow this round reliance should be broken.

One arrangement is to save a piece of the main page table utilized by the portion (or some other higher-half page table) for briefly planning page casings to make them open. On the off chance that the part is planned at 0xC0000000(page registry section with file 768), and 4 KB page outlines are utilized, then, at that point, the bit has something like a one-page table. On the off chance that we expect — or restrict us to — a bit of size all things considered 4 MB less 4 KB we can commit the last passage (section 1023) of this page table for transitory mappings. The virtual location of pages planned in utilizing the last passage of the bit’s PT will be:

(768 << 22) | (1023 << 12) | 0x000 = 0xC03FF000

After we’ve temporarily mapped the page frame we want to use as a page table, and set it up to map in our first-page frame, we can add it to the paging directory, and remove the temporary mapping.

A Kernel Heap

So far we’ve simply had the option to work with fixed-size information, or straightforwardly with crude memory. Since we have a page outline allocator we can carry out mallocand be freeto use it in the bit.

Kernighan and Ritchie have a model execution in their book [8] that we can draw motivation from. The solitary alteration we need to do is to supplant calls to sbrk/brkwith calls to the page outline allocator when more memory is required. We should likewise try to plan the page outlines returned by the page outline allocator to virtual addresses. A right execution ought to likewise return page casings to the page outline allocator available to come into work to free, at whatever point adequately huge squares of memory are liberated.

--

--