1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#pragma once
4
5#include <mos/lib/structures/list.hpp>
6#include <mos/mos_global.h>
7#include <mos/types.hpp>
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 additional_info
38 {
39 list_node_t list_node; // for use of freelist in the buddy allocator
40
41 struct pagecache_frame_info // allocated frame
42 {
43 // for page cache frames
44 bool dirty : 1; ///< 1 if the page is dirty
45 } pagecache;
46 } info;
47
48 union alloc_info
49 {
50 // number of times this frame is mapped, if this drops to 0, the frame is freed
51 atomic_t refcount;
52 } alloc;
53} phyframe_t;
54
55MOS_STATIC_ASSERT(sizeof(phyframe_t) == 32, "update phyframe_t struct size");
56
57typedef struct
58{
59 pfn_t pfn_start;
60 size_t nframes;
61 bool reserved;
62 u32 type; // platform-specific type
63} pmm_region_t;
64
65typedef enum
66{
67 /// allocate normal pages
68 PMM_ALLOC_NORMAL = 0,
69} pmm_allocation_flags_t;
70
71extern phyframe_t *phyframes; // array of all physical frames
72
73#define phyframe_pfn(frame) ((pfn_t) ((frame) - phyframes))
74#define pfn_phyframe(pfn) (&phyframes[(pfn)])
75
76extern size_t pmm_total_frames, pmm_allocated_frames, pmm_reserved_frames;
77
78/**
79 * @brief Dump the physical memory manager's state, (i.e. the free list and the allocated list).
80 */
81void pmm_dump_lists(void);
82
83void pmm_init();
84
85/**
86 * @brief Allocate n_frames of contiguous physical memory.
87 *
88 * @param n_frames Number of frames to allocate.
89 * @param flags Allocation flags.
90 * @return phyframe_t* Pointer to the first frame of the allocated block.
91 */
92phyframe_t *pmm_allocate_frames(size_t n_frames, pmm_allocation_flags_t flags);
93void pmm_free_frames(phyframe_t *start_frame, size_t n_pages);
94
95/**
96 * @brief Mark a range of physical memory as reserved.
97 *
98 * @param pfn Physical frame number of the block to reserve.
99 * @param npages Number of pages to reserve.
100 * @return pfn_t Physical frame number of the first frame in the reserved block.
101 *
102 * @note The memory will be marked as `PMM_REGION_RESERVED` and will be moved to the allocated list.
103 *
104 * @note If the range does not exist in the free list and overlaps with an allocated block,
105 * the kernel panics.
106 *
107 */
108pfn_t pmm_reserve_frames(pfn_t pfn, size_t npages);
109#define pmm_reserve_address(paddr) pmm_reserve_frames(ALIGN_DOWN_TO_PAGE(paddr) / MOS_PAGE_SIZE, 1)
110#define pmm_reserve_addresses(paddr, npages) pmm_reserve_frames(ALIGN_DOWN_TO_PAGE(paddr) / MOS_PAGE_SIZE, npages)
111
112/**
113 * @brief Find a region in the physical memory manager.
114 *
115 * @param needle One address in the region to find.
116 * @return pmm_region_t* The region found, or NULL if not found.
117 */
118pmm_region_t *pmm_find_reserved_region(ptr_t needle);
119
120// ! ref and unref frames
121
122phyframe_t *_pmm_ref_phyframes(phyframe_t *frame, size_t npages);
123void _pmm_unref_phyframes(phyframe_t *frame, size_t npages);
124
125should_inline pfn_t _pmm_ref_pfn_range(pfn_t pfn_start, size_t npages)
126{
127 _pmm_ref_phyframes(pfn_phyframe(pfn_start), npages);
128 return pfn_start;
129}
130
131should_inline void _pmm_unref_pfn_range(pfn_t pfn_start, size_t npages)
132{
133 _pmm_unref_phyframes(pfn_phyframe(pfn_start), npages);
134}
135
136#ifdef __cplusplus
137should_inline phyframe_t *pmm_ref(phyframe_t *frame, size_t npages = 1)
138{
139 return _pmm_ref_phyframes(frame, npages);
140}
141should_inline pfn_t pmm_ref(pfn_t pfn_start, size_t npages = 1)
142{
143 return _pmm_ref_pfn_range(pfn_start, npages);
144}
145
146should_inline void pmm_unref(phyframe_t *frame, size_t npages = 1)
147{
148 _pmm_unref_phyframes(frame, npages);
149}
150should_inline void pmm_unref(pfn_t pfn_start, size_t npages = 1)
151{
152 _pmm_unref_pfn_range(pfn_start, npages);
153}
154#else
155#define pmm_ref(thing, npages) _Generic((thing), phyframe_t *: _pmm_ref_phyframes, pfn_t: _pmm_ref_pfn_range)(thing, npages)
156#define pmm_unref(thing, npages) _Generic((thing), phyframe_t *: _pmm_unref_phyframes, pfn_t: _pmm_unref_pfn_range)(thing, npages)
157#endif
158
159#define pmm_ref_one(thing) pmm_ref(thing, 1)
160#define pmm_unref_one(thing) pmm_unref(thing, 1)
161
162/** @} */
163