]>
Commit | Line | Data |
---|---|---|
9e933f4a BH |
1 | /* |
2 | * QEMU PowerPC PowerNV machine model | |
3 | * | |
4 | * Copyright (c) 2016, IBM Corporation. | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
21 | #include "qapi/error.h" | |
22 | #include "sysemu/sysemu.h" | |
23 | #include "sysemu/numa.h" | |
24 | #include "hw/hw.h" | |
25 | #include "target-ppc/cpu.h" | |
26 | #include "qemu/log.h" | |
27 | #include "hw/ppc/fdt.h" | |
28 | #include "hw/ppc/ppc.h" | |
29 | #include "hw/ppc/pnv.h" | |
30 | #include "hw/loader.h" | |
31 | #include "exec/address-spaces.h" | |
32 | #include "qemu/cutils.h" | |
33 | ||
34 | #include <libfdt.h> | |
35 | ||
36 | #define FDT_MAX_SIZE 0x00100000 | |
37 | ||
38 | #define FW_FILE_NAME "skiboot.lid" | |
39 | #define FW_LOAD_ADDR 0x0 | |
40 | #define FW_MAX_SIZE 0x00400000 | |
41 | ||
42 | #define KERNEL_LOAD_ADDR 0x20000000 | |
43 | #define INITRD_LOAD_ADDR 0x40000000 | |
44 | ||
45 | /* | |
46 | * On Power Systems E880 (POWER8), the max cpus (threads) should be : | |
47 | * 4 * 4 sockets * 12 cores * 8 threads = 1536 | |
48 | * Let's make it 2^11 | |
49 | */ | |
50 | #define MAX_CPUS 2048 | |
51 | ||
52 | /* | |
53 | * Memory nodes are created by hostboot, one for each range of memory | |
54 | * that has a different "affinity". In practice, it means one range | |
55 | * per chip. | |
56 | */ | |
57 | static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start, | |
58 | hwaddr size) | |
59 | { | |
60 | char *mem_name; | |
61 | uint64_t mem_reg_property[2]; | |
62 | int off; | |
63 | ||
64 | mem_reg_property[0] = cpu_to_be64(start); | |
65 | mem_reg_property[1] = cpu_to_be64(size); | |
66 | ||
67 | mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start); | |
68 | off = fdt_add_subnode(fdt, 0, mem_name); | |
69 | g_free(mem_name); | |
70 | ||
71 | _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); | |
72 | _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, | |
73 | sizeof(mem_reg_property)))); | |
74 | _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id))); | |
75 | } | |
76 | ||
77 | static void *powernv_create_fdt(MachineState *machine) | |
78 | { | |
79 | const char plat_compat[] = "qemu,powernv\0ibm,powernv"; | |
80 | PnvMachineState *pnv = POWERNV_MACHINE(machine); | |
81 | void *fdt; | |
82 | char *buf; | |
83 | int off; | |
84 | ||
85 | fdt = g_malloc0(FDT_MAX_SIZE); | |
86 | _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); | |
87 | ||
88 | /* Root node */ | |
89 | _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2))); | |
90 | _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2))); | |
91 | _FDT((fdt_setprop_string(fdt, 0, "model", | |
92 | "IBM PowerNV (emulated by qemu)"))); | |
93 | _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat, | |
94 | sizeof(plat_compat)))); | |
95 | ||
96 | buf = qemu_uuid_unparse_strdup(&qemu_uuid); | |
97 | _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf))); | |
98 | if (qemu_uuid_set) { | |
99 | _FDT((fdt_property_string(fdt, "system-id", buf))); | |
100 | } | |
101 | g_free(buf); | |
102 | ||
103 | off = fdt_add_subnode(fdt, 0, "chosen"); | |
104 | if (machine->kernel_cmdline) { | |
105 | _FDT((fdt_setprop_string(fdt, off, "bootargs", | |
106 | machine->kernel_cmdline))); | |
107 | } | |
108 | ||
109 | if (pnv->initrd_size) { | |
110 | uint32_t start_prop = cpu_to_be32(pnv->initrd_base); | |
111 | uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size); | |
112 | ||
113 | _FDT((fdt_setprop(fdt, off, "linux,initrd-start", | |
114 | &start_prop, sizeof(start_prop)))); | |
115 | _FDT((fdt_setprop(fdt, off, "linux,initrd-end", | |
116 | &end_prop, sizeof(end_prop)))); | |
117 | } | |
118 | ||
119 | /* TODO: put all the memory in one node on chip 0 until we find a | |
120 | * way to specify different ranges for each chip | |
121 | */ | |
122 | powernv_populate_memory_node(fdt, 0, 0, machine->ram_size); | |
123 | ||
124 | return fdt; | |
125 | } | |
126 | ||
127 | static void ppc_powernv_reset(void) | |
128 | { | |
129 | MachineState *machine = MACHINE(qdev_get_machine()); | |
130 | void *fdt; | |
131 | ||
132 | qemu_devices_reset(); | |
133 | ||
134 | fdt = powernv_create_fdt(machine); | |
135 | ||
136 | /* Pack resulting tree */ | |
137 | _FDT((fdt_pack(fdt))); | |
138 | ||
139 | cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); | |
140 | } | |
141 | ||
142 | static void ppc_powernv_init(MachineState *machine) | |
143 | { | |
144 | PnvMachineState *pnv = POWERNV_MACHINE(machine); | |
145 | MemoryRegion *ram; | |
146 | char *fw_filename; | |
147 | long fw_size; | |
148 | ||
149 | /* allocate RAM */ | |
150 | if (machine->ram_size < (1 * G_BYTE)) { | |
151 | error_report("Warning: skiboot may not work with < 1GB of RAM"); | |
152 | } | |
153 | ||
154 | ram = g_new(MemoryRegion, 1); | |
155 | memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", | |
156 | machine->ram_size); | |
157 | memory_region_add_subregion(get_system_memory(), 0, ram); | |
158 | ||
159 | /* load skiboot firmware */ | |
160 | if (bios_name == NULL) { | |
161 | bios_name = FW_FILE_NAME; | |
162 | } | |
163 | ||
164 | fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); | |
165 | ||
166 | fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); | |
167 | if (fw_size < 0) { | |
168 | hw_error("qemu: could not load OPAL '%s'\n", fw_filename); | |
169 | exit(1); | |
170 | } | |
171 | g_free(fw_filename); | |
172 | ||
173 | /* load kernel */ | |
174 | if (machine->kernel_filename) { | |
175 | long kernel_size; | |
176 | ||
177 | kernel_size = load_image_targphys(machine->kernel_filename, | |
178 | KERNEL_LOAD_ADDR, 0x2000000); | |
179 | if (kernel_size < 0) { | |
180 | hw_error("qemu: could not load kernel'%s'\n", | |
181 | machine->kernel_filename); | |
182 | exit(1); | |
183 | } | |
184 | } | |
185 | ||
186 | /* load initrd */ | |
187 | if (machine->initrd_filename) { | |
188 | pnv->initrd_base = INITRD_LOAD_ADDR; | |
189 | pnv->initrd_size = load_image_targphys(machine->initrd_filename, | |
190 | pnv->initrd_base, 0x10000000); /* 128MB max */ | |
191 | if (pnv->initrd_size < 0) { | |
192 | error_report("qemu: could not load initial ram disk '%s'", | |
193 | machine->initrd_filename); | |
194 | exit(1); | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | static void powernv_machine_class_init(ObjectClass *oc, void *data) | |
200 | { | |
201 | MachineClass *mc = MACHINE_CLASS(oc); | |
202 | ||
203 | mc->desc = "IBM PowerNV (Non-Virtualized)"; | |
204 | mc->init = ppc_powernv_init; | |
205 | mc->reset = ppc_powernv_reset; | |
206 | mc->max_cpus = MAX_CPUS; | |
207 | mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for | |
208 | * storage */ | |
209 | mc->no_parallel = 1; | |
210 | mc->default_boot_order = NULL; | |
211 | mc->default_ram_size = 1 * G_BYTE; | |
212 | } | |
213 | ||
214 | static const TypeInfo powernv_machine_info = { | |
215 | .name = TYPE_POWERNV_MACHINE, | |
216 | .parent = TYPE_MACHINE, | |
217 | .instance_size = sizeof(PnvMachineState), | |
218 | .class_init = powernv_machine_class_init, | |
219 | }; | |
220 | ||
221 | static void powernv_machine_register_types(void) | |
222 | { | |
223 | type_register_static(&powernv_machine_info); | |
224 | } | |
225 | ||
226 | type_init(powernv_machine_register_types) |