1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#pragma once
4
5#include <mos/lib/structures/list.h>
6#include <mos/mos_global.h>
7#include <mos/types.h>
8
9/**
10 * @defgroup pmm Physical Memory Manager
11 * @ingroup mm
12 * @brief Allocate/free the physical memory, in the form of physical frames.
13 *
14 * @details
15 * The physical memory manager is responsible for allocating and freeing physical memory.
16 *
17 * @note The allocated physical memory is not zeroed out, in fact, it does nothing to the real
18 * memory content except for marking it as allocated.
19 *
20 * @{
21 */
22
23typedef struct phyframe phyframe_t;
24
25// represents a physical frame, there will be one `phyframe_t` for each physical frame in the system
26typedef struct phyframe
27{
28 enum phyframe_state
29 {
30 PHYFRAME_RESERVED = 0, // intentionally 0 (initially, all frames are reserved)
31 PHYFRAME_FREE,
32 PHYFRAME_ALLOCATED,
33 } state;
34
35 u8 order;
36
37 union
38 {
39 MOS_WARNING_PUSH
40 MOS_WARNING_DISABLE("-Wpedantic") // ISO C++ forbids anonymous structs
41
42 struct // free frame
43 {
44 as_linked_list; // for use of freelist in the buddy allocator
45 };
46
47 struct
48 {
49 // for page cache frames
50 bool dirty : 1; ///< 1 if the page is dirty
51 } pagecache;
52 MOS_WARNING_POP
53 };
54
55 union
56 {
57 // number of times this frame is mapped, if this drops to 0, the frame is freed
58 atomic_t allocated_refcount;
59 };
60} phyframe_t;
61
62MOS_STATIC_ASSERT(sizeof(phyframe_t) == 32, "update phyframe_t struct size");
63
64typedef struct
65{
66 pfn_t pfn_start;
67 size_t nframes;
68 bool reserved;
69 u32 type; // platform-specific type
70} pmm_region_t;
71
72typedef enum
73{
74 /// allocate normal pages
75 PMM_ALLOC_NORMAL = 0,
76} pmm_allocation_flags_t;
77
78extern phyframe_t *phyframes; // array of all physical frames
79
80#define phyframe_pfn(frame) ((pfn_t) ((frame) - phyframes))
81#define pfn_phyframe(pfn) (&phyframes[(pfn)])
82
83extern size_t pmm_total_frames, pmm_allocated_frames, pmm_reserved_frames;
84
85/**
86 * @brief Dump the physical memory manager's state, (i.e. the free list and the allocated list).
87 */
88void pmm_dump_lists(void);
89
90void pmm_init();
91
92/**
93 * @brief Allocate n_frames of contiguous physical memory.
94 *
95 * @param n_frames Number of frames to allocate.
96 * @param flags Allocation flags.
97 * @return phyframe_t* Pointer to the first frame of the allocated block.
98 */
99phyframe_t *pmm_allocate_frames(size_t n_frames, pmm_allocation_flags_t flags);
100void pmm_free_frames(phyframe_t *start_frame, size_t n_pages);
101
102/**
103 * @brief Mark a range of physical memory as reserved.
104 *
105 * @param pfn Physical frame number of the block to reserve.
106 * @param npages Number of pages to reserve.
107 * @return pfn_t Physical frame number of the first frame in the reserved block.
108 *
109 * @note The memory will be marked as `PMM_REGION_RESERVED` and will be moved to the allocated list.
110 *
111 * @note If the range does not exist in the free list and overlaps with an allocated block,
112 * the kernel panics.
113 *
114 */
115pfn_t pmm_reserve_frames(pfn_t pfn, size_t npages);
116#define pmm_reserve_address(paddr) pmm_reserve_frames(ALIGN_DOWN_TO_PAGE(paddr) / MOS_PAGE_SIZE, 1)
117#define pmm_reserve_addresses(paddr, npages) pmm_reserve_frames(ALIGN_DOWN_TO_PAGE(paddr) / MOS_PAGE_SIZE, npages)
118
119/**
120 * @brief Find a region in the physical memory manager.
121 *
122 * @param needle One address in the region to find.
123 * @return pmm_region_t* The region found, or NULL if not found.
124 */
125pmm_region_t *pmm_find_reserved_region(ptr_t needle);
126
127// ! ref and unref frames
128
129phyframe_t *_pmm_ref_phyframes(phyframe_t *frame, size_t npages);
130void _pmm_unref_phyframes(phyframe_t *frame, size_t npages);
131
132should_inline pfn_t _pmm_ref_pfn_range(pfn_t pfn_start, size_t npages)
133{
134 _pmm_ref_phyframes(pfn_phyframe(pfn_start), npages);
135 return pfn_start;
136}
137
138should_inline void _pmm_unref_pfn_range(pfn_t pfn_start, size_t npages)
139{
140 _pmm_unref_phyframes(pfn_phyframe(pfn_start), npages);
141}
142
143#define pmm_ref(thing, npages) _Generic((thing), phyframe_t *: _pmm_ref_phyframes, pfn_t: _pmm_ref_pfn_range)(thing, npages)
144#define pmm_unref(thing, npages) _Generic((thing), phyframe_t *: _pmm_unref_phyframes, pfn_t: _pmm_unref_pfn_range)(thing, npages)
145
146#define pmm_ref_one(thing) pmm_ref(thing, 1)
147#define pmm_unref_one(thing) pmm_unref(thing, 1)
148
149/** @} */
150