> ## Documentation Index
> Fetch the complete documentation index at: https://kernel.mvhcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Physical Memory Manager API Reference — MVH Kernel

> Reference for the MVH Kernel Physical Memory Manager API: pmm_init, pmm_alloc_pages, pmm_free_pages, pmm_get_stats, and pmm_self_test with usage examples.

The Physical Memory Manager (PMM) is responsible for tracking and allocating 4 KiB physical pages across the identity-mapped first 1024 MiB of physical address space. It uses a bitmap-backed allocator, where each bit represents one 4 KiB page. Before you can call any allocation function, you must initialize the PMM with the total available RAM and the end address of the kernel image so that kernel memory is correctly marked reserved.

## Data Types

The `pmm_stats_t` struct holds a snapshot of PMM counters at the moment `pmm_get_stats` is called. Its definition from `mvh/memory.h` is:

```c theme={null}
typedef struct {
    uint64_t total_pages;          /* Total physical pages visible to the PMM    */
    uint64_t used_pages;           /* Pages currently allocated                   */
    uint64_t free_pages;           /* Pages currently available                   */
    uint64_t reserved_pages;       /* Pages reserved (kernel image, bitmap, etc.) */
    uint64_t allocation_requests;  /* Cumulative pmm_alloc_pages calls            */
    uint64_t free_requests;        /* Cumulative pmm_free_pages calls             */
    uint64_t failed_allocations;   /* Cumulative failed allocations               */
    uint64_t peak_used_pages;      /* High-water mark of used_pages               */
} pmm_stats_t;
```

***

## Functions

### `pmm_init`

```c theme={null}
void pmm_init(uint64_t memory_kib, uintptr_t kernel_end);
```

Initializes the Physical Memory Manager. You must call this function exactly once, before any call to `pmm_alloc_pages`, typically as one of the first steps in your kernel entry point. The PMM marks all pages occupied by the kernel image and its own bitmap as reserved so they are never returned to callers.

<ParamField path="memory_kib" type="uint64_t" required>
  Total usable RAM in kibibytes, as reported by the bootloader (for example, from a Multiboot2 memory map). The PMM caps its working range at the identity-mapped first 1024 MiB (1,048,576 KiB); any value above this is silently clamped.
</ParamField>

<ParamField path="kernel_end" type="uintptr_t" required>
  Physical address of the first byte beyond the end of the loaded kernel image. All pages from physical address `0` up to and including the page containing `kernel_end - 1` are marked reserved and will never be allocated. Pass the value of the linker-exported `_kernel_end` symbol here.
</ParamField>

***

### `pmm_alloc_pages`

```c theme={null}
void *pmm_alloc_pages(uint32_t count);
```

Allocates a run of `count` physically contiguous 4 KiB pages and returns a pointer to the first page. The returned pointer is a direct physical address within the identity-mapped region, so you can use it for both physical and virtual access without any additional translation. The PMM searches for the first sufficiently large contiguous free run in its bitmap.

<ParamField path="count" type="uint32_t" required>
  Number of contiguous 4 KiB pages to allocate. Must be greater than zero. Requesting more pages than are currently free, or requesting a run that cannot be satisfied contiguously, returns `NULL`.
</ParamField>

<ResponseField name="return" type="void *">
  Pointer to the first byte of the allocated region on success, or `NULL` if the allocation fails (insufficient free pages, no contiguous run of the requested length, or the PMM has not been initialized). The returned pointer is always page-aligned (a multiple of 4096).
</ResponseField>

<Note>
  All allocated pages are **physically contiguous**. If you need, for example, 16 KiB of DMA-safe memory, pass `count = 4` and you are guaranteed to receive four consecutive pages with no gaps.
</Note>

***

### `pmm_free_pages`

```c theme={null}
void pmm_free_pages(void *address, uint32_t count);
```

Returns a previously allocated run of pages to the free pool. You must pass exactly the same `address` and `count` that were returned or used in the corresponding `pmm_alloc_pages` call. Partial frees and double-frees produce undefined behavior.

<ParamField path="address" type="void *" required>
  The pointer that was returned by `pmm_alloc_pages`. Must be page-aligned and within the identity-mapped region. Passing an unaligned, out-of-range, or already-freed address is undefined behavior.
