net: Add network_offset to blocks
[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 this,
29          * 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) && (boot_freemem_paddr <= start + len)) {
39                 len = start + len - boot_freemem_paddr;
40                 start = boot_freemem_paddr;
41         }
42         /* Skip any part that intersects with the kernel, which is linked and loaded
43          * from EXTPHYSMEM to end in kernel64.ld */
44         if (regions_collide_unsafe(EXTPHYSMEM, PADDR(end), start, start + len)) {
45                 len = start + len - PADDR(end);
46                 start = PADDR(end);
47         }
48         /* We need to give the arena PGSIZE-quantum segments. */
49         if (PGOFF(start)) {
50                 len -= PGOFF(start);
51                 start = ROUNDUP(start, PGSIZE);
52         }
53         len = ROUNDDOWN(len, PGSIZE);
54         if (!len)
55                 return;
56         arena_add(base_arena, KADDR(start), len, MEM_WAIT);
57 }
58
59 /* Since we can't parse multiboot mmap entries, we need to just guess at what
60  * pages are free and which ones aren't.
61  *
62  * Despite the lack of info from mbi, I know there is a magic hole in physical
63  * memory that we can't use, from the IOAPIC_PBASE on up [0xfec00000,
64  * 0xffffffff] (I'm being pessimistic).  But, that's not pessimistic enough!
65  * Qemu still doesn't like that.   From using 0xe0000000 instead works for mine.
66  * According to http://wiki.osdev.org/Memory_Map_(x86), some systems could
67  * reserve from [0xc0000000, 0xffffffff].  Anyway, in lieu of real memory
68  * detection, I'm just skipping that entire region.
69  *
70  * We may or may not have more free memory above this magic hole, depending on
71  * both the amount of RAM we have as well as 32 vs 64 bit.
72  *
73  * So we'll go with two free memory regions:
74  *
75  *              [ 0, ROUNDUP(boot_freemem_paddr, PGSIZE) ) = busy
76  *              [ ROUNDUP(boot_freemem_paddr, PGSIZE), TOP_OF_1 ) = free
77  *              [ MAGIC_HOLE, 0x0000000100000000 ) = busy
78  *              (and maybe this:)
79  *              [ 0x0000000100000000, max_paddr ) = free
80  *
81  * where TOP_OF_1 is the min of IOAPIC_PBASE and max_paddr.
82  *
83  * As with parsing mbi regions, this will ignore the hairy areas below
84  * EXTPHYSMEM, and mark the entire kernel and anything we've boot alloc'd as
85  * busy. */
86 static void account_for_pages(physaddr_t boot_freemem_paddr)
87 {
88         physaddr_t top_of_busy = ROUNDUP(boot_freemem_paddr, PGSIZE);
89         physaddr_t top_of_free_1 = MIN(0xc0000000, max_paddr);
90         physaddr_t start_of_free_2;
91
92         printk("Warning: poor memory detection (qemu?).  May lose 1GB of RAM\n");
93         arena_add(base_arena, KADDR(top_of_busy), top_of_free_1 - top_of_busy,
94                   MEM_WAIT);
95         /* If max_paddr is less than the start of our potential second free mem
96          * region, we can just leave.  We also don't want to poke around the pages
97          * array either (and accidentally run off the end of the array).
98          *
99          * Additionally, 32 bit doesn't acknowledge pmem above the 4GB mark. */
100         start_of_free_2 = 0x0000000100000000;
101         if (max_paddr < start_of_free_2)
102                 return;
103         arena_add(base_arena, KADDR(start_of_free_2), max_paddr - start_of_free_2,
104                   MEM_WAIT);
105 }
106
107 /* Initialize base arena based on available free memory.  After this, do not use
108  * boot_alloc. */
109 void base_arena_init(struct multiboot_info *mbi)
110 {
111         /* First, all memory is busy / not free by default.
112          *
113          * To avoid a variety of headaches, any memory below 1MB is considered busy.
114          * Likewise, everything in the kernel, up to _end is also busy.  And
115          * everything we've already boot_alloc'd is busy.  These chunks of memory
116          * are reported as 'free' by multiboot.  All of this memory is below
117          * boot_freemem_paddr.  We don't treat anything below that as free.
118          *
119          * We'll also abort the mapping for any addresses over max_paddr, since
120          * we'll never use them.  'pages' does not track them either.
121          *
122          * One special note: we actually use the memory at 0x1000 for smp_boot.
123          * It'll never get freed; just FYI. */
124         physaddr_t boot_freemem_paddr;
125         void *base_pg;
126
127         /* Need to do the boot-allocs before our last look at the top of
128          * boot_freemem. */
129         base_pg = boot_alloc(PGSIZE, PGSHIFT);
130         base_arena = arena_builder(base_pg, "base", PGSIZE, NULL, NULL, NULL,
131                                    0);
132         boot_freemem_paddr = PADDR(ROUNDUP(boot_freemem, PGSIZE));
133         if (mboot_has_mmaps(mbi)) {
134                 mboot_foreach_mmap(mbi, parse_mboot_region, (void*)boot_freemem_paddr);
135         } else {
136                 /* No multiboot mmap regions (probably run from qemu with -kernel) */
137                 account_for_pages(boot_freemem_paddr);
138         }
139 }