IBNOS
physmem.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Michael Müller
3  * Copyright (c) 2014, Sebastian Lackner
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <memory/physmem.h>
29 #include <memory/paging.h>
30 #include <console/console.h>
31 #include <util/util.h>
32 
39 uint32_t ramSize = 0;
40 uint32_t ramUsableSize = 0;
41 
42 #define PHYSMEMEXTRA_SIZE 0x1000 /* must match PAGE_SIZE for now */
43 #define PHYSMEMEXTRA_MASK 0x3FF
44 #define PHYSMEMEXTRA_BITS 10
45 #define PHYSMEMEXTRA_COUNT 0x400
46 
48 {
49  union
50  {
51  struct
52  {
53  uint32_t present : 1;
54  uint32_t ref : 7;
55  uint32_t unpageable : 1;
56  uint32_t avail : 23;
57  };
58  uint32_t value;
59  };
60 } __attribute__((packed));
61 
62 static bool physMemInitialized = false;
63 static uint32_t physMemMap[(PAGE_COUNT + 31) / 32] __attribute__((aligned(4096)));
64 static struct physMemExtraInfo *physMemExtra[PHYSMEMEXTRA_COUNT] __attribute__((aligned(4096)));
65 
66 /* used to determine the memory layout of the kernel itself */
67 extern uint32_t __kernelBegin;
68 extern uint32_t __kernelEnd;
69 #define LINKER_KERNEL_BEGIN ((uint32_t)&__kernelBegin)
70 #define LINKER_KERNEL_SIZE ((uint32_t)&__kernelEnd - (uint32_t)&__kernelBegin)
71 
72 static const char *error_outOfMemory[] =
73 {
74  " OUT OF MEMORY ",
75  " The system ran out of physical memory!",
76  NULL
77 };
78 
79 uint32_t __getCR0();
80 asm(".text\n.align 4\n"
81 "__getCR0:\n"
82 " movl %cr0, %eax\n"
83 " ret\n"
84 );
85 
86 /* Returns a pointer to the physMemExtraTable */
87 static struct physMemExtraInfo *__getPhysMemExtraInfo(uint32_t index, bool alloc)
88 {
89  uint32_t i = index >> PHYSMEMEXTRA_BITS;
90  assert(__getCR0() & 0x80000000);
91 
93  if (!physMemExtra[i])
94  {
95  if (!alloc) return NULL;
96  physMemExtra[i] = pagingAllocatePhysMem(NULL, 1, true, false);
97  memset(physMemExtra[i], 0, PAGE_SIZE);
98 
99  /* TODO: mark it as unpageable? */
100  physMemMarkUnpageable(pagingGetPhysMem(NULL, physMemExtra[i]));
101  }
102 
103  return &((physMemExtra[i])[index & PHYSMEMEXTRA_MASK]);
104 }
105 
117 {
118  size_t offset = 0;
119 
120  assert(!physMemInitialized);
121  assert(bootInfo);
122 
123  /* clear the extra info map */
124  assert(((uint32_t)physMemExtra & PAGE_MASK) == 0);
125  memset(physMemExtra, 0, sizeof(physMemExtra));
126 
127  /* determine ram size */
128  assert(bootInfo->flags & MULTIBOOT_INFO_MEM_MAP); /* Is mmap_* valid? */
129  ramSize = bootInfo->mem_upper;
130 
131  /* set the complete memory to reserved */
133 
134  /*
135  * Setup free memory regions
136  */
137  assert(bootInfo->flags & MULTIBOOT_MEMORY_INFO); /* Is mem_{upper,lower} valid? */
138  while (offset < bootInfo->mmap_length)
139  {
140  uint64_t startIndex, stopIndex;
141 
142  multiboot_memory_map_t *memMap = (multiboot_memory_map_t *)((char*)bootInfo->mmap_addr + offset);
143  offset += sizeof(memMap->size) + memMap->size;
144 
145  if (!(memMap->type & MULTIBOOT_MEMORY_AVAILABLE))
146  continue;
147 
148  /* process this entry */
149  startIndex = (memMap->addr + PAGE_MASK) >> PAGE_BITS;
150  stopIndex = (memMap->addr + memMap->len) >> PAGE_BITS;
151 
152  if (stopIndex <= startIndex)
153  continue;
154 
155  if (startIndex >= PAGE_COUNT)
156  continue;
157 
158  if (stopIndex >= PAGE_COUNT)
159  stopIndex = PAGE_COUNT - 1;
160 
161  /* set the available memory as free */
162  physMemSetMemoryBits(startIndex, stopIndex - startIndex, PHYSMEM_FREE);
163  }
164 
165  /*
166  * Protect the kernel itself
167  */
169 
170  /*
171  * Protect useful boot information
172  */
173  physMemProtectBootEntry((uint32_t)bootInfo, sizeof(*bootInfo));
174  physMemProtectBootEntry(bootInfo->mmap_addr, bootInfo->mmap_length);
175 
176  /* Is the command line passed? */
177  if (bootInfo->flags & MULTIBOOT_INFO_CMDLINE)
178  physMemProtectBootEntry(bootInfo->cmdline, stringLength((char*)bootInfo->cmdline));
179 
180  /* Are mods_* valid? */
181  if (bootInfo->flags & MULTIBOOT_INFO_MODS)
182  {
183  physMemProtectBootEntry(bootInfo->mods_addr, bootInfo->mods_count * sizeof(multiboot_module_t));
184 
185  multiboot_module_t *module = (multiboot_module_t *)bootInfo->mods_addr;
186  for (uint32_t i = 0; i < bootInfo->mods_count; i++)
187  {
188  if (module[i].mod_start >= module[i].mod_end)
189  continue;
190 
191  physMemProtectBootEntry(module[i].mod_start, module[i].mod_end - module[i].mod_start);
192  }
193  }
194 
195  /*
196  if (bootInfo->drives_addr && bootInfo->drives_length)
197  physMemProtectBootEntry(bootInfo->drives_addr, bootInfo->drives_length);
198 
199  if (bootInfo->config_table)
200  physMemProtectBootEntry(bootInfo->config_table, PAGE_SIZE);
201 
202  if (bootInfo->boot_loader_name)
203  physMemProtectBootEntry(bootInfo->boot_loader_name, stringLength((char*)bootInfo->boot_loader_name));
204 
205  if (bootInfo->apm_table)
206  physMemProtectBootEntry(bootInfo->apm_table, sizeof(struct multiboot_apm_info));
207  */
208 
209  physMemInitialized = true;
210 }
211 
219 uint32_t physMemRAMSize()
220 {
221  return ramSize;
222 }
223 
233 {
234  /* FIXME: Calculate appropriate usable ram size */
235  NOTIMPLEMENTED();
236  return ramUsableSize;
237 }
238 
245 {
246  uint32_t mask = reserved ? 0xFFFFFFFF : 0;
247  uint32_t longIndex;
248 
249  for (longIndex = 0; longIndex < sizeof(physMemMap) / sizeof(physMemMap[0]); longIndex++)
250  physMemMap[longIndex] = mask;
251 }
252 
259 void physMemProtectBootEntry(uint32_t addr, uint32_t length)
260 {
261  uint32_t startIndex = addr >> PAGE_BITS;
262  uint32_t stopIndex = ((uint64_t)addr + length + PAGE_MASK) >> PAGE_BITS;
263 
264  if (stopIndex >= PAGE_COUNT)
265  stopIndex = PAGE_COUNT - 1;
266 
267  assert(startIndex <= stopIndex);
268 
269  pagingInsertBootMap(startIndex, stopIndex);
270  physMemSetMemoryBits(startIndex, stopIndex - startIndex, PHYSMEM_RESERVED);
271  return;
272 }
273 
280 void physMemReserveMemory(uint32_t addr, uint32_t length)
281 {
282  uint32_t startIndex = addr >> PAGE_BITS;
283  uint32_t stopIndex = ((uint64_t)addr + length + PAGE_MASK) >> PAGE_BITS;
284 
285  if (stopIndex >= PAGE_COUNT)
286  stopIndex = PAGE_COUNT - 1;
287 
288  assert(startIndex <= stopIndex);
289 
290  physMemSetMemoryBits(startIndex, stopIndex - startIndex, PHYSMEM_RESERVED);
291 }
292 
299 void physMemFreeMemory(uint32_t addr, uint32_t length)
300 {
301  uint32_t startIndex = (addr + PAGE_MASK) >> PAGE_BITS;
302  uint32_t stopIndex = ((uint64_t)addr + length) >> PAGE_BITS;
303 
304  if (stopIndex >= PAGE_COUNT)
305  stopIndex = PAGE_COUNT - 1;
306 
307  assert(startIndex <= stopIndex);
308 
309  physMemSetMemoryBits(startIndex, stopIndex - startIndex, PHYSMEM_FREE);
310 }
311 
319 void physMemSetMemoryBits(uint32_t startIndex, uint32_t length, bool reserved)
320 {
321  uint32_t longIndex, longOffset;
322  uint32_t mask;
323 
324  assert(length < PAGE_COUNT);
325  assert(startIndex < PAGE_COUNT - length);
326 
327  /* get index and offset */
328  longIndex = startIndex >> 5;
329  longOffset = startIndex & 31;
330 
331  /* handle all bits till the next dword boundary */
332  if (longOffset)
333  {
334 
335  /* number of bits reaches into the next dword */
336  if (length > 32 - longOffset)
337  {
338  mask = 0xFFFFFFFF << longOffset;
339  length -= (32 - longOffset);
340  }
341  else
342  {
343  mask = ((1 << length) - 1) << longOffset;
344  length = 0;
345  }
346 
347  if (reserved)
348  physMemMap[longIndex++] |= mask;
349  else
350  physMemMap[longIndex++] &= ~mask;
351  }
352 
353  /* handle full dwords */
354  mask = reserved ? 0xFFFFFFFF : 0;
355  while (length >= 32)
356  {
357  physMemMap[longIndex++] = mask;
358  length -= 32;
359  }
360 
361  /* handle rest */
362  if (length > 0)
363  {
364  mask = (1 << length) - 1;
365 
366  if (reserved)
367  physMemMap[longIndex] |= mask;
368  else
369  physMemMap[longIndex] &= ~mask;
370  }
371 }
372 
383 uint32_t physMemAllocPage(bool lowmem)
384 {
385  uint32_t try, longIndex, longOffset;
386 
387  for (try = 0; try < 0x10; try++)
388  {
389 
390  for (longIndex = lowmem ? 0 : 8 /* 1MB */; longIndex < sizeof(physMemMap) / sizeof(physMemMap[0]); longIndex++)
391  {
392  if (physMemMap[longIndex] != 0xFFFFFFFF)
393  break;
394  }
395 
396  if (longIndex < sizeof(physMemMap) / sizeof(physMemMap[0]))
397  {
398  longOffset = 0;
399 
400  while ((physMemMap[longIndex] >> longOffset) & 1)
401  longOffset++;
402 
403  physMemMap[longIndex] |= (1 << longOffset);
404 
405  return longIndex << 5 | longOffset;
406  }
407 
408  /* try to page out some other stuff */
409  physMemPageOut(1);
410  }
411 
412  SYSTEM_FAILURE(error_outOfMemory, lowmem);
413  return 0; /* never reached */
414 }
415 
426 uint32_t physMemReleasePage(uint32_t index)
427 {
428  struct physMemExtraInfo *info;
429  uint32_t longIndex, longOffset;
430 
431  longIndex = index >> 5;
432  longOffset = index & 31;
433 
434  /* You can not released a free page */
435  assert(((physMemMap[longIndex] >> longOffset) & 1));
436 
437  info = __getPhysMemExtraInfo(index, false);
438  if (info && info->value)
439  {
440  assert(info->present);
441 
442  /* decrease refcount, only free page if we reach zero */
443  if (--info->ref) return info->ref;
444 
445  /* reset */
446  info->value = 0;
447  }
448 
449  /* mark the page as free */
450  physMemMap[longIndex] &= ~(1 << longOffset);
451  return 0;
452 }
453 
460 uint32_t physMemAddRefPage(uint32_t index)
461 {
462  struct physMemExtraInfo *info = __getPhysMemExtraInfo(index, true);
463  assert(info);
464 
465  if (!info->value)
466  {
467  info->present = 1;
468  info->ref = 1;
469  }
470 
471  info->ref++;
472  assert(info->ref);
473 
474  return index;
475 }
476 
486 uint32_t physMemMarkUnpageable(uint32_t index)
487 {
488  struct physMemExtraInfo *info = __getPhysMemExtraInfo(index, true);
489  assert(info);
490 
491  if (!info->value)
492  {
493  info->present = 1;
494  info->ref = 1;
495  }
496 
497  info->unpageable = 1;
498 
499  return index;
500 }
501 
510 bool physMemIsLastRef(uint32_t index)
511 {
512  struct physMemExtraInfo *info = __getPhysMemExtraInfo(index, false);
513  return (!info || !info->value || info->ref == 1);
514 }
515 
521 void physMemPageOut(UNUSED uint32_t length)
522 {
523  NOTIMPLEMENTED();
524 }
525 
535 uint32_t physMemPageIn(UNUSED uint32_t hdd_index)
536 {
537  NOTIMPLEMENTED();
538  return 0;
539 }
540 
545 {
546  uint32_t startIndex = 0;
547  bool reserved = physMemMap[0] & 1;
548  uint32_t mask = reserved ? 0xFFFFFFFF : 0;
549 
550  uint32_t index = 0;
551  uint32_t longIndex = 0, longOffset = 0; /* initialization not necessary, but gets rid of warnings */
552 
553  uint32_t usableMemory = 0;
554 
555  consoleWriteString("PHYSICAL MEMORY MAP:\n\n");
556 
557  for (;;)
558  {
559  if (index < PAGE_COUNT)
560  {
561  longIndex = index >> 5;
562  longOffset = index & 31;
563 
564  if (physMemMap[longIndex] == mask)
565  {
566  index += (32 - longOffset);
567  continue;
568  }
569  else if (((physMemMap[longIndex] >> longOffset) & 1) == reserved)
570  {
571  index++;
572  continue;
573  }
574  }
575 
576  consoleWriteHex32(startIndex << PAGE_BITS);
577  consoleWriteString(" - ");
578  consoleWriteHex32((index << PAGE_BITS) - 1);
579 
580  if (reserved)
581  consoleWriteString(" RESERVED\n");
582  else
583  {
584  consoleWriteString(" FREE\n");
585  usableMemory += (index - startIndex) << PAGE_BITS;
586  }
587 
588  if (index >= PAGE_COUNT)
589  break;
590 
591  startIndex = index++;
592  reserved = (physMemMap[longIndex] >> longOffset) & 1;
593  mask = reserved ? 0xFFFFFFFF : 0;
594  }
595 
596  consoleWriteString("\nUsable Memory: ");
597  consoleWriteHex32(usableMemory);
598  consoleWriteString("\n\n");
599 }
600 
void physMemFreeMemory(uint32_t addr, uint32_t length)
Marks all pages fully contained within the memory range as free.
Definition: physmem.c:299
uint32_t physMemReleasePage(uint32_t index)
Releases a page of physical memory.
Definition: physmem.c:426
#define PHYSMEMEXTRA_MASK
Definition: physmem.c:43
union physMemExtraInfo::@32 __attribute__
#define PAGE_MASK
Definition: physmem.h:36
multiboot_uint32_t size
Definition: multiboot.h:232
uint32_t physMemMarkUnpageable(uint32_t index)
Marks a physical page as unpageable.
Definition: physmem.c:486
uint32_t __kernelBegin
#define assert(ex)
Definition: util.h:61
#define UNUSED
Definition: util.h:39
#define MULTIBOOT_INFO_CMDLINE
Definition: multiboot.h:62
void * memset(void *ptr, int value, size_t num)
Fills a memory region with some specific byte value.
Definition: util.c:123
multiboot_uint64_t addr
Definition: multiboot.h:233
uint32_t physMemPageIn(UNUSED uint32_t hdd_index)
Pages in some data from the hard drive.
Definition: physmem.c:535
multiboot_uint64_t len
Definition: multiboot.h:234
#define PAGE_SIZE
Definition: physmem.h:35
uint32_t stringLength(const char *str)
Returns the length of a nullterminated string.
Definition: util.c:37
void physMemProtectBootEntry(uint32_t addr, uint32_t length)
Marks all pages within a memory range as reserved and adds them to the boot map.
Definition: physmem.c:259
#define LINKER_KERNEL_BEGIN
Definition: physmem.c:69
bool physMemIsLastRef(uint32_t index)
Checks if a physical page is only referenced exactly one time.
Definition: physmem.c:510
void * pagingAllocatePhysMem(struct process *p, uint32_t length, bool rw, bool user)
Allocates several pages of physical memory in a process.
Definition: paging.c:659
#define PAGE_BITS
Definition: physmem.h:37
#define MULTIBOOT_MEMORY_INFO
Definition: multiboot.h:47
#define NOTIMPLEMENTED()
Definition: util.h:82
multiboot_uint32_t type
Definition: multiboot.h:240
void physMemReserveMemory(uint32_t addr, uint32_t length)
Marks all pages within a memory range as reserved.
Definition: physmem.c:280
uint32_t unpageable
Definition: physmem.c:55
#define MULTIBOOT_INFO_MODS
Definition: multiboot.h:64
uint32_t ramUsableSize
Definition: physmem.c:40
uint32_t physMemAddRefPage(uint32_t index)
Increment the refcounter of a physical page.
Definition: physmem.c:460
void physMemSetMemoryBits(uint32_t startIndex, uint32_t length, bool reserved)
Marks a specific range of pages as reserved or free.
Definition: physmem.c:319
uint32_t value
Definition: physmem.c:58
void physMemInit(multiboot_info_t *bootInfo)
Initializes the physical memory management.
Definition: physmem.c:116
multiboot_uint32_t flags
Definition: multiboot.h:147
uint32_t physMemRAMSize()
Query RAM size.
Definition: physmem.c:219
#define PHYSMEMEXTRA_BITS
Definition: physmem.c:44
#define MULTIBOOT_MEMORY_AVAILABLE
Definition: multiboot.h:235
Definition: multiboot.h:230
#define LINKER_KERNEL_SIZE
Definition: physmem.c:70
multiboot_uint32_t mmap_addr
Definition: multiboot.h:171
#define PHYSMEM_RESERVED
Definition: physmem.h:49
void consoleWriteHex32(uint32_t value)
Write a 32 bit integer as hex value on the console.
Definition: console.c:303
uint32_t __kernelEnd
#define SYSTEM_FAILURE(lines,...)
Definition: util.h:71
#define PHYSMEMEXTRA_COUNT
Definition: physmem.c:45
#define PAGE_COUNT
Definition: physmem.h:38
void physMemDumpMemInfo()
Dumps information about the physical memory usage.
Definition: physmem.c:544
uint32_t physMemAllocPage(bool lowmem)
Allocates a page of physical memory.
Definition: physmem.c:383
uint32_t pagingGetPhysMem(struct process *p, void *addr)
Returns the physical page index for a virtual address.
Definition: paging.c:1111
void physMemClearMemoryBits(bool reserved)
Marks the full memory range as reserved or free.
Definition: physmem.c:244
uint32_t avail
Definition: physmem.c:56
void consoleWriteString(const char *str)
Write a C string to the console.
Definition: console.c:240
void physMemPageOut(UNUSED uint32_t length)
Pages out some memory to the hard drive.
Definition: physmem.c:521
multiboot_uint32_t mem_upper
Definition: multiboot.h:151
#define MULTIBOOT_INFO_MEM_MAP
Definition: multiboot.h:74
uint32_t ramSize
Definition: physmem.c:39
uint32_t present
Definition: physmem.c:53
#define PHYSMEM_FREE
Definition: physmem.h:48
uint32_t physMemUsableMemory()
Query usable RAM size.
Definition: physmem.c:232
uint32_t ref
Definition: physmem.c:54
void pagingInsertBootMap(uint32_t startIndex, uint32_t stopIndex)
Appends a specific range of physical pages to the bootmap.
Definition: paging.c:391
uint32_t __getCR0()
uint8_t reserved
Definition: gdt.h:134