xref: /kvm-unit-tests/lib/x86/setup.c (revision be704aff683c54fc108deaafacc7cb89ad0648d9)
1 /*
2  * Initialize machine setup information
3  *
4  * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 #include "libcflat.h"
9 #include "fwcfg.h"
10 #include "alloc_phys.h"
11 #include "argv.h"
12 
13 extern char edata;
14 
15 struct mbi_bootinfo {
16 	u32 flags;
17 	u32 mem_lower;
18 	u32 mem_upper;
19 	u32 boot_device;
20 	u32 cmdline;
21 	u32 mods_count;
22 	u32 mods_addr;
23 	u32 reserved[4];   /* 28-43 */
24 	u32 mmap_length;
25 	u32 mmap_addr;
26 	u32 reserved0[3];  /* 52-63 */
27 	u32 bootloader;
28 	u32 reserved1[5];  /* 68-87 */
29 	u32 size;
30 };
31 
32 struct mbi_module {
33 	u32 start, end;
34 	u32 cmdline;
35 	u32 unused;
36 };
37 
38 struct mbi_mem {
39 	u32 size;
40 	u64 base_addr;
41 	u64 length;
42 	u32 type;
43 } __attribute__((packed));
44 
45 #define ENV_SIZE 16384
46 
47 void setup_env(char *env, int size);
48 void setup_multiboot(struct mbi_bootinfo *bootinfo);
49 void setup_libcflat(void);
50 
51 char *initrd;
52 u32 initrd_size;
53 
54 static char env[ENV_SIZE];
55 static struct mbi_bootinfo *bootinfo;
56 
57 #define HUGEPAGE_SIZE (1 << 21)
58 
59 #ifdef __x86_64__
60 void find_highmem(void)
61 {
62 	/* Memory above 4 GB is only supported on 64-bit systems.  */
63 	if (!(bootinfo->flags & 64))
64 	    	return;
65 
66 	u64 upper_end = bootinfo->mem_upper * 1024ull;
67 	u64 best_start = (uintptr_t) &edata;
68 	u64 best_end = upper_end;
69 	u64 max_end = fwcfg_get_u64(FW_CFG_MAX_RAM);
70 	if (max_end == 0)
71 		max_end = -1ull;
72 	bool found = false;
73 
74 	uintptr_t mmap = bootinfo->mmap_addr;
75 	while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) {
76 		struct mbi_mem *mem = (void *)mmap;
77 		mmap += mem->size + 4;
78 		if (mem->type != 1)
79 			continue;
80 		if (mem->base_addr <= (uintptr_t) &edata ||
81 		    (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end))
82 			continue;
83 		if (mem->length < best_end - best_start)
84 			continue;
85 		if (mem->base_addr >= max_end)
86 			continue;
87 		best_start = mem->base_addr;
88 		best_end = mem->base_addr + mem->length;
89 		if (best_end > max_end)
90 			best_end = max_end;
91 		found = true;
92 	}
93 
94 	if (found) {
95 		best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE;
96 		best_end = best_end & -HUGEPAGE_SIZE;
97 		phys_alloc_init(best_start, best_end - best_start);
98 	}
99 }
100 #endif
101 
102 void setup_multiboot(struct mbi_bootinfo *bi)
103 {
104 	struct mbi_module *mods;
105 
106 	bootinfo = bi;
107 
108 	u64 best_start = (uintptr_t) &edata;
109 	u64 best_end = bootinfo->mem_upper * 1024ull;
110 	phys_alloc_init(best_start, best_end - best_start);
111 
112 	if (bootinfo->mods_count != 1)
113 		return;
114 
115 	mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr;
116 
117 	initrd = (char *)(uintptr_t) mods->start;
118 	initrd_size = mods->end - mods->start;
119 }
120 
121 void setup_libcflat(void)
122 {
123 	if (initrd) {
124 		/* environ is currently the only file in the initrd */
125 		u32 size = MIN(initrd_size, ENV_SIZE);
126 		const char *str;
127 
128 		memcpy(env, initrd, size);
129 		setup_env(env, size);
130 		if ((str = getenv("BOOTLOADER")) && atol(str) != 0)
131 			add_setup_arg("bootloader");
132 	}
133 }
134