MOS Source Code
Loading...
Searching...
No Matches
pmm.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Public API for the Physical Memory Manager
3
5
6#include "mos/assert.h"
7#include "mos/mm/mm.h"
10#include "mos/syslog/printk.h"
11
12#include <mos_stdlib.h>
13#include <mos_string.h>
14
16size_t pmm_total_frames = 0; // system pfn <= pfn_max
19
20void pmm_init(void)
21{
22 pr_dinfo2(pmm, "setting up physical memory manager...");
23 MOS_ASSERT_ONCE("pmm_init should only be called once");
24
25 const size_t phyframes_npages = ALIGN_UP_TO_PAGE(platform_info->max_pfn * sizeof(phyframe_t)) / MOS_PAGE_SIZE;
26 pr_dinfo2(pmm, "%zu pages required for the phyframes array with %llu pages in total", phyframes_npages, platform_info->max_pfn);
27
28 const pmm_region_t *phyframes_region = NULL; // the region that will hold the phyframes array
29 pfn_t phyframes_pfn = 0;
30
31 // now we need to find contiguous memory for the phyframes array
32 for (u32 i = 0; i < platform_info->num_pmm_regions; i++)
33 {
34 const pmm_region_t *const r = &platform_info->pmm_regions[i];
35
36 if (r->reserved)
37 {
38 pr_dinfo2(pmm, "skipping reserved region " PFNADDR_RANGE, PFNADDR(r->pfn_start, r->pfn_start + r->nframes));
39 continue;
40 }
41
42 if (r->nframes < phyframes_npages)
43 {
44 pr_dinfo2(pmm, "skipping region " PFNADDR_RANGE " because it's too small", PFNADDR(r->pfn_start, r->pfn_start + r->nframes));
45 continue; // early out if this region is too small
46 }
47
48 phyframes_pfn = r->pfn_start;
49 phyframes_region = r;
50
51 pr_dinfo2(pmm, "using " PFNADDR_RANGE " for the phyframes array", PFNADDR(phyframes_pfn, phyframes_pfn + phyframes_npages));
52
53 // ! initialise phyframes array
54 phyframes = (void *) pfn_va(phyframes_pfn);
55 memzero(phyframes, phyframes_npages * MOS_PAGE_SIZE);
58 pmm_reserve_frames(phyframes_pfn, phyframes_npages);
59 break;
60 }
61
62 MOS_ASSERT_X(phyframes && phyframes_region, "failed to find a region for the phyframes array");
63
64 // add all the other regions
65 for (u32 i = 0; i < platform_info->num_pmm_regions; i++)
66 {
68 if (r == phyframes_region)
69 continue;
70 if (r->nframes == 0) // ???
71 mos_warn_once("region " PFN_FMT " has 0 frames", r->pfn_start);
72 if (r->reserved)
73 {
75 continue; // we ignore any reserved regions that are outside of the max_pfn
77 }
78 }
79
80 MOS_ASSERT_X(phyframes[0].state == PHYFRAME_RESERVED, "phyframe 0 isn't reserved, things have gone horribly wrong");
81 pr_dinfo2(pmm, "initialised");
82}
83
85{
86 pr_info("Physical Memory Manager dump:");
88}
89
90MOS_PANIC_HOOK_FEAT(pmm, pmm_dump_lists, "dump physical allocator lists");
91
93{
95 phyframe_t *frame = buddy_alloc_n_exact(n_frames);
96 if (!frame)
97 return NULL;
98 const pfn_t pfn = phyframe_pfn(frame);
99 pr_dinfo2(pmm, "allocated " PFN_RANGE ", %zu pages", pfn, pfn + n_frames, n_frames);
100
101 for (size_t i = 0; i < n_frames; i++)
102 pfn_phyframe(pfn + i)->allocated_refcount = 0;
103
104 pmm_allocated_frames += n_frames;
105 return frame;
106}
107
108void pmm_free_frames(phyframe_t *start_frame, size_t n_pages)
109{
110 const pfn_t start = phyframe_pfn(start_frame);
111 pr_dinfo2(pmm, "freeing " PFN_RANGE ", %zu pages", start, start + n_pages - 1, n_pages);
112 for (pfn_t pfn = start; pfn < start + n_pages; pfn++)
113 {
114 phyframe_t *frame = pfn_phyframe(pfn);
115 linked_list_init(list_node(frame)); // sanitize the list node
116 buddy_free_n(phyframe_pfn(frame), 1);
117 }
118
119 pmm_allocated_frames -= n_pages;
120}
121
122pfn_t pmm_reserve_frames(pfn_t pfn_start, size_t npages)
123{
124 MOS_ASSERT_X(pfn_start + npages <= pmm_total_frames, "out of bounds: " PFN_RANGE ", %zu pages", pfn_start, pfn_start + npages - 1, npages);
125 pr_dinfo2(pmm, "reserving " PFN_RANGE ", %zu pages", pfn_start, pfn_start + npages - 1, npages);
126 buddy_reserve_n(pfn_start, npages);
127 pmm_reserved_frames += npages;
128 return pfn_start;
129}
130
132{
133 pr_dinfo2(pmm, "looking for block containing " PTR_FMT, needle);
134
135 const pfn_t needle_pfn = needle / MOS_PAGE_SIZE;
136
137 for (size_t i = 0; i < platform_info->num_pmm_regions; i++)
138 {
140 if (r->reserved && r->pfn_start <= needle_pfn && needle_pfn < r->pfn_start + r->nframes)
141 {
142 pr_dinfo2(pmm, "found block: " PFN_RANGE ", %zu pages", r->pfn_start, r->pfn_start + r->nframes - 1, r->nframes);
143 return r;
144 }
145 }
146
147 pr_dinfo2(pmm, "no block found");
148 return NULL;
149}
150
152{
153 const pfn_t start = phyframe_pfn(frame);
154 MOS_ASSERT_X(start + n_pages <= pmm_total_frames, "out of bounds");
155 pr_dinfo2(pmm, "ref range: " PFN_RANGE ", %zu pages", start, start + n_pages, n_pages);
156
157 for (size_t i = start; i < start + n_pages; i++)
158 pfn_phyframe(i)->allocated_refcount++;
159 return frame;
160}
161
162void _pmm_unref_phyframes(phyframe_t *frame, size_t n_pages)
163{
164 const pfn_t start = phyframe_pfn(frame);
165 MOS_ASSERT_X(start + n_pages <= pmm_total_frames, "out of bounds");
166 pr_dinfo2(pmm, "unref range: " PFN_RANGE ", %zu pages", start, start + n_pages, n_pages);
167
168 for (pfn_t pfn = start; pfn < start + n_pages; pfn++)
169 {
170 phyframe_t *frame = pfn_phyframe(pfn);
171 MOS_ASSERT(frame->allocated_refcount > 0);
172
173 if (--frame->allocated_refcount == 0)
174 pmm_free_frames(frame, 1);
175 }
176}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.h:15
#define MOS_ASSERT_ONCE(...)
Definition assert.h:13
#define MOS_ASSERT(cond)
Definition assert.h:14
#define mos_warn_once(...)
Definition assert.h:25
#define MOS_PAGE_SIZE
Definition autoconf.h:6
phyframe_t * buddy_alloc_n_exact(size_t nframes)
Allocate nframes of contiguous physical memory.
Definition buddy_core.c:331
void buddy_dump_all()
Dump the state of the buddy allocator.
Definition buddy_core.c:301
void buddy_free_n(pfn_t pfn, size_t nframes)
Free nframes of contiguous physical memory.
Definition buddy_core.c:370
void buddy_init(size_t max_nframes)
Initialize the buddy allocator with the given maximum number of frames.
Definition buddy_core.c:309
void buddy_reserve_n(pfn_t pfn, size_t nframes)
Reserve several frames at the given physical frame number.
Definition buddy_core.c:323
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.c:15
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.h:68
#define pfn_va(pfn)
Definition mm.h:76
#define pfn_phyframe(pfn)
Definition pmm.h:81
void _pmm_unref_phyframes(phyframe_t *frame, size_t n_pages)
Definition pmm.c:162
size_t pmm_total_frames
Definition pmm.c:16
phyframe_t * _pmm_ref_phyframes(phyframe_t *frame, size_t n_pages)
Definition pmm.c:151
#define phyframe_pfn(frame)
Definition pmm.h:80
pfn_t pmm_reserve_frames(pfn_t pfn_start, size_t npages)
Mark a range of physical memory as reserved.
Definition pmm.c:122
void pmm_free_frames(phyframe_t *start_frame, size_t n_pages)
Definition pmm.c:108
pmm_allocation_flags_t
Definition pmm.h:73
phyframe_t * phyframes
Definition pmm.c:15
phyframe_t * pmm_allocate_frames(size_t n_frames, pmm_allocation_flags_t flags)
Allocate n_frames of contiguous physical memory.
Definition pmm.c:92
void pmm_init(void)
Definition pmm.c:20
pmm_region_t * pmm_find_reserved_region(ptr_t needle)
Find a region in the physical memory manager.
Definition pmm.c:131
void pmm_dump_lists(void)
Dump the physical memory manager's state, (i.e. the free list and the allocated list).
Definition pmm.c:84
@ PMM_ALLOC_NORMAL
allocate normal pages
Definition pmm.h:75
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:75
#define MOS_PANIC_HOOK_FEAT(_feat, _f, _n)
Definition panic.h:32
#define NULL
Definition pb_syshdr.h:46
size_t pmm_reserved_frames
Definition pmm.c:18
size_t pmm_allocated_frames
Definition pmm.c:17
#define pr_info(fmt,...)
Definition printk.h:35
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
mos_platform_info_t *const platform_info
#define memzero(ptr, size)
Definition rpc_client.c:40
pmm_region_t pmm_regions[MOS_MAX_MEMREGIONS]
Definition platform.h:124
atomic_t allocated_refcount
Definition pmm.h:58
bool reserved
Definition pmm.h:68
pfn_t pfn_start
Definition pmm.h:66
size_t nframes
Definition pmm.h:67
#define PFNADDR(pfn, end)
Definition types.h:46
unsigned int u32
Definition types.h:21
unsigned long long pfn_t
Definition types.h:41
#define PFN_FMT
Definition types.h:42
#define PTR_FMT
Definition types.h:33
#define PFN_RANGE
Definition types.h:43
unsigned long ptr_t
Definition types.h:25
#define PFNADDR_RANGE
Definition types.h:44