</ParamField>

<ParamField path="count" type="uint32_t" required>
  The number of pages to free. This value must exactly match the `count` argument that was passed to the `pmm_alloc_pages` call that produced `address`. Passing a larger or smaller value corrupts the bitmap.
</ParamField>

<Warning>
  The PMM does **not** perform double-free detection. Freeing the same range twice silently corrupts the bitmap and will lead to two callers receiving overlapping physical memory. Always track allocation lifetimes carefully at the call site.
</Warning>

***

### `pmm_get_stats`

```c theme={null}
void pmm_get_stats(pmm_stats_t *stats);
```

Fills the `pmm_stats_t` structure pointed to by `stats` with a consistent snapshot of the PMM's current counters. Use this function to monitor physical memory pressure or to produce diagnostic output.

<ParamField path="stats" type="pmm_stats_t *" required>
  Pointer to a caller-allocated `pmm_stats_t` struct. Must not be `NULL`. All fields are overwritten on return.
</ParamField>

<ResponseField name="stats.total_pages" type="uint64_t">
  Total number of 4 KiB pages visible to the PMM (derived from `memory_kib` at `pmm_init` time, capped at 1024 MiB).
</ResponseField>

<ResponseField name="stats.used_pages" type="uint64_t">
  Number of pages currently allocated by callers (excludes reserved pages).
</ResponseField>

<ResponseField name="stats.free_pages" type="uint64_t">
  Number of pages currently available for allocation.
</ResponseField>

<ResponseField name="stats.reserved_pages" type="uint64_t">
  Number of pages permanently reserved for the kernel image, the PMM bitmap, and other non-allocatable regions.
</ResponseField>

<ResponseField name="stats.allocation_requests" type="uint64_t">
  Cumulative count of `pmm_alloc_pages` calls since `pmm_init`.
</ResponseField>

<ResponseField name="stats.free_requests" type="uint64_t">
  Cumulative count of `pmm_free_pages` calls since `pmm_init`.
</ResponseField>

<ResponseField name="stats.failed_allocations" type="uint64_t">
  Cumulative count of `pmm_alloc_pages` calls that returned `NULL`.
</ResponseField>

<ResponseField name="stats.peak_used_pages" type="uint64_t">
  The highest value `used_pages` has ever reached since `pmm_init`. Useful for capacity-planning.
</ResponseField>

***

### `pmm_self_test`

```c theme={null}
int pmm_self_test(void);
```

Runs the PMM's built-in self-test suite. The test allocates and frees pages in various patterns to verify bitmap correctness, contiguous-run detection, and stat accounting. Call this once during early kernel initialization to validate PMM integrity on a new platform or after any change to PMM internals.

<ResponseField name="return" type="int">
  `0` if all self-test cases pass. A non-zero value indicates the specific sub-test that failed; check the kernel serial log for a human-readable description.
</ResponseField>

<Tip>
  Run `pmm_self_test` immediately after `pmm_init` in debug builds. It is safe to run against live PMM state because the test allocates and fully restores any pages it touches before returning.
</Tip>

***

## Usage Example

The following example shows the canonical alloc → use → free pattern, along with how to inspect PMM statistics.

```c theme={null}
#include <mvh/memory.h>

void example_pmm_usage(void) {
    /* Allocate 4 contiguous pages (16 KiB) */
    void *buf = pmm_alloc_pages(4);
    if (buf == NULL) {
        /* Handle allocation failure — check stats to understand why */
        pmm_stats_t stats;
        pmm_get_stats(&stats);
        klog("PMM alloc failed: free_pages=%llu, failed=%llu\n",
             stats.free_pages, stats.failed_allocations);
        return;
    }

    /* Use the physically contiguous buffer (e.g. for a DMA descriptor ring) */
    uint8_t *p = (uint8_t *)buf;
    for (int i = 0; i < 4 * 4096; i++) {
        p[i] = 0;
    }

    /* Inspect current PMM state */
    pmm_stats_t stats;
    pmm_get_stats(&stats);
    klog("PMM: used=%llu free=%llu peak=%llu\n",
         stats.used_pages, stats.free_pages, stats.peak_used_pages);

    /* Free the pages — count must match the original allocation */
    pmm_free_pages(buf, 4);
}
```
