MonkOS  v0.1
A simple 64-bit operating system (x86_64)
pmap.c
Go to the documentation of this file.
1 //============================================================================
2 /// @file pmap.c
3 /// @brief Physical memory map describing usable and reserved regions
4 /// of physical memory.
5 /// @details Before this code is ever executed, the boot code has filled
6 /// much of the memory map with memory regions supplied by the
7 /// BIOS.
8 //
9 // Copyright 2016 Brett Vickers.
10 // Use of this source code is governed by a BSD-style license that can
11 // be found in the MonkOS LICENSE file.
12 //============================================================================
13 
14 #include <core.h>
15 #include <libc/stdlib.h>
16 #include <libc/string.h>
17 #include <kernel/mem/kmem.h>
18 #include <kernel/mem/pmap.h>
19 
20 // Pointer to the BIOS-generated memory map.
21 static pmap_t *map = (pmap_t *)KMEM_TABLE_BIOS;
22 static bool initialized = false;
23 
24 /// Add a memory region to the end of the memory map.
25 static void
26 add_region(uint64_t addr, uint64_t size, enum pmemtype type)
27 {
28  pmapregion_t *r = map->region + map->count;
29  r->addr = addr;
30  r->size = size;
31  r->type = (int32_t)type;
32  r->flags = 0;
33 
34  ++map->count;
35 }
36 
37 /// Compare two memory region records and return a sorting integer.
38 static int
39 cmp_region(const void *a, const void *b)
40 {
41  const pmapregion_t *r1 = (const pmapregion_t *)a;
42  const pmapregion_t *r2 = (const pmapregion_t *)b;
43  if (r1->addr > r2->addr)
44  return +1;
45  if (r1->addr < r2->addr)
46  return -1;
47  if (r1->size > r2->size)
48  return +1;
49  if (r1->size < r2->size)
50  return -1;
51  return 0;
52 }
53 
54 /// Remove a region from the memory map and shift all subsequent regions
55 /// down by one.
56 static inline void
57 collapse(pmapregion_t *r, pmapregion_t *term)
58 {
59  if (r + 1 < term)
60  memmove(r, r + 1, (term - r) * sizeof(pmapregion_t));
61  --map->count;
62 }
63 
64 /// Insert a new, uninitialized memory region record after an existing record
65 /// in the memory map.
66 static inline void
67 insertafter(pmapregion_t *r, pmapregion_t *term)
68 {
69  if (r + 1 < term)
70  memmove(r + 2, r + 1, (term - (r + 1)) * sizeof(pmapregion_t));
71  ++map->count;
72 }
73 
74 /// Re-sort all unsorted region records starting from the requested record.
75 static void
76 resort(pmapregion_t *r, pmapregion_t *term)
77 {
78  while (r + 1 < term) {
79  if (cmp_region(r, r + 1) < 0)
80  break;
81  pmapregion_t tmp = r[0];
82  r[0] = r[1];
83  r[1] = tmp;
84  r++;
85  }
86 }
87 
88 /// Find all overlapping memory regions in the memory map and collapse or
89 /// reorganize them.
90 static void
92 {
93  pmapregion_t *curr = map->region;
94  pmapregion_t *term = map->region + map->count;
95 
96  while (curr + 1 < term) {
97 
98  // Collapse empty entries.
99  if (curr->size == 0) {
100  collapse(curr, term--);
101  continue;
102  }
103 
104  pmapregion_t *next = curr + 1;
105 
106  uint64_t cl = curr->addr;
107  uint64_t cr = curr->addr + curr->size;
108  uint64_t nl = next->addr;
109  uint64_t nr = next->addr + next->size;
110 
111  // No overlap? Then go to the next region.
112  if (min(cr, nr) <= max(cl, nl)) {
113  curr++;
114  continue;
115  }
116 
117  // Handle the 5 alignment cases:
118  // xxx xxx xxxx xxx xxxxx
119  // yyy yyyy yyy yyy yyy
120  // The remaining cases are impossible due to sorting.
121 
122  if (cl == nl) {
123  if (cr == nr) {
124  if (next->type > curr->type) {
125  // 111 -> 222
126  // 222
127  collapse(curr, term--);
128  }
129  else {
130  // 222 -> 222
131  // 111
132  collapse(next, term--);
133  }
134  }
135  else { /* if cr != nr */
136  if (next->type > curr->type) {
137  // 111 -> 2222
138  // 2222
139  collapse(curr, term--);
140  }
141  else {
142  // 222 -> 222
143  // 1111 -> 1
144  next->size = nr - cr;
145  next->addr = cr;
146  resort(next, term);
147  }
148  }
149  }
150 
151  else { /* if cl != nl */
152  if (cr == nr) {
153  if (next->type > curr->type) {
154  // 1111 -> 1
155  // 222 -> 222
156  curr->size = nl - cl;
157  }
158  else {
159  // 2222 -> 2222
160  // 111
161  collapse(next, term--);
162  }
163  }
164  else if (cr < nr) {
165  if (next->type > curr->type) {
166  // 1111 -> 1
167  // 2222 -> 2222
168  curr->size = nl - cl;
169  }
170  else {
171  // 2222 -> 2222
172  // 1111 -> 1
173  next->size = nr - cr;
174  next->addr = cr;
175  resort(next, term);
176  }
177  }
178  else { /* if cr > nr */
179  if (next->type > curr->type) {
180  // 11111 -> 1
181  // 222 -> 222
182  // -> 1
183  curr->size = nl - cl;
184  insertafter(next, term++);
185  next[1].addr = nr;
186  next[1].size = cr - nr;
187  next[1].type = curr->type;
188  next[1].flags = curr->flags;
189  resort(next + 1, term);
190  }
191  else {
192  // 22222 -> 22222
193  // 111
194  collapse(next, term--);
195  }
196  }
197  }
198 
199  }
200 }
201 
202 /// Find missing memory regions in the map, and fill them with entries of
203 /// the requested type.
204 static void
205 fill_gaps(int32_t type)
206 {
207  pmapregion_t *curr = map->region;
208  pmapregion_t *term = map->region + map->count;
209 
210  while (curr + 1 < term) {
211  pmapregion_t *next = curr + 1;
212 
213  uint64_t cr = curr->addr + curr->size;
214  uint64_t nl = next->addr;
215 
216  if (cr == nl) {
217  curr++;
218  continue;
219  }
220 
221  // Try to expand one of the neighboring entries if one of them has the
222  // same type as the fill type.
223  if (curr->type == type) {
224  curr->size += nl - cr;
225  }
226  else if (next->type == type) {
227  next->size += nl - cr;
228  next->addr = cr;
229  }
230 
231  // Neither neighboring region has the fill type, so insert a new
232  // region record.
233  else {
234  insertafter(curr, term++);
235  next->addr = cr;
236  next->size = nl - cr;
237  next->type = type;
238  next->flags = 0;
239  }
240  }
241 }
242 
243 /// Find adjacent memory entries of the same type and merge them.
244 static void
246 {
247  pmapregion_t *curr = map->region;
248  pmapregion_t *term = map->region + map->count;
249 
250  while (curr + 1 < term) {
251  pmapregion_t *next = curr + 1;
252  if (curr->type == next->type) {
253  curr->size += next->size;
254  collapse(next, term--);
255  }
256  else {
257  curr++;
258  }
259  }
260 }
261 
262 static void
264 {
265  map->last_usable = 0;
266  for (int i = map->count - 1; i >= 0; i--) {
267  const pmapregion_t *r = &map->region[i];
268  if (r->type == PMEMTYPE_USABLE) {
269  map->last_usable = r->addr + r->size;
270  break;
271  }
272  }
273 }
274 
275 static void
277 {
278  // Sort the memory map by address.
279  qsort(map->region, map->count, sizeof(pmapregion_t),
280  cmp_region);
281 
282  // Remove overlapping regions, fill gaps between regions with "reserved"
283  // memory, squash adjacent regions of the same type, and calculate the end
284  // of the last usable memory region.
289 }
290 
291 void
293 {
294  // During the boot process, the physical memory map at KMEM_TABLE_BIOS has
295  // been updated to include memory regions reported by the BIOS. This
296  // function cleans up the BIOS memory map (sorts it, removes overlaps,
297  // etc.) and adds a few additional memory regions.
298 
299  // Mark VGA video memory as uncached.
301 
302  // Reserve memory for the kernel and its global data structures.
304 
305  // Mark the first page of memory as unmapped so deferencing a null pointer
306  // always faults.
307  add_region(0, 0x1000, PMEMTYPE_UNMAPPED);
308 
309  // Fix up the memory map.
310  normalize();
311 
312  initialized = true;
313 }
314 
315 const pmap_t *
317 {
318  return map;
319 }
320 
321 void
322 pmap_add(uint64_t addr, uint64_t size, enum pmemtype type)
323 {
324  add_region(addr, size, type);
325 
326  if (initialized)
327  normalize();
328 }
static void resort(pmapregion_t *r, pmapregion_t *term)
Re-sort all unsorted region records starting from the requested record.
Definition: pmap.c:76
const pmap_t * pmap()
Return a pointer to the current physical memory map.
Definition: pmap.c:316
Physical memory map describing usable and reserved regions of physical memory.
String and memory operations.
#define min(a, b)
Generic min/max routines.
Definition: core.h:29
#define KMEM_KERNEL_IMAGE_END
Definition: kmem.h:40
Core include file.
Kernel physical (and virtual) memory map.
static void fill_gaps(int32_t type)
Find missing memory regions in the map, and fill them with entries of the requested type...
Definition: pmap.c:205
pmemtype
The types of physical memory.
Definition: pmap.h:21
Marked as uncacheable, usually for I/O.
Definition: pmap.h:28
Reported usable by the BIOS.
Definition: pmap.h:23
static bool initialized
Definition: pmap.c:22
void * memmove(void *dst, const void *src, size_t num)
Move bytes from one memory region to another, even if the regions overlap.
static pmap_t * map
Definition: pmap.c:21
static void update_last_usable()
Definition: pmap.c:263
static int cmp_region(const void *a, const void *b)
Compare two memory region records and return a sorting integer.
Definition: pmap.c:39
void pmap_add(uint64_t addr, uint64_t size, enum pmemtype type)
Add a region of memory to the physical memory map.
Definition: pmap.c:322
static void normalize()
Definition: pmap.c:276
static void add_region(uint64_t addr, uint64_t size, enum pmemtype type)
Add a memory region to the end of the memory map.
Definition: pmap.c:26
#define max(a, b)
Definition: core.h:30
#define KMEM_VIDEO_SIZE
Definition: kmem.h:43
void qsort(void *base, size_t num, size_t size, sortcmp cmp)
Sort the elements of an array using a sort comparison function.
#define KMEM_TABLE_BIOS
Definition: kmem.h:24
void pmap_init()
Initialize the physical memory map using data installed by the BIOS during boot loading.
Definition: pmap.c:292
Reported (or inferred) to be reserved.
Definition: pmap.h:24
static void collapse_overlaps()
Find all overlapping memory regions in the memory map and collapse or reorganize them.
Definition: pmap.c:91
General utilities library.
static void consolidate_neighbors()
Find adjacent memory entries of the same type and merge them.
Definition: pmap.c:245
static void collapse(pmapregion_t *r, pmapregion_t *term)
Remove a region from the memory map and shift all subsequent regions down by one. ...
Definition: pmap.c:57
Marked as "do not map".
Definition: pmap.h:29
#define KMEM_VIDEO
Definition: kmem.h:32
static void insertafter(pmapregion_t *r, pmapregion_t *term)
Insert a new, uninitialized memory region record after an existing record in the memory map...
Definition: pmap.c:67