1 /*
2  * Copyright (c) 2009 Corey Tabaka
3  * Copyright (c) 2015 Intel Corporation
4  * Copyright (c) 2016 Travis Geiselbrecht
5  *
6  * Use of this source code is governed by a MIT-style
7  * license that can be found in the LICENSE file or at
8  * https://opensource.org/licenses/MIT
9  */
10 
11 #include <lk/err.h>
12 #include <lk/init.h>
13 #include <lk/trace.h>
14 #include <arch/x86/mmu.h>
15 #include <platform.h>
16 #include <platform/pc.h>
17 #include <platform/console.h>
18 #include <platform/keyboard.h>
19 #include <dev/uart.h>
20 #include <hw/multiboot.h>
21 #include <arch/x86.h>
22 #include <arch/mmu.h>
23 #include <malloc.h>
24 #include <assert.h>
25 #include <inttypes.h>
26 #include <kernel/vm.h>
27 #include <lib/acpi_lite.h>
28 
29 #include "platform_p.h"
30 
31 #if WITH_DEV_BUS_PCI
32 #include <dev/bus/pci.h>
33 #endif
34 #if WITH_LIB_MINIP
35 #include <lib/minip.h>
36 #endif
37 
38 #define LOCAL_TRACE 0
39 
40 /* multiboot information passed in, if present */
41 extern uint32_t _multiboot_info;
42 
43 #define DEFAULT_MEMEND (16*1024*1024)
44 
45 extern uint64_t __code_start;
46 extern uint64_t __code_end;
47 extern uint64_t __rodata_start;
48 extern uint64_t __rodata_end;
49 extern uint64_t __data_start;
50 extern uint64_t __data_end;
51 extern uint64_t __bss_start;
52 extern uint64_t __bss_end;
53 
54 /* based on multiboot (or other methods) we support up to 16 arenas */
55 #define NUM_ARENAS 16
56 static pmm_arena_t mem_arena[NUM_ARENAS];
57 
58 /* parse an array of multiboot mmap entries */
parse_multiboot_mmap(const memory_map_t * mmap,const size_t mmap_length,size_t * found_mem_arenas)59 static status_t parse_multiboot_mmap(const memory_map_t *mmap, const size_t mmap_length, size_t *found_mem_arenas) {
60     for (uint i = 0; i < mmap_length / sizeof(memory_map_t); i++) {
61 
62         uint64_t base = mmap[i].base_addr_low | (uint64_t)mmap[i].base_addr_high << 32;
63         uint64_t length = mmap[i].length_low | (uint64_t)mmap[i].length_high << 32;
64 
65         dprintf(SPEW, "\ttype %u addr %#" PRIx64 " len %#" PRIx64 "\n",
66                 mmap[i].type, base, length);
67         if (mmap[i].type == MB_MMAP_TYPE_AVAILABLE) {
68 
69             /* do some sanity checks to cut out small arenas */
70             if (length < PAGE_SIZE * 2) {
71                 continue;
72             }
73 
74             /* align the base and length */
75             uint64_t oldbase = base;
76             base = PAGE_ALIGN(base);
77             if (base > oldbase) {
78                 length -= base - oldbase;
79             }
80             length = ROUNDDOWN(length, PAGE_SIZE);
81 
82             /* ignore memory < 1MB */
83             if (base < 1*MB) {
84                 /* skip everything < 1MB */
85                 continue;
86             }
87 
88             /* ignore everything that extends past the size PHYSMAP maps into the kernel.
89              * see arch/x86/arch.c mmu_initial_mappings
90              */
91             if (base >= PHYSMAP_SIZE) {
92                 continue;
93             }
94             uint64_t end = base + length;
95             if (end > PHYSMAP_SIZE) {
96                 end = PHYSMAP_SIZE;
97                 DEBUG_ASSERT(end > base);
98                 length = end - base;
99                 dprintf(INFO, "PC: trimmed memory to %" PRIu64 " bytes\n", PHYSMAP_SIZE);
100             }
101 
102             /* initialize a new pmm arena */
103             mem_arena[*found_mem_arenas].name = "memory";
104             mem_arena[*found_mem_arenas].base = base;
105             mem_arena[*found_mem_arenas].size = length;
106             mem_arena[*found_mem_arenas].priority = 1;
107             mem_arena[*found_mem_arenas].flags = PMM_ARENA_FLAG_KMAP;
108             (*found_mem_arenas)++;
109             if (*found_mem_arenas == countof(mem_arena)) {
110                 break;
111             }
112         }
113     }
114 
115     return NO_ERROR;
116 }
117 
118 /* Walk through the multiboot structure and attempt to discover all of the runs
119  * of physical memory to bootstrap the pmm areas.
120  * Returns number of arenas initialized in passed in pointer
121  */
platform_parse_multiboot_info(size_t * found_mem_arenas)122 static status_t platform_parse_multiboot_info(size_t *found_mem_arenas) {
123     *found_mem_arenas = 0;
124 
125     dprintf(SPEW, "PC: multiboot address %#" PRIx32 "\n", _multiboot_info);
126     if (_multiboot_info == 0) {
127         return ERR_NOT_FOUND;
128     }
129 
130     /* bump the multiboot pointer up to the kernel mapping */
131     /* TODO: test that it's within range of the kernel mapping */
132     const multiboot_info_t *multiboot_info = (void *)((uintptr_t)_multiboot_info + KERNEL_BASE);
133 
134     dprintf(SPEW, "\tflags %#x\n", multiboot_info->flags);
135 
136     // legacy multiboot memory size field
137     if (multiboot_info->flags & MB_INFO_MEM_SIZE) {
138         dprintf(SPEW, "PC: multiboot memory lower %#x upper %#" PRIx64 "\n",
139                 multiboot_info->mem_lower * 1024U, multiboot_info->mem_upper * 1024ULL);
140         if ((multiboot_info->flags & MB_INFO_MMAP) == 0) {
141             // There is no mmap to give us a more detailed memory map
142             // so we'll need to use this one. Synthesize a fake mmap array to pass
143             // to the mmap code.
144             memory_map_t mmap[2] = {};
145             mmap[0].length_low = multiboot_info->mem_lower * 1024U;
146             mmap[0].type = MB_MMAP_TYPE_AVAILABLE;
147             mmap[1].base_addr_low = 1 * 1024U * 1024U;
148             mmap[1].length_low = multiboot_info->mem_upper * 1024U;
149             mmap[1].type = MB_MMAP_TYPE_AVAILABLE;
150             parse_multiboot_mmap(mmap, 2 * sizeof(memory_map_t), found_mem_arenas);
151         }
152     }
153 
154     // more modern multiboot mmap array
155     if (multiboot_info->flags & MB_INFO_MMAP) {
156         const memory_map_t *mmap = (const memory_map_t *)(uintptr_t)multiboot_info->mmap_addr;
157         mmap = (void *)((uintptr_t)mmap + KERNEL_BASE);
158 
159         dprintf(SPEW, "PC: multiboot memory map, length %u:\n", multiboot_info->mmap_length);
160         parse_multiboot_mmap(mmap, multiboot_info->mmap_length, found_mem_arenas);
161     }
162 
163     if (multiboot_info->flags & MB_INFO_FRAMEBUFFER) {
164         dprintf(SPEW, "PC: multiboot framebuffer info present\n");
165         dprintf(SPEW, "\taddress %#" PRIx64 " pitch %u width %u height %u bpp %hhu type %u\n",
166                 multiboot_info->framebuffer_addr, multiboot_info->framebuffer_pitch,
167                 multiboot_info->framebuffer_width, multiboot_info->framebuffer_height,
168                 multiboot_info->framebuffer_bpp, multiboot_info->framebuffer_type);
169 
170         if (multiboot_info->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
171             dprintf(SPEW, "\tcolor bit layout: R %u:%u G %u:%u B %u:%u\n",
172                     multiboot_info->framebuffer_red_field_position, multiboot_info->framebuffer_red_mask_size,
173                     multiboot_info->framebuffer_green_field_position, multiboot_info->framebuffer_green_mask_size,
174                     multiboot_info->framebuffer_blue_field_position, multiboot_info->framebuffer_blue_mask_size);
175         }
176     }
177 
178     return NO_ERROR;
179 }
180 
platform_early_init(void)181 void platform_early_init(void) {
182     /* get the debug output working */
183     platform_init_debug_early();
184 
185     /* get the text console working */
186     platform_init_console();
187 
188     /* initialize the interrupt controller */
189     platform_init_interrupts();
190 
191     /* look at multiboot to determine our memory size */
192     size_t found_arenas;
193     platform_parse_multiboot_info(&found_arenas);
194     if (found_arenas <= 0) {
195         /* if we couldn't find any memory, initialize a default arena */
196         mem_arena[0] = (pmm_arena_t) {
197             .name = "memory",
198             .base = MEMBASE,
199             .size = DEFAULT_MEMEND,
200             .priority = 1,
201             .flags = PMM_ARENA_FLAG_KMAP
202         };
203         found_arenas = 1;
204         printf("PC: WARNING failed to detect memory map from multiboot, using default\n");
205     }
206 
207     DEBUG_ASSERT(found_arenas > 0 && found_arenas <= countof(mem_arena));
208 
209     /* add the arenas we just set up to the pmm */
210     uint64_t total_mem = 0;
211     for (size_t i = 0; i < found_arenas; i++) {
212         pmm_add_arena(&mem_arena[i]);
213         total_mem += mem_arena[i].size;
214     }
215     dprintf(INFO, "PC: total memory detected %" PRIu64 " bytes\n", total_mem);
216 }
217 
platform_init(void)218 void platform_init(void) {
219     platform_init_debug();
220 
221     platform_init_keyboard(&console_input_buf);
222 
223     // Look for the root ACPI table
224     __UNUSED bool found_acpi = false;
225     if (acpi_lite_init(0) == NO_ERROR) {
226         if (LOCAL_TRACE) {
227             acpi_lite_dump_tables(false);
228         }
229         acpi_lite_dump_madt_table();
230         found_acpi = true;
231     }
232 
233     // Look for secondary cpus
234 #if WITH_SMP
235     platform_start_secondary_cpus();
236 #endif
237 
238 #if WITH_DEV_BUS_PCI
239     bool pci_initted = false;
240     if (found_acpi) {
241         // TODO: handle interrupt source overrides from the MADT table
242 
243         // try to find the mcfg table
244         const struct acpi_mcfg_table *table = (const struct acpi_mcfg_table *)acpi_get_table_by_sig(ACPI_MCFG_SIG);
245         if (table) {
246             if (table->header.length >= sizeof(*table) + sizeof(struct acpi_mcfg_entry)) {
247                 const struct acpi_mcfg_entry *entry = (const void *)(table + 1);
248                 printf("PCI MCFG: segment %#hx bus [%hhu...%hhu] address %#llx\n",
249                         entry->segment, entry->start_bus, entry->end_bus, entry->base_address);
250 
251                 // try to initialize pci based on the MCFG ecam aperture
252                 status_t err = pci_init_ecam(entry->base_address, entry->segment, entry->start_bus, entry->end_bus);
253                 if (err == NO_ERROR) {
254                     pci_bus_mgr_init();
255                     pci_initted = true;
256                 }
257             }
258         }
259     }
260 
261     // fall back to legacy pci if we couldn't find the pcie aperture
262     if (!pci_initted) {
263         status_t err = pci_init_legacy();
264         if (err == NO_ERROR) {
265             pci_bus_mgr_init();
266         }
267     }
268 #endif
269 
270     platform_init_mmu_mappings();
271 }
272 
273 #if WITH_LIB_MINIP
_start_minip(uint level)274 void _start_minip(uint level) {
275     extern status_t e1000_register_with_minip(void);
276     status_t err = e1000_register_with_minip();
277     if (err == NO_ERROR) {
278         minip_start_dhcp();
279     }
280 }
281 
282 LK_INIT_HOOK(start_minip, _start_minip, LK_INIT_LEVEL_APPS - 1);
283 #endif
284