123 lines
4.8 KiB
ReStructuredText
123 lines
4.8 KiB
ReStructuredText
Memory management functions
|
|
===========================
|
|
|
|
The document describes the facilities implemented for managing virtual and physical
|
|
memory and related resources.
|
|
|
|
All virtual memory is split into two spaces: kernel and user. ``0xFFFFFF0000000000``
|
|
is the boundary, any addresses beyond which are considered kernel-space and any address
|
|
lower which is user-space.
|
|
|
|
Physical memory management
|
|
--------------------------
|
|
|
|
Kernel functions can allocate physical memory using the following two functions::
|
|
|
|
uintptr_t mm_phys_alloc_page(void);
|
|
uintptr_t mm_phys_alloc_contiguous(size_t count);
|
|
|
|
These two functions return physical pointers (or ``MM_NADDR`` in case of failure).
|
|
The first one allocates and returns a single page, whereas the latter one attempts
|
|
an allocation of contiguous physical range of ``count`` 4KiB pages.
|
|
|
|
After usage, the pages can be freed using ``mm_phys_free_page()``::
|
|
|
|
void mm_phys_free_page(uintptr_t addr)
|
|
|
|
This function only deallocates a single page, so for contiguous ranges, a loop is
|
|
required::
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
mm_phys_free_page(addr + i * MM_PAGE_SIZE);
|
|
}
|
|
|
|
Kernel heap
|
|
-----------
|
|
|
|
For small and frequent allocations, heap is available::
|
|
|
|
void *kmalloc(size_t size);
|
|
void kfree(void *ptr);
|
|
|
|
These two functions should work exactly as ``malloc(3)``/``free(3)`` everyone's
|
|
familiar with, so no further description is needed.
|
|
|
|
Kernel virtual memory management
|
|
--------------------------------
|
|
|
|
Function useful in developing kernel features are described here. For userspace virtual
|
|
memory facilities see `Userspace memory management`_.
|
|
|
|
The kernel has lower 1GiB of physical memory mapped at ``0xFFFFFF0000000000``, which
|
|
allows for easier access to physical memory without needing to map it first.
|
|
|
|
``MM_VIRTUALIZE(addr)`` macro is used to convert a physical memory address into a
|
|
kernel-space pointer.
|
|
|
|
Likewise, ``MM_PHYS(addr)`` macro converts the virtual address to physical one.
|
|
|
|
.. warning ::
|
|
These functions don't provide any boundary check and may cause undefined behavior
|
|
if provided invalid input.
|
|
|
|
Userspace memory management
|
|
---------------------------
|
|
|
|
Userspace memory consists of two kinds of virtual regions:
|
|
|
|
1. Unique ``virt`` -> ``phys`` mappings. These always correspond to unqiue physical
|
|
pages.
|
|
2. Shared memory regions (``mmap()`` ed or any other kind). These may refer either
|
|
to shared physical memory regions, or can be file/device-mapped.
|
|
|
|
Primary function for manipulating non-shared mappings is ``mm_map_single()`` function::
|
|
|
|
int mm_map_single(mm_space_t space, uintptr_t virt, uintptr_t phys, uint64_t flags);
|
|
|
|
The function is used to map a single virtual memory page to a physical address, creating
|
|
``virt .. virt + MM_PAGE_SIZE`` -> ``phys .. phys + MM_PAGE_SIZE`` association. Currently,
|
|
only 4KiB pages can be mapped this way. The ``flags`` parameter controls permission bits
|
|
for the page and can be one of the following:
|
|
|
|
* ``MM_PAGE_USER`` --- the page is accessible from userspace code
|
|
* ``MM_PAGE_WRITE`` --- the page is writable
|
|
* ``MM_PAGE_EXEC`` --- code execution is allowed for this page
|
|
|
|
A mapped address can be queried for its corresponding physical address using
|
|
``mm_map_get()``::
|
|
|
|
uintptr_t mm_map_get(mm_space_t space, uintptr_t virt, uint64_t *flags)
|
|
|
|
Corresponding physical address is returned on success, optionally setting ``*flags``
|
|
to mapping permission bits. In case of failure (the address is not mapped), ``MM_NADDR``
|
|
is returned.
|
|
|
|
Finally, once the virtual mapping is no longer needed, it can be removed from
|
|
process' virtual address tables using ``mm_umap_single``::
|
|
|
|
uintptr_t mm_umap_single(mm_space_t space, uintptr_t virt, uint32_t size);
|
|
|
|
``size`` parameter here limits which kinds of pages are unmapped, returning ``MM_NADDR``
|
|
if trying, for example, to unmap a 4KiB page, but the virtual address actually represens
|
|
2MiB page. ``size`` takes the following values:
|
|
|
|
* 0 --- Any mapping is removed
|
|
* 1 --- 4KiB mapping is removed
|
|
|
|
On success, this function will return physical memory page address which was referred
|
|
to by ``virt`` in the memory space. Otherwise, ``MM_NADDR`` is reported.
|
|
|
|
Contiguous regions in memory spaces can be bound to physical memory using ``vmfind()``
|
|
and ``vmalloc()`` functions::
|
|
|
|
uintptr_t vmfind(const mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages);
|
|
uintptr_t vmalloc(mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages, uint64_t flags);
|
|
|
|
``vmfind()`` searches for a contiguous free space of ``npages`` * 4KiB pages in
|
|
``from .. to`` range and returns it. ``vmalloc()`` does the same, additionally mapping
|
|
the region to a set of physical pages with given permission ``flags``. Both functions
|
|
return ``MM_NADDR`` in case of failure.
|
|
|
|
Then, ``vmfree()`` function can be used to release the region, unmapping and freeing
|
|
physical pages.
|