kconfig: use pkg-config for ncurses detection
[akaros.git] / kern / arch / x86 / page_alloc.c
1 /* Copyright (c) 2013 The Regents of the University of California.
2  * Copyright (c) 2016 Google Inc
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details. */
5
6 #include <page_alloc.h>
7 #include <pmap.h>
8 #include <kmalloc.h>
9 #include <multiboot.h>
10 #include <arena.h>
11
12 /* Helper.  Adds free entries to the base arena.  Most entries are page aligned,
13  * though on some machines below EXTPHYSMEM we may have some that aren't. */
14 static void parse_mboot_region(struct multiboot_mmap_entry *entry, void *data)
15 {
16         physaddr_t boot_freemem_paddr = (physaddr_t)data;
17         physaddr_t start = entry->addr;
18         size_t len = entry->len;
19         extern char end[];
20
21         if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
22                 return;
23         /* Skip anything over max_paddr - might be bad entries(?) */
24         if (start >= max_paddr)
25                 return;
26         if (start + len > max_paddr)
27                 len = max_paddr - start;
28         /* For paranoia, skip anything below EXTPHYSMEM.  If we ever change
29          * this, we'll need to deal with smp_boot's trampoline page. */
30         if ((start < EXTPHYSMEM) && (start + len < EXTPHYSMEM))
31                 return;
32         if ((start < EXTPHYSMEM) && (EXTPHYSMEM <= start + len)) {
33                 len = start + len - EXTPHYSMEM;
34                 start = EXTPHYSMEM;
35         }
36         /* Skip over any pages already allocated in boot_alloc().
37          * (boot_freemem_paddr is the next free addr.) */
38         if ((start < boot_freemem_paddr)
39             && (boot_freemem_paddr <= start + len)) {
40                 len = start + len - boot_freemem_paddr;
41                 start = boot_freemem_paddr;
42         }
43         /* Skip any part that intersects with the kernel, which is linked and
44          * loaded from EXTPHYSMEM to end in kernel64.ld */
45         if (regions_collide_unsafe(EXTPHYSMEM, PADDR(end), start, start + len))
46         {
47                 len = start + len - PADDR(end);
48                 start = PADDR(end);
49         }
50         /* We need to give the arena PGSIZE-quantum segments. */
51         if (PGOFF(start)) {
52                 len -= PGOFF(start);
53                 start = ROUNDUP(start, PGSIZE);
54         }
55         len = ROUNDDOWN(len, PGSIZE);
56         if (!len)
57                 return;
58         arena_add(base_arena, KADDR(start), len, MEM_WAIT);
59 }
60
61 /* Since we can't parse multiboot mmap entries, we need to just guess at what
62  * pages are free and which ones aren't.
63  *
64  * Despite the lack of info from mbi, I know there is a magic hole in physical
65  * memory that we can't use, from the IOAPIC_PBASE on up [0xfec00000,
66  * 0xffffffff] (I'm being pessimistic).  But, that's not pessimistic enough!
67  * Qemu still doesn't like that.   From using 0xe0000000 instead works for mine.
68  * According to http://wiki.osdev.org/Memory_Map_(x86), some systems could
69  * reserve from [0xc0000000, 0xffffffff].  Anyway, in lieu of real memory
70  * detection, I'm just skipping that entire region.
71  *
72  * We may or may not have more free memory above this magic hole, depending on
73  * both the amount of RAM we have as well as 32 vs 64 bit.
74  *
75  * So we'll go with two free memory regions:
76  *
77  *      [ 0, ROUNDUP(boot_freemem_paddr, PGSIZE) ) = busy
78  *      [ ROUNDUP(boot_freemem_paddr, PGSIZE), TOP_OF_1 ) = free
79  *      [ MAGIC_HOLE, 0x0000000100000000 ) = busy
80  *      (and maybe this:)
81  *      [ 0x0000000100000000, max_paddr ) = free
82  *
83  * where TOP_OF_1 is the min of IOAPIC_PBASE and max_paddr.
84  *
85  * As with parsing mbi regions, this will ignore the hairy areas below
86  * EXTPHYSMEM, and mark the entire kernel and anything we've boot alloc'd as
87  * busy. */
88 static void account_for_pages(physaddr_t boot_freemem_paddr)
89 {
90         physaddr_t top_of_busy = ROUNDUP(boot_freemem_paddr, PGSIZE);
91         physaddr_t top_of_free_1 = MIN(0xc0000000, max_paddr);
92         physaddr_t start_of_free_2;
93
94         printk("Warning: poor memory detection (qemu?). May lose 1GB of RAM\n");
95         arena_add(base_arena, KADDR(top_of_busy), top_of_free_1 - top_of_busy,
96                   MEM_WAIT);
97         /* If max_paddr is less than the start of our potential second free mem
98          * region, we can just leave.  We also don't want to poke around the
99          * pages array either (and accidentally run off the end of the array).
100          *
101          * Additionally, 32 bit doesn't acknowledge pmem above the 4GB mark. */
102         start_of_free_2 = 0x0000000100000000;
103         if (max_paddr < start_of_free_2)
104                 return;
105         arena_add(base_arena, KADDR(start_of_free_2), max_paddr -
106                   start_of_free_2, MEM_WAIT);
107 }
108
109 /* Initialize base arena based on available free memory.  After this, do not use
110  * boot_alloc. */
111 void base_arena_init(struct multiboot_info *mbi)
112 {
113         /* First, all memory is busy / not free by default.
114          *
115          * To avoid a variety of headaches, any memory below 1MB is considered
116          * busy.  Likewise, everything in the kernel, up to _end is also busy.
117          * And everything we've already boot_alloc'd is busy.  These chunks of
118          * memory are reported as 'free' by multiboot.  All of this memory is
119          * below boot_freemem_paddr.  We don't treat anything below that as
120          * free.
121          *
122          * We'll also abort the mapping for any addresses over max_paddr, since
123          * we'll never use them.  'pages' does not track them either.
124          *
125          * One special note: we actually use the memory at 0x1000 for smp_boot.
126          * It'll never get freed; just FYI. */
127         physaddr_t boot_freemem_paddr;
128         void *base_pg;
129
130         /* Need to do the boot-allocs before our last look at the top of
131          * boot_freemem. */
132         base_pg = boot_alloc(PGSIZE, PGSHIFT);
133         base_arena = arena_builder(base_pg, "base", PGSIZE, NULL, NULL, NULL,
134                                    0);
135         boot_freemem_paddr = PADDR(ROUNDUP(boot_freemem, PGSIZE));
136         if (mboot_has_mmaps(mbi)) {
137                 mboot_foreach_mmap(mbi, parse_mboot_region,
138                                    (void*)boot_freemem_paddr);
139         } else {
140                 /* No multiboot mmap regions (probably run from qemu with
141                  * -kernel) */
142                 account_for_pages(boot_freemem_paddr);
143         }
144 }