Skip to content

Pagination

Sukant Pal edited this page Mar 11, 2018 · 12 revisions

Silcos kernel uses the memory protection and organization technique known as paging. In this technique, each virtual address is mapped to a physical address in blocks known as pages. These pages can be of various sizes - 4KB and 2MB (IA32, PAE). The blocks in physical memory are known as page-frames and in virtual memory they are called only pages.

Kernel Memory

Silcos has a "build" time map of the locations of various permanent data structures in kernel memory. This map is declared through sets of macros in Memory/KMemorySpace.h. On changing the values in this file & re-building the kernel, the data structures will be mapped to different offsets in kernel memory. The data structures include -

  1. Platform Information

  2. ACPI Tables & Objects

  3. CPU Status Table

  4. Dynamically-allocable Memory

Platform Information

This is not being used currently and is reserved. It is intended for use to store architectural information - x2APIC enabled, TSC enabled, etc.

ACPI Tables & Objects

This memory is intended for use by the kernel's ACPI subsystem. Later it will be shared by drivers (when the kernel finishes). It is used for mapping ACPI tables in non-cacheable memory currently.

CPU Status Table

This region of memory is intended for storing the struct Processor and related data structures which are usually accessed only by the owner-CPU. Each CPU gets a 32-KB block in kernel-memory which can be accessed by the GetProcessorById and GetIRQTableById macros included in HardwareAbstraction/Processor.h.

Dynamically-allocable Memory

This memory is used for allocating objects & module-segments (plus some other minute purposes). The size of dynamic-memory is decided during boot depending on the amount of physical memory available. The KMemoryManager was built to manage this portion of kernel-memory.

Kernel Paging

The kernel provides a platform-independent interface that allows software to map ranges of virtual addresses to physical addresses or exclusively allocated page-frames. This is done without the help of any MemoryContext object after it has been switched to using switchSpace() because of the recursive-mapping nature of the kernel-pager. The pager is implemented in the KernelHost's Arch folder and is completely platform-dependent.

class Pager
{
public:
	static void switchSpace(MemoryContext *cxt);

	static void dispose(VirtAddr vadr);

	static void disposeAll(VirtAddr base, VirtAddr limit);

	static void map(VirtAddr vadr, PhysAddr padr,
			unsigned allocFlags, PageAttributes attr);
	static void mapAll(VirtAddr base, PhysAddr pbase, unsigned size,
			unsigned allocFlags, PageAttributes attr);
	static void useAllSmall(VirtAddr base, VirtAddr limit,
			unsigned allocFlags, PageAttributes attr);
	static void useAllHuge(VirtAddr base, VirtAddr limit,
			unsigned allocFlags, PageAttributes attr);
	static void use(VirtAddr base, unsigned allocFlags,
			PageAttributes attr);
	static void useAll(VirtAddr base, VirtAddr limit,
			unsigned allocFlags, PageAttributes attr);
private:
	Pager();
};

Pager::switchSpace()

Prototype:

void Pager::switchSpace(MemoryContext *cxt);

Description:

Switches the currently-used memory-context object for mapping pages to page-frames. This is particularly done while switching-processes and remote IPC.

Parameters:

MemoryContext *cxt - The memory-context object that holds the page-tables for mapping and the configuration-flags to control it.

Pager::dispose()

Prototype:

void Pager::dispose(VirtAddr vadr);

Description:

Unmaps the page holding the given address, whether or not it is page-aligned, so that any future access to it will cause a page-fault in this memory-context. It does not test whether a page-frame was freed before calling it and assumes no memory-leak will occur by doing so.

If the caller needs to free this page-frame, it should called GetFrames (Memory/MemoryTransfer.h) and serially free each page-frame descriptor in the list.

Parameters:

VirtAddr vadr - Any address which comes in the boundaries of the given page to be unmapped. This need not be page-aligned.

Pager::disposeAll()

Prototype:

void disposeAll(VirtAddr base, VirtAddr limit);

Description:

Unmaps all the pages in the range base to limit excluding the latter so that any future access will cause a page-fault in this memory-context. It doesn't check whether the page-frame being ripped was freed or not and assumes no memory leak will occur on doing so. Note that if any huge-page was used in this range, then it will also be unmapped in the same way. If the huge-page is larger then the range (base, limit) then it may be still unmapped, but the caller should be knowing how things got mapped.

If the caller wants to free the page-frames, it should call GetFrames (Memory/MemoryTransfer.h) and then free each page-frame in the list serially.

Parameters:

VirtAddr base - Any address in the first-page to be unmapped, without the need to be page-aligned. This "first-page" may also be a huge-page.

VirtAddr limit - Any address in the page after the last-page to be unmapped, without the need to be page-aligned.

Pager::map()

Prototype:

void Pager::map(VirtAddr vadr, PhysAddr padr, unsigned long frFlags, PageAttributes attr);

Description:

Maps the page holding the vadr given to the page-frame holding the padr given as a small-page. It may allocate a page-table if required in the process, with the allocation flags frFlags. The attributes for mapping attr will be applied on the entry of the page.

For kernel specific data, the KernelData macro is best suited for attr and FLG_ATOMIC is most used for the frFlags. Actually, frFlags is used so that initialization routines can get page-tables allocated with special flags like KF_NOINTR and FLG_NOCACHE to bypass optimization in allocation.

This method is generally called when the kernel requires writes to a specific page-frame to communicate with an device or for some other reason.

Parameters:

VirtAddr vadr - Any virtual address in the page that needs to be mapped to the page-frame at the moment, and does not need to be page-aligned.

PhysAddr padr - Any physical address in the page-frame that is mapped to the page, and does not need to be page-frame-aligned.

unsigned long frFlags - Allocation flags for the page-frame allocator if the need to allocate a page-table arises. Normal kernel routines can blindly use FLG_ATOMIC for this unless something special is going on.

PageAttributes attr - The attributes with which the page is mapped.

Pager::mapAll()

Prototype:

void Pager::mapAll(VirtAddr base, PhysAddr pbase, unsigned size, unsigned allocFlags, PageAttributes attr);

Description:

Maps all the pages in the range base to base + size to the pageframes in the range pbase to pbase + size. This may incorporate usage of huge-pages in ranges that are larger than 2 megabytes, when base and pbase are aligned modulo 2 megabytes. In the scenario when page-tables are required to map the respective addresses, they are taken from the page-frame allocator with allocFlags. The mappings are done with the given attributes attr.

Parameters:

VirtAddr base - The address contained in the first page to be mapped to the first pageframe in pbase.

PhysAddr pbase - The physical address contained in the first page-frame to be mapped to the first page.

unsigned size - The amount of memory to be mapped. This should be page-aligned otherwise the page contained the base + size address will be lefted unmapped.

unsigned allocFlags - In case a page-table has to be allocated, it will be taken from the page-frame allocator using these flags.

PageAttributes attr - The mapping attributes for the corresponding pages.