UnnamedOS
pmm.c
Go to the documentation of this file.
1 
15 #include <common.h>
16 #include <string.h>
17 #include <mem/pmm.h>
18 #include <boot/multiboot.h>
19 
20 #define PAGE_SIZE 4096
21 #define PAGE_SHIFT 12
22 #define MEMORY_SIZE 0x100000000
23 #define TYPE_BITS 2
24 #define PAGE_NUMBER (MEMORY_SIZE / PAGE_SIZE)
25 #define ENTRIES 1024
26 #define PAGES_PER_DWORD (32 / TYPE_BITS)
27 #define PAGES_BER_BYTE (8 / TYPE_BITS)
28 #define TYPE_MASK (0xFFFFFFFF >> (32 - TYPE_BITS))
29 #define BITMAP_INIT 0x55555555
30 
31 #define BIT_CHECK(val, bit) (((val) >> (bit)) & 1)
33 #define BITMAP_SET(idx) (bitmap[(idx) / 32] |= 1 << ((idx) % 32))
35 #define BITMAP_CLEAR(idx) (bitmap[(idx) / 32] &= ~(1 << ((idx) % 32)))
37 
40 static uint32_t bitmap[PAGE_NUMBER / PAGES_PER_DWORD];
41 static uint32_t highest_kernel_page = 0;
42 
43 // symbols defined in script.ld and main_asm.S, only the addresses matter
44 extern const void
45  kernel_start,
46  kernel_end,
48 
50 static void* main_kernel_stack_start = (void*) ((uintptr_t)
51  &main_kernel_stack_end - STACK_SIZE + 1);
52 
58 static void pmm_bitmap_set(uint32_t idx, uint32_t value) {
59  idx *= TYPE_BITS; // make this flexible so that more types can be added
60  for (int i = 0; i < TYPE_BITS; i++)
61  if (BIT_CHECK(value, i))
62  BITMAP_SET(idx + i);
63  else
64  BITMAP_CLEAR(idx + i);
65 }
66 
72 static uint32_t pmm_bitmap_get(uint32_t idx) {
73  idx *= TYPE_BITS;
74  return (bitmap[idx / 32] >> (idx % 32)) & TYPE_MASK;
75 }
76 
78 static void pmm_use_kernel_memory() {
79  logln("PMM", "Kernel memory:");
80  logln("PMM", " kernel=%08x-%08x", &kernel_start, &kernel_end);
81  logln("PMM", " stack=%08x-%08x", main_kernel_stack_start, &main_kernel_stack_end);
82  pmm_use((void*) &kernel_start, (uintptr_t) &kernel_end -
83  (uintptr_t) &kernel_start + 1, PMM_KERNEL, "kernel");
84 }
85 
87 void pmm_init() {
88  print("PMM init ... ");
90  memset(bitmap, BITMAP_INIT, sizeof(bitmap));
91  if (!multiboot_free_memory()) {
92  println("%4afail%a. Memory map not found.");
93  return;
94  }
97  // pmm_use(0, MULTIBOOT_FIRST_PAGE_TABLE, PMM_RESERVED, "VM86 memory");
98  // We could do it like that, but a direct memset proves to be faster:
99  logln("PMM", "Use the first megabyte for VM86");
101  pmm_use_kernel_memory(); // Maps the actual kernel code and data.
106  multiboot_copy_memory();
109  pmm_use((void*) MULTIBOOT_LOWER_MEMORY,
110  MULTIBOOT_FIRST_PAGE_TABLE - MULTIBOOT_LOWER_MEMORY, PMM_UNUSED, 0);
111  println("%2aok%a.");
112 }
113 
120 uint32_t pmm_get_page(void* ptr, uint32_t offset) {
121  return ((uintptr_t) ptr + offset) >> PAGE_SHIFT;
122 }
123 
130 void* pmm_get_address(uint32_t page, uint32_t offset) {
131  return (void*) ((page << PAGE_SHIFT) + offset);
132 }
133 
141 void pmm_use(void* ptr, size_t len, pmm_flags_t flags, char* tag) {
142  if (len == 0) return;
143  uint32_t start_page = pmm_get_page(ptr, 0), end_page = pmm_get_page(ptr, len - 1);
144  log("PMM", "%s %08x-%08x (page %05x-%05x)", flags == PMM_UNUSED ? "Free" : "Use ",
145  ptr, ptr + len - 1, start_page, end_page);
146  if (tag)
147  log(0, " for %s", tag);
148  logln(0, "");
149  for (int i = start_page; i <= end_page; i++)
150  pmm_bitmap_set(i, flags); // mark pages as used in the bitmap
151  if (flags == PMM_KERNEL && end_page > highest_kernel_page)
152  highest_kernel_page = end_page;
153 }
154 
160 static void* pmm_find_free(size_t len) {
161  if (len == 0) return 0;
162  uint32_t pages = len / PAGE_SIZE + (len % PAGE_SIZE ? 1 : 0), // "round up"
163  free_pages = 0;
164  for (int i = 0; i < PAGE_NUMBER; i++) { // makeshift first-fit linear search
165  if (pmm_bitmap_get(i) == PMM_UNUSED)
166  free_pages++;
167  else
168  free_pages = 0;
169  if (free_pages >= pages) // if we found enough in-a-row pages,
170  // return a pointer to the first page's beginning
171  return pmm_get_address(i - free_pages + 1, 0);
172  }
173  println("%4aPMM: Not enough memory%a");
174  return 0;
175 }
176 
183 void* pmm_alloc(size_t len, pmm_flags_t flags) {
184  void* ptr = pmm_find_free(len); // find some free pages in a row
185  if (!ptr)
186  return 0;
187  pmm_use(ptr, len, flags, "pmm_alloc"); // mark the pages as used
188  return ptr;
189 }
190 
196 void pmm_free(void* ptr, size_t len) {
197  if (len == 0) return;
198  pmm_use(ptr, len, PMM_UNUSED, 0);
199 }
200 
206 pmm_flags_t pmm_check(void* ptr) {
207  return pmm_bitmap_get(pmm_get_page(ptr, 0));
208 }
209 
215 void pmm_dump(void* ptr, size_t len) {
216  if (len == 0) return;
217  uint32_t start_page = pmm_get_page(ptr, 0), end_page = pmm_get_page(ptr, len - 1);
218  log("PMM", "Memory bitmap from page %05x to %05x:", start_page, end_page);
219  for (int i = start_page; i <= end_page; i++) {
220  if ((i - start_page) % 64 == 0) { // line break every 64*PAGE_SIZE bytes
221  uint32_t kilobytes = i * PAGE_SIZE / 1024;
222  logln(0, ""), log("PMM", "[%7d%cB] ",
223  kilobytes % 1024 == 0 ? kilobytes / 1024 : kilobytes,
224  kilobytes % 1024 == 0 ? 'M' : 'K');
225  }
226  log(0, "%x", pmm_bitmap_get(i));
227  }
228  logln(0, "");
229 }
230 
236  return highest_kernel_page;
237 }
238 
static uint32_t highest_kernel_page
remember the highest kernel page
Definition: pmm.c:41
#define PAGE_SIZE
4KB pages
Definition: pmm.c:20
#define PAGE_NUMBER
total number of pages
Definition: pmm.c:24
#define ENTRIES
number of entries in a page table
Definition: pmm.c:25
#define BIT_CHECK(val, bit)
checks a bit in a given value
Definition: pmm.c:32
void pmm_free(void *ptr, size_t len)
Frees page frames.
Definition: pmm.c:196
static uint32_t pmm_bitmap_get(uint32_t idx)
Returns a bitmap entry.
Definition: pmm.c:72
void pmm_init()
Initializes the PMM.
Definition: pmm.c:87
void pmm_dump(void *ptr, size_t len)
Dumps information on page frames for a given memory range.
Definition: pmm.c:215
static void pmm_bitmap_set(uint32_t idx, uint32_t value)
Sets a bitmap entry to a value.
Definition: pmm.c:58
#define TYPE_MASK
calc 2^TYPE_BITS-1
Definition: pmm.c:28
uint32_t pmm_get_page(void *ptr, uint32_t offset)
Returns to which page a given memory address belongs.
Definition: pmm.c:120
#define PAGE_SHIFT
bits to shift to get page (2^12=4096)
Definition: pmm.c:21
pmm_flags_t pmm_check(void *ptr)
Returns whether a page frame is used or unused.
Definition: pmm.c:206
static uint32_t bitmap[PAGE_NUMBER/PAGES_PER_DWORD]
Holds information on used page frames.
Definition: pmm.c:40
#define BITMAP_SET(idx)
sets a bit in the memory bitmap
Definition: pmm.c:34
pmm_flags_t
information on who uses a page frame (needs to fit in TYPE_BITS)
Definition: pmm.h:13
static void pmm_use_kernel_memory()
Marks all kernel memory as used.
Definition: pmm.c:78
const void kernel_end
end of the kernel section
void pmm_use(void *ptr, size_t len, pmm_flags_t flags, char *tag)
Marks page frames for a given memory range as used or unused.
Definition: pmm.c:141
static void * pmm_find_free(size_t len)
Finds free page frames.
Definition: pmm.c:160
#define BITMAP_INIT
0b0101...01, use PMM_RESERVED
Definition: pmm.c:29
void * pmm_alloc(size_t len, pmm_flags_t flags)
Allocates page frames.
Definition: pmm.c:183
#define PAGES_BER_BYTE
pages per bitmap entry byte
Definition: pmm.c:27
#define BITMAP_CLEAR(idx)
clears a bit in the memory bitmap
Definition: pmm.c:36
uint32_t pmm_get_highest_kernel_page()
Returns the highest page used by the kernel.
Definition: pmm.c:235
const void main_kernel_stack_end
end of the main kernel stack
#define PAGES_PER_DWORD
pages in each bitmap entry
Definition: pmm.c:26
#define TYPE_BITS
number of bits per page entry
Definition: pmm.c:23
void * pmm_get_address(uint32_t page, uint32_t offset)
Returns a memory address belonging to a given page.
Definition: pmm.c:130
const void kernel_start
start of the kernel section
static void * main_kernel_stack_start
start of the main kernel stack
Definition: pmm.c:50