1 | |
2 | /* |
3 | * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator |
4 | * |
5 | * Copyright (c) 2004-2007 Fabrice Bellard |
6 | * Copyright (c) 2007 Jocelyn Mayer |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | * of this software and associated documentation files (the "Software"), to deal |
10 | * in the Software without restriction, including without limitation the rights |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | * copies of the Software, and to permit persons to whom the Software is |
13 | * furnished to do so, subject to the following conditions: |
14 | * |
15 | * The above copyright notice and this permission notice shall be included in |
16 | * all copies or substantial portions of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
24 | * THE SOFTWARE. |
25 | */ |
26 | |
27 | #include "qemu/osdep.h" |
28 | #include "qemu-common.h" |
29 | #include "qemu/units.h" |
30 | #include "qapi/error.h" |
31 | #include "hw/ppc/ppc.h" |
32 | #include "hw/qdev-properties.h" |
33 | #include "mac.h" |
34 | #include "hw/input/adb.h" |
35 | #include "sysemu/sysemu.h" |
36 | #include "net/net.h" |
37 | #include "hw/isa/isa.h" |
38 | #include "hw/pci/pci.h" |
39 | #include "hw/pci/pci_host.h" |
40 | #include "hw/boards.h" |
41 | #include "hw/nvram/fw_cfg.h" |
42 | #include "hw/char/escc.h" |
43 | #include "hw/misc/macio/macio.h" |
44 | #include "hw/ide.h" |
45 | #include "hw/loader.h" |
46 | #include "hw/fw-path-provider.h" |
47 | #include "elf.h" |
48 | #include "qemu/error-report.h" |
49 | #include "sysemu/kvm.h" |
50 | #include "sysemu/reset.h" |
51 | #include "kvm_ppc.h" |
52 | #include "exec/address-spaces.h" |
53 | |
54 | #define MAX_IDE_BUS 2 |
55 | #define CFG_ADDR 0xf0000510 |
56 | #define TBFREQ 16600000UL |
57 | #define CLOCKFREQ 266000000UL |
58 | #define BUSFREQ 66000000UL |
59 | |
60 | #define NDRV_VGA_FILENAME "qemu_vga.ndrv" |
61 | |
62 | #define GRACKLE_BASE 0xfec00000 |
63 | |
64 | static void fw_cfg_boot_set(void *opaque, const char *boot_device, |
65 | Error **errp) |
66 | { |
67 | fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); |
68 | } |
69 | |
70 | static uint64_t translate_kernel_address(void *opaque, uint64_t addr) |
71 | { |
72 | return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; |
73 | } |
74 | |
75 | static void ppc_heathrow_reset(void *opaque) |
76 | { |
77 | PowerPCCPU *cpu = opaque; |
78 | |
79 | cpu_reset(CPU(cpu)); |
80 | } |
81 | |
82 | static void ppc_heathrow_init(MachineState *machine) |
83 | { |
84 | ram_addr_t ram_size = machine->ram_size; |
85 | const char *kernel_filename = machine->kernel_filename; |
86 | const char *kernel_cmdline = machine->kernel_cmdline; |
87 | const char *initrd_filename = machine->initrd_filename; |
88 | const char *boot_device = machine->boot_order; |
89 | MemoryRegion *sysmem = get_system_memory(); |
90 | PowerPCCPU *cpu = NULL; |
91 | CPUPPCState *env = NULL; |
92 | char *filename; |
93 | int linux_boot, i; |
94 | MemoryRegion *ram = g_new(MemoryRegion, 1); |
95 | MemoryRegion *bios = g_new(MemoryRegion, 1); |
96 | uint32_t kernel_base, initrd_base, cmdline_base = 0; |
97 | int32_t kernel_size, initrd_size; |
98 | PCIBus *pci_bus; |
99 | OldWorldMacIOState *macio; |
100 | MACIOIDEState *macio_ide; |
101 | SysBusDevice *s; |
102 | DeviceState *dev, *pic_dev; |
103 | BusState *adb_bus; |
104 | int bios_size; |
105 | unsigned int smp_cpus = machine->smp.cpus; |
106 | uint16_t ppc_boot_device; |
107 | DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; |
108 | void *fw_cfg; |
109 | uint64_t tbfreq; |
110 | |
111 | linux_boot = (kernel_filename != NULL); |
112 | |
113 | /* init CPUs */ |
114 | for (i = 0; i < smp_cpus; i++) { |
115 | cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); |
116 | env = &cpu->env; |
117 | |
118 | /* Set time-base frequency to 16.6 Mhz */ |
119 | cpu_ppc_tb_init(env, TBFREQ); |
120 | qemu_register_reset(ppc_heathrow_reset, cpu); |
121 | } |
122 | |
123 | /* allocate RAM */ |
124 | if (ram_size > 2047 * MiB) { |
125 | error_report("Too much memory for this machine: %" PRId64 " MB, " |
126 | "maximum 2047 MB" , ram_size / MiB); |
127 | exit(1); |
128 | } |
129 | |
130 | memory_region_allocate_system_memory(ram, NULL, "ppc_heathrow.ram" , |
131 | ram_size); |
132 | memory_region_add_subregion(sysmem, 0, ram); |
133 | |
134 | /* allocate and load BIOS */ |
135 | memory_region_init_ram(bios, NULL, "ppc_heathrow.bios" , BIOS_SIZE, |
136 | &error_fatal); |
137 | |
138 | if (bios_name == NULL) |
139 | bios_name = PROM_FILENAME; |
140 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); |
141 | memory_region_set_readonly(bios, true); |
142 | memory_region_add_subregion(sysmem, PROM_ADDR, bios); |
143 | |
144 | /* Load OpenBIOS (ELF) */ |
145 | if (filename) { |
146 | bios_size = load_elf(filename, NULL, 0, NULL, NULL, NULL, NULL, |
147 | 1, PPC_ELF_MACHINE, 0, 0); |
148 | g_free(filename); |
149 | } else { |
150 | bios_size = -1; |
151 | } |
152 | if (bios_size < 0 || bios_size > BIOS_SIZE) { |
153 | error_report("could not load PowerPC bios '%s'" , bios_name); |
154 | exit(1); |
155 | } |
156 | |
157 | if (linux_boot) { |
158 | uint64_t lowaddr = 0; |
159 | int bswap_needed; |
160 | |
161 | #ifdef BSWAP_NEEDED |
162 | bswap_needed = 1; |
163 | #else |
164 | bswap_needed = 0; |
165 | #endif |
166 | kernel_base = KERNEL_LOAD_ADDR; |
167 | kernel_size = load_elf(kernel_filename, NULL, |
168 | translate_kernel_address, NULL, |
169 | NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, |
170 | 0, 0); |
171 | if (kernel_size < 0) |
172 | kernel_size = load_aout(kernel_filename, kernel_base, |
173 | ram_size - kernel_base, bswap_needed, |
174 | TARGET_PAGE_SIZE); |
175 | if (kernel_size < 0) |
176 | kernel_size = load_image_targphys(kernel_filename, |
177 | kernel_base, |
178 | ram_size - kernel_base); |
179 | if (kernel_size < 0) { |
180 | error_report("could not load kernel '%s'" , kernel_filename); |
181 | exit(1); |
182 | } |
183 | /* load initrd */ |
184 | if (initrd_filename) { |
185 | initrd_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); |
186 | initrd_size = load_image_targphys(initrd_filename, initrd_base, |
187 | ram_size - initrd_base); |
188 | if (initrd_size < 0) { |
189 | error_report("could not load initial ram disk '%s'" , |
190 | initrd_filename); |
191 | exit(1); |
192 | } |
193 | cmdline_base = TARGET_PAGE_ALIGN(initrd_base + initrd_size); |
194 | } else { |
195 | initrd_base = 0; |
196 | initrd_size = 0; |
197 | cmdline_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); |
198 | } |
199 | ppc_boot_device = 'm'; |
200 | } else { |
201 | kernel_base = 0; |
202 | kernel_size = 0; |
203 | initrd_base = 0; |
204 | initrd_size = 0; |
205 | ppc_boot_device = '\0'; |
206 | for (i = 0; boot_device[i] != '\0'; i++) { |
207 | /* TOFIX: for now, the second IDE channel is not properly |
208 | * used by OHW. The Mac floppy disk are not emulated. |
209 | * For now, OHW cannot boot from the network. |
210 | */ |
211 | #if 0 |
212 | if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { |
213 | ppc_boot_device = boot_device[i]; |
214 | break; |
215 | } |
216 | #else |
217 | if (boot_device[i] >= 'c' && boot_device[i] <= 'd') { |
218 | ppc_boot_device = boot_device[i]; |
219 | break; |
220 | } |
221 | #endif |
222 | } |
223 | if (ppc_boot_device == '\0') { |
224 | error_report("No valid boot device for G3 Beige machine" ); |
225 | exit(1); |
226 | } |
227 | } |
228 | |
229 | /* XXX: we register only 1 output pin for heathrow PIC */ |
230 | pic_dev = qdev_create(NULL, TYPE_HEATHROW); |
231 | qdev_init_nofail(pic_dev); |
232 | |
233 | /* Connect the heathrow PIC outputs to the 6xx bus */ |
234 | for (i = 0; i < smp_cpus; i++) { |
235 | switch (PPC_INPUT(env)) { |
236 | case PPC_FLAGS_INPUT_6xx: |
237 | qdev_connect_gpio_out(pic_dev, 0, |
238 | ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]); |
239 | break; |
240 | default: |
241 | error_report("Bus model not supported on OldWorld Mac machine" ); |
242 | exit(1); |
243 | } |
244 | } |
245 | |
246 | /* Timebase Frequency */ |
247 | if (kvm_enabled()) { |
248 | tbfreq = kvmppc_get_tbfreq(); |
249 | } else { |
250 | tbfreq = TBFREQ; |
251 | } |
252 | |
253 | /* init basic PC hardware */ |
254 | if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { |
255 | error_report("Only 6xx bus is supported on heathrow machine" ); |
256 | exit(1); |
257 | } |
258 | |
259 | /* Grackle PCI host bridge */ |
260 | dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); |
261 | qdev_prop_set_uint32(dev, "ofw-addr" , 0x80000000); |
262 | object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic" , |
263 | &error_abort); |
264 | qdev_init_nofail(dev); |
265 | s = SYS_BUS_DEVICE(dev); |
266 | sysbus_mmio_map(s, 0, GRACKLE_BASE); |
267 | sysbus_mmio_map(s, 1, GRACKLE_BASE + 0x200000); |
268 | /* PCI hole */ |
269 | memory_region_add_subregion(get_system_memory(), 0x80000000ULL, |
270 | sysbus_mmio_get_region(s, 2)); |
271 | /* Register 2 MB of ISA IO space */ |
272 | memory_region_add_subregion(get_system_memory(), 0xfe000000, |
273 | sysbus_mmio_get_region(s, 3)); |
274 | |
275 | pci_bus = PCI_HOST_BRIDGE(dev)->bus; |
276 | |
277 | pci_vga_init(pci_bus); |
278 | |
279 | for (i = 0; i < nb_nics; i++) { |
280 | pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci" , NULL); |
281 | } |
282 | |
283 | ide_drive_get(hd, ARRAY_SIZE(hd)); |
284 | |
285 | /* MacIO */ |
286 | macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); |
287 | dev = DEVICE(macio); |
288 | qdev_prop_set_uint64(dev, "frequency" , tbfreq); |
289 | object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic" , |
290 | &error_abort); |
291 | qdev_init_nofail(dev); |
292 | |
293 | macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), |
294 | "ide[0]" )); |
295 | macio_ide_init_drives(macio_ide, hd); |
296 | |
297 | macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), |
298 | "ide[1]" )); |
299 | macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); |
300 | |
301 | dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda" )); |
302 | adb_bus = qdev_get_child_bus(dev, "adb.0" ); |
303 | dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); |
304 | qdev_init_nofail(dev); |
305 | dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); |
306 | qdev_init_nofail(dev); |
307 | |
308 | if (machine_usb(machine)) { |
309 | pci_create_simple(pci_bus, -1, "pci-ohci" ); |
310 | } |
311 | |
312 | if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) |
313 | graphic_depth = 15; |
314 | |
315 | /* No PCI init: the BIOS will do it */ |
316 | |
317 | dev = qdev_create(NULL, TYPE_FW_CFG_MEM); |
318 | fw_cfg = FW_CFG(dev); |
319 | qdev_prop_set_uint32(dev, "data_width" , 1); |
320 | qdev_prop_set_bit(dev, "dma_enabled" , false); |
321 | object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, |
322 | OBJECT(fw_cfg), NULL); |
323 | qdev_init_nofail(dev); |
324 | s = SYS_BUS_DEVICE(dev); |
325 | sysbus_mmio_map(s, 0, CFG_ADDR); |
326 | sysbus_mmio_map(s, 1, CFG_ADDR + 2); |
327 | |
328 | fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); |
329 | fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)machine->smp.max_cpus); |
330 | fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); |
331 | fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW); |
332 | fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); |
333 | fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); |
334 | if (kernel_cmdline) { |
335 | fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); |
336 | pstrcpy_targphys("cmdline" , cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); |
337 | } else { |
338 | fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); |
339 | } |
340 | fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); |
341 | fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); |
342 | fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device); |
343 | |
344 | fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); |
345 | fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); |
346 | fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); |
347 | |
348 | fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); |
349 | if (kvm_enabled()) { |
350 | uint8_t *hypercall; |
351 | |
352 | hypercall = g_malloc(16); |
353 | kvmppc_get_hypercall(env, hypercall, 16); |
354 | fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); |
355 | fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); |
356 | } |
357 | fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq); |
358 | /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ |
359 | fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ); |
360 | fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ); |
361 | |
362 | /* MacOS NDRV VGA driver */ |
363 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, NDRV_VGA_FILENAME); |
364 | if (filename) { |
365 | gchar *ndrv_file; |
366 | gsize ndrv_size; |
367 | |
368 | if (g_file_get_contents(filename, &ndrv_file, &ndrv_size, NULL)) { |
369 | fw_cfg_add_file(fw_cfg, "ndrv/qemu_vga.ndrv" , ndrv_file, ndrv_size); |
370 | } |
371 | g_free(filename); |
372 | } |
373 | |
374 | qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); |
375 | } |
376 | |
377 | /* |
378 | * Implementation of an interface to adjust firmware path |
379 | * for the bootindex property handling. |
380 | */ |
381 | static char *heathrow_fw_dev_path(FWPathProvider *p, BusState *bus, |
382 | DeviceState *dev) |
383 | { |
384 | PCIDevice *pci; |
385 | IDEBus *ide_bus; |
386 | IDEState *ide_s; |
387 | MACIOIDEState *macio_ide; |
388 | |
389 | if (!strcmp(object_get_typename(OBJECT(dev)), "macio-oldworld" )) { |
390 | pci = PCI_DEVICE(dev); |
391 | return g_strdup_printf("mac-io@%x" , PCI_SLOT(pci->devfn)); |
392 | } |
393 | |
394 | if (!strcmp(object_get_typename(OBJECT(dev)), "macio-ide" )) { |
395 | macio_ide = MACIO_IDE(dev); |
396 | return g_strdup_printf("ata-3@%x" , macio_ide->addr); |
397 | } |
398 | |
399 | if (!strcmp(object_get_typename(OBJECT(dev)), "ide-drive" )) { |
400 | ide_bus = IDE_BUS(qdev_get_parent_bus(dev)); |
401 | ide_s = idebus_active_if(ide_bus); |
402 | |
403 | if (ide_s->drive_kind == IDE_CD) { |
404 | return g_strdup("cdrom" ); |
405 | } |
406 | |
407 | return g_strdup("disk" ); |
408 | } |
409 | |
410 | if (!strcmp(object_get_typename(OBJECT(dev)), "ide-hd" )) { |
411 | return g_strdup("disk" ); |
412 | } |
413 | |
414 | if (!strcmp(object_get_typename(OBJECT(dev)), "ide-cd" )) { |
415 | return g_strdup("cdrom" ); |
416 | } |
417 | |
418 | if (!strcmp(object_get_typename(OBJECT(dev)), "virtio-blk-device" )) { |
419 | return g_strdup("disk" ); |
420 | } |
421 | |
422 | return NULL; |
423 | } |
424 | |
425 | static int heathrow_kvm_type(MachineState *machine, const char *arg) |
426 | { |
427 | /* Always force PR KVM */ |
428 | return 2; |
429 | } |
430 | |
431 | static void heathrow_class_init(ObjectClass *oc, void *data) |
432 | { |
433 | MachineClass *mc = MACHINE_CLASS(oc); |
434 | FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); |
435 | |
436 | mc->desc = "Heathrow based PowerMAC" ; |
437 | mc->init = ppc_heathrow_init; |
438 | mc->block_default_type = IF_IDE; |
439 | mc->max_cpus = MAX_CPUS; |
440 | #ifndef TARGET_PPC64 |
441 | mc->is_default = 1; |
442 | #endif |
443 | /* TOFIX "cad" when Mac floppy is implemented */ |
444 | mc->default_boot_order = "cd" ; |
445 | mc->kvm_type = heathrow_kvm_type; |
446 | mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1" ); |
447 | mc->default_display = "std" ; |
448 | mc->ignore_boot_device_suffixes = true; |
449 | fwc->get_dev_path = heathrow_fw_dev_path; |
450 | } |
451 | |
452 | static const TypeInfo ppc_heathrow_machine_info = { |
453 | .name = MACHINE_TYPE_NAME("g3beige" ), |
454 | .parent = TYPE_MACHINE, |
455 | .class_init = heathrow_class_init, |
456 | .interfaces = (InterfaceInfo[]) { |
457 | { TYPE_FW_PATH_PROVIDER }, |
458 | { } |
459 | }, |
460 | }; |
461 | |
462 | static void ppc_heathrow_register_types(void) |
463 | { |
464 | type_register_static(&ppc_heathrow_machine_info); |
465 | } |
466 | |
467 | type_init(ppc_heathrow_register_types); |
468 | |