Build your own Operating System #8

Page Frame Allocation

Gayan Malinda
5 min readSep 10, 2021
image from freepik

Hello everyone!

This is the eighth article of the “Build your own Operating System“ article series. In the seventh article of this article series, we knew about Virtual Memory and Paging and how to implement them. I like to suggest, please refer to previous articles, before reading this. It will help you to a better understanding of this article.

In this article, I’m talking about page frame allocation.

Why do we need page frame allocation?

When using virtual memory, how does the OS know which parts of memory are free to use? That is why we need page frame allocation. That is the role of the page frame allocator.

Managing Available Memory

Before managing the available memory first of all we have to know how much memory is available on the computer the OS is running on.

How Much Memory is There?

The simplest 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 the memory used by the kernel as free (since GRUB doesn’t mark this memory as reserved).

There is 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:

These labels can be read using assembly code and pushed on the stack to make them available to C code. Update loader.s file as below:

This way you 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 available memory does not need to be contiguous. In the first 1 MB there are several I/O mapped memory sections, as well as memory used by GRUB and the BIOS. Other parts of the memory might be similarly unavailable.

It’s convenient to divide the memory sections into complete page frames, as we can’t map part of pages into memory.

Managing Available Memory

To know which page frames are in use, The page frame allocator needs to keep track of which are free and which are not. There are several ways to do this: bitmaps, linked lists, trees, the Buddy System (used by Linux), etc.

Click here to see more information about the different algorithms.

Bitmaps are quite easy to implement. One bit is used for each page frame and one (or more) page frames are dedicated to store the bitmap.

Note that this is only one method; other ideas may be better and enjoyable to apply.

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?

We need to map the page frame into virtual memory, by updating the PDT and/or PT used by the kernel.

If all available page tables are full, Then we can not map the page frame into memory, because we have needed a new page table, which takes up an entire page frame, and to write to this page frame we have need to map its page frame. Somehow this circular dependency must be broken.

One solution is to reserve a part of the first-page table used by the kernel (or some other higher-half page table) for temporarily mapping page frames to make them accessible. If the kernel is mapped at 0xC0000000 (page directory entry with index 768), and 4 KB page frames are used, then the kernel has at least one-page table. If we assume or limit us to a kernel of size at most 4 MB minus 4 KB we can dedicate the last entry (entry 1023) of this page table for temporary mappings. The virtual address of pages mapped using the last entry of the kernel’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 have 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.

Dennis M. Ritchie Brian W. Kernighan, 1988. The c programming language, second edition. Prentice Hall, Inc. http://cslabcms.nju.edu.cn/problem_solving/images/c/cc/The_C_Programming_Language_%282nd_Edition_Ritchie_Kernighan%29.pdf

The above-given book has an example implementation in this book that we can draw inspiration from. The only modification we need to do is to replace calls to sbrk/brk with calls to the page frame allocator when more memory is needed. We must also make sure to map the page frames returned by the page frame allocator to virtual addresses. A correct implementation should also return page frames to the page frame allocator on call to free, whenever sufficiently large blocks of memory are freed.

I hope you got a good understanding of page frame allocation. Let’s meet with the next article of the ‘Build your own Operating System’ series. Thank you so much for reading!

The Little OS Book: https://littleosbook.github.io/book.pdf

The OSDev wiki page on page frame allocation: https://wiki.osdev.org/Page_Frame_Allocation

Writing A Page Frame Allocator: https://wiki.osdev.org/Writing_A_Page_Frame_Allocator

Previous articles:

--

--

Gayan Malinda

Software Engineering Undergraduate - University of Kelaniya, Sri Lanka