xref: /kvm-unit-tests/lib/x86/setup.c (revision 5b70cbdb7bc2ea65096b51565c75815cc95945b8)
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 	bool found = false;
70 
71 	uintptr_t mmap = bootinfo->mmap_addr;
72 	while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) {
73 		struct mbi_mem *mem = (void *)mmap;
74 		mmap += mem->size + 4;
75 		if (mem->type != 1)
76 			continue;
77 		if (mem->base_addr <= (uintptr_t) &edata ||
78 		    (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end))
79 			continue;
80 		if (mem->length < best_end - best_start)
81 			continue;
82 		best_start = mem->base_addr;
83 		best_end = mem->base_addr + mem->length;
84 		found = true;
85 	}
86 
87 	if (found) {
88 		best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE;
89 		best_end = best_end & -HUGEPAGE_SIZE;
90 		phys_alloc_init(best_start, best_end - best_start);
91 	}
92 }
93 #endif
94 
95 void setup_multiboot(struct mbi_bootinfo *bi)
96 {
97 	struct mbi_module *mods;
98 
99 	bootinfo = bi;
100 
101 	u64 best_start = (uintptr_t) &edata;
102 	u64 best_end = bootinfo->mem_upper * 1024ull;
103 	phys_alloc_init(best_start, best_end - best_start);
104 
105 	if (bootinfo->mods_count != 1)
106 		return;
107 
108 	mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr;
109 
110 	initrd = (char *)(uintptr_t) mods->start;
111 	initrd_size = mods->end - mods->start;
112 }
113 
114 void setup_libcflat(void)
115 {
116 	if (initrd) {
117 		/* environ is currently the only file in the initrd */
118 		u32 size = MIN(initrd_size, ENV_SIZE);
119 		const char *str;
120 
121 		memcpy(env, initrd, size);
122 		setup_env(env, size);
123 		if ((str = getenv("BOOTLOADER")) && atol(str) != 0)
124 			add_setup_arg("bootloader");
125 	}
126 }
127