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