]>
Commit | Line | Data |
---|---|---|
04e7ca8d LV |
1 | /* |
2 | * QEMU Motorla 680x0 Macintosh hardware System Emulator | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | * of this software and associated documentation files (the "Software"), to deal | |
6 | * in the Software without restriction, including without limitation the rights | |
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | * copies of the Software, and to permit persons to whom the Software is | |
9 | * furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | * THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include "qemu/osdep.h" | |
24 | #include "qemu/units.h" | |
2c65db5e | 25 | #include "qemu/datadir.h" |
693869a6 | 26 | #include "qemu/guest-random.h" |
04e7ca8d LV |
27 | #include "sysemu/sysemu.h" |
28 | #include "cpu.h" | |
04e7ca8d | 29 | #include "hw/boards.h" |
95264861 | 30 | #include "hw/or-irq.h" |
04e7ca8d LV |
31 | #include "elf.h" |
32 | #include "hw/loader.h" | |
33 | #include "ui/console.h" | |
04e7ca8d LV |
34 | #include "hw/char/escc.h" |
35 | #include "hw/sysbus.h" | |
36 | #include "hw/scsi/esp.h" | |
382d71af LV |
37 | #include "standard-headers/asm-m68k/bootinfo.h" |
38 | #include "standard-headers/asm-m68k/bootinfo-mac.h" | |
04e7ca8d | 39 | #include "bootinfo.h" |
a8019229 | 40 | #include "hw/m68k/q800.h" |
8e093280 | 41 | #include "hw/m68k/q800-glue.h" |
04e7ca8d | 42 | #include "hw/misc/mac_via.h" |
e2fd695e | 43 | #include "hw/misc/djmemc.h" |
04e7ca8d LV |
44 | #include "hw/input/adb.h" |
45 | #include "hw/nubus/mac-nubus-bridge.h" | |
46 | #include "hw/display/macfb.h" | |
47 | #include "hw/block/swim.h" | |
48 | #include "net/net.h" | |
49 | #include "qapi/error.h" | |
cc37d98b | 50 | #include "qemu/error-report.h" |
04e7ca8d LV |
51 | #include "sysemu/qtest.h" |
52 | #include "sysemu/runstate.h" | |
53 | #include "sysemu/reset.h" | |
07e39012 | 54 | #include "migration/vmstate.h" |
04e7ca8d | 55 | |
e24e58e8 | 56 | #define MACROM_ADDR 0x40800000 |
04e7ca8d LV |
57 | #define MACROM_SIZE 0x00100000 |
58 | ||
59 | #define MACROM_FILENAME "MacROM.bin" | |
60 | ||
653901ca LV |
61 | #define IO_BASE 0x50000000 |
62 | #define IO_SLICE 0x00040000 | |
f18a2886 | 63 | #define IO_SLICE_MASK (IO_SLICE - 1) |
653901ca LV |
64 | #define IO_SIZE 0x04000000 |
65 | ||
66 | #define VIA_BASE (IO_BASE + 0x00000) | |
67 | #define SONIC_PROM_BASE (IO_BASE + 0x08000) | |
68 | #define SONIC_BASE (IO_BASE + 0x0a000) | |
69 | #define SCC_BASE (IO_BASE + 0x0c020) | |
e2fd695e | 70 | #define DJMEMC_BASE (IO_BASE + 0x0e000) |
653901ca LV |
71 | #define ESP_BASE (IO_BASE + 0x10000) |
72 | #define ESP_PDMA (IO_BASE + 0x10100) | |
73 | #define ASC_BASE (IO_BASE + 0x14000) | |
74 | #define SWIM_BASE (IO_BASE + 0x1E000) | |
75 | ||
408c5733 MCA |
76 | #define SONIC_PROM_SIZE 0x1000 |
77 | ||
04e7ca8d LV |
78 | /* |
79 | * the video base, whereas it a Nubus address, | |
80 | * is needed by the kernel to have early display and | |
81 | * thus provided by the bootloader | |
82 | */ | |
df8abbba | 83 | #define VIDEO_BASE 0xf9000000 |
04e7ca8d LV |
84 | |
85 | #define MAC_CLOCK 3686418 | |
86 | ||
5ef25141 MCA |
87 | /* |
88 | * Slot 0x9 is reserved for use by the in-built framebuffer whilst only | |
89 | * slots 0xc, 0xd and 0xe physically exist on the Quadra 800 | |
90 | */ | |
91 | #define Q800_NUBUS_SLOTS_AVAILABLE (BIT(0x9) | BIT(0xc) | BIT(0xd) | \ | |
92 | BIT(0xe)) | |
93 | ||
07e39012 | 94 | |
04e7ca8d LV |
95 | static void main_cpu_reset(void *opaque) |
96 | { | |
fbbbe7eb | 97 | M68kCPU *cpu = opaque; |
04e7ca8d LV |
98 | CPUState *cs = CPU(cpu); |
99 | ||
100 | cpu_reset(cs); | |
101 | cpu->env.aregs[7] = ldl_phys(cs->as, 0); | |
102 | cpu->env.pc = ldl_phys(cs->as, 4); | |
103 | } | |
104 | ||
fbbbe7eb JD |
105 | static void rerandomize_rng_seed(void *opaque) |
106 | { | |
107 | struct bi_record *rng_seed = opaque; | |
108 | qemu_guest_getrandom_nofail((void *)rng_seed->data + 2, | |
109 | be16_to_cpu(*(uint16_t *)rng_seed->data)); | |
110 | } | |
111 | ||
e24e58e8 JD |
112 | static uint8_t fake_mac_rom[] = { |
113 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
114 | ||
115 | /* offset: 0xa - mac_reset */ | |
116 | ||
117 | /* via2[vDirB] |= VIA2B_vPower */ | |
118 | 0x20, 0x7C, 0x50, 0xF0, 0x24, 0x00, /* moveal VIA2_BASE+vDirB,%a0 */ | |
119 | 0x10, 0x10, /* moveb %a0@,%d0 */ | |
120 | 0x00, 0x00, 0x00, 0x04, /* orib #4,%d0 */ | |
121 | 0x10, 0x80, /* moveb %d0,%a0@ */ | |
122 | ||
123 | /* via2[vBufB] &= ~VIA2B_vPower */ | |
124 | 0x20, 0x7C, 0x50, 0xF0, 0x20, 0x00, /* moveal VIA2_BASE+vBufB,%a0 */ | |
125 | 0x10, 0x10, /* moveb %a0@,%d0 */ | |
126 | 0x02, 0x00, 0xFF, 0xFB, /* andib #-5,%d0 */ | |
127 | 0x10, 0x80, /* moveb %d0,%a0@ */ | |
128 | ||
129 | /* while (true) ; */ | |
130 | 0x60, 0xFE /* bras [self] */ | |
131 | }; | |
132 | ||
f18a2886 MCA |
133 | static MemTxResult macio_alias_read(void *opaque, hwaddr addr, uint64_t *data, |
134 | unsigned size, MemTxAttrs attrs) | |
135 | { | |
136 | MemTxResult r; | |
137 | uint32_t val; | |
138 | ||
139 | addr &= IO_SLICE_MASK; | |
140 | addr |= IO_BASE; | |
141 | ||
142 | switch (size) { | |
143 | case 4: | |
144 | val = address_space_ldl_be(&address_space_memory, addr, attrs, &r); | |
145 | break; | |
146 | case 2: | |
147 | val = address_space_lduw_be(&address_space_memory, addr, attrs, &r); | |
148 | break; | |
149 | case 1: | |
150 | val = address_space_ldub(&address_space_memory, addr, attrs, &r); | |
151 | break; | |
152 | default: | |
153 | g_assert_not_reached(); | |
154 | } | |
155 | ||
156 | *data = val; | |
157 | return r; | |
158 | } | |
159 | ||
160 | static MemTxResult macio_alias_write(void *opaque, hwaddr addr, uint64_t value, | |
161 | unsigned size, MemTxAttrs attrs) | |
162 | { | |
163 | MemTxResult r; | |
164 | ||
165 | addr &= IO_SLICE_MASK; | |
166 | addr |= IO_BASE; | |
167 | ||
168 | switch (size) { | |
169 | case 4: | |
170 | address_space_stl_be(&address_space_memory, addr, value, attrs, &r); | |
171 | break; | |
172 | case 2: | |
173 | address_space_stw_be(&address_space_memory, addr, value, attrs, &r); | |
174 | break; | |
175 | case 1: | |
176 | address_space_stb(&address_space_memory, addr, value, attrs, &r); | |
177 | break; | |
178 | default: | |
179 | g_assert_not_reached(); | |
180 | } | |
181 | ||
182 | return r; | |
183 | } | |
184 | ||
185 | static const MemoryRegionOps macio_alias_ops = { | |
186 | .read_with_attrs = macio_alias_read, | |
187 | .write_with_attrs = macio_alias_write, | |
188 | .endianness = DEVICE_BIG_ENDIAN, | |
189 | .valid = { | |
190 | .min_access_size = 1, | |
191 | .max_access_size = 4, | |
192 | }, | |
193 | }; | |
194 | ||
1a514d3a | 195 | static void q800_machine_init(MachineState *machine) |
04e7ca8d | 196 | { |
36e2e338 | 197 | Q800MachineState *m = Q800_MACHINE(machine); |
04e7ca8d LV |
198 | int linux_boot; |
199 | int32_t kernel_size; | |
200 | uint64_t elf_entry; | |
201 | char *filename; | |
202 | int bios_size; | |
203 | ram_addr_t initrd_base; | |
204 | int32_t initrd_size; | |
408c5733 MCA |
205 | MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); |
206 | uint8_t *prom; | |
408c5733 | 207 | int i, checksum; |
df8abbba | 208 | MacFbMode *macfb_mode; |
04e7ca8d LV |
209 | ram_addr_t ram_size = machine->ram_size; |
210 | const char *kernel_filename = machine->kernel_filename; | |
211 | const char *initrd_filename = machine->initrd_filename; | |
212 | const char *kernel_cmdline = machine->kernel_cmdline; | |
1684273c | 213 | const char *bios_name = machine->firmware ?: MACROM_FILENAME; |
04e7ca8d LV |
214 | hwaddr parameters_base; |
215 | CPUState *cs; | |
216 | DeviceState *dev; | |
04e7ca8d LV |
217 | SysBusESPState *sysbus_esp; |
218 | ESPState *esp; | |
219 | SysBusDevice *sysbus; | |
220 | BusState *adb_bus; | |
221 | NubusBus *nubus; | |
eb064db9 | 222 | DriveInfo *dinfo; |
693869a6 | 223 | uint8_t rng_seed[32]; |
04e7ca8d LV |
224 | |
225 | linux_boot = (kernel_filename != NULL); | |
226 | ||
227 | if (ram_size > 1 * GiB) { | |
228 | error_report("Too much memory for this machine: %" PRId64 " MiB, " | |
229 | "maximum 1024 MiB", ram_size / MiB); | |
230 | exit(1); | |
231 | } | |
232 | ||
233 | /* init CPUs */ | |
36e2e338 MCA |
234 | object_initialize_child(OBJECT(machine), "cpu", &m->cpu, machine->cpu_type); |
235 | qdev_realize(DEVICE(&m->cpu), NULL, &error_fatal); | |
236 | qemu_register_reset(main_cpu_reset, &m->cpu); | |
04e7ca8d | 237 | |
653901ca | 238 | /* RAM */ |
8591a179 | 239 | memory_region_add_subregion(get_system_memory(), 0, machine->ram); |
04e7ca8d | 240 | |
7527c52f MCA |
241 | /* |
242 | * Create container for all IO devices | |
243 | */ | |
244 | memory_region_init(&m->macio, OBJECT(machine), "mac-io", IO_SLICE); | |
245 | memory_region_add_subregion(get_system_memory(), IO_BASE, &m->macio); | |
246 | ||
653901ca LV |
247 | /* |
248 | * Memory from IO_BASE to IO_BASE + IO_SLICE is repeated | |
249 | * from IO_BASE + IO_SLICE to IO_BASE + IO_SIZE | |
250 | */ | |
f18a2886 MCA |
251 | memory_region_init_io(&m->macio_alias, OBJECT(machine), &macio_alias_ops, |
252 | &m->macio, "mac-io.alias", IO_SIZE - IO_SLICE); | |
253 | memory_region_add_subregion(get_system_memory(), IO_BASE + IO_SLICE, | |
254 | &m->macio_alias); | |
653901ca | 255 | |
04e7ca8d | 256 | /* IRQ Glue */ |
1ecc6ec1 MCA |
257 | object_initialize_child(OBJECT(machine), "glue", &m->glue, TYPE_GLUE); |
258 | object_property_set_link(OBJECT(&m->glue), "cpu", OBJECT(&m->cpu), | |
36e2e338 | 259 | &error_abort); |
1ecc6ec1 | 260 | sysbus_realize(SYS_BUS_DEVICE(&m->glue), &error_fatal); |
04e7ca8d | 261 | |
e2fd695e MCA |
262 | /* djMEMC memory controller */ |
263 | object_initialize_child(OBJECT(machine), "djmemc", &m->djmemc, | |
264 | TYPE_DJMEMC); | |
265 | sysbus = SYS_BUS_DEVICE(&m->djmemc); | |
266 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
267 | memory_region_add_subregion(&m->macio, DJMEMC_BASE - IO_BASE, | |
268 | sysbus_mmio_get_region(sysbus, 0)); | |
269 | ||
02a68a3e | 270 | /* VIA 1 */ |
6d32c064 MCA |
271 | object_initialize_child(OBJECT(machine), "via1", &m->via1, |
272 | TYPE_MOS6522_Q800_VIA1); | |
eb064db9 LV |
273 | dinfo = drive_get(IF_MTD, 0, 0); |
274 | if (dinfo) { | |
6d32c064 MCA |
275 | qdev_prop_set_drive(DEVICE(&m->via1), "drive", |
276 | blk_by_legacy_dinfo(dinfo)); | |
eb064db9 | 277 | } |
6d32c064 MCA |
278 | sysbus = SYS_BUS_DEVICE(&m->via1); |
279 | sysbus_realize(sysbus, &error_fatal); | |
f18a2886 MCA |
280 | memory_region_add_subregion(&m->macio, VIA_BASE - IO_BASE, |
281 | sysbus_mmio_get_region(sysbus, 1)); | |
1ecc6ec1 MCA |
282 | sysbus_connect_irq(sysbus, 0, |
283 | qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_VIA1)); | |
a85d18aa | 284 | /* A/UX mode */ |
6d32c064 | 285 | qdev_connect_gpio_out(DEVICE(&m->via1), 0, |
1ecc6ec1 MCA |
286 | qdev_get_gpio_in_named(DEVICE(&m->glue), |
287 | "auxmode", 0)); | |
02a68a3e | 288 | |
6d32c064 | 289 | adb_bus = qdev_get_child_bus(DEVICE(&m->via1), "adb.0"); |
3e80f690 MA |
290 | dev = qdev_new(TYPE_ADB_KEYBOARD); |
291 | qdev_realize_and_unref(dev, adb_bus, &error_fatal); | |
292 | dev = qdev_new(TYPE_ADB_MOUSE); | |
293 | qdev_realize_and_unref(dev, adb_bus, &error_fatal); | |
04e7ca8d | 294 | |
02a68a3e | 295 | /* VIA 2 */ |
d7942e16 MCA |
296 | object_initialize_child(OBJECT(machine), "via2", &m->via2, |
297 | TYPE_MOS6522_Q800_VIA2); | |
298 | sysbus = SYS_BUS_DEVICE(&m->via2); | |
299 | sysbus_realize(sysbus, &error_fatal); | |
f18a2886 MCA |
300 | memory_region_add_subregion(&m->macio, VIA_BASE - IO_BASE + VIA_SIZE, |
301 | sysbus_mmio_get_region(sysbus, 1)); | |
1ecc6ec1 MCA |
302 | sysbus_connect_irq(sysbus, 0, |
303 | qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_VIA2)); | |
02a68a3e | 304 | |
04e7ca8d LV |
305 | /* MACSONIC */ |
306 | ||
307 | if (nb_nics > 1) { | |
308 | error_report("q800 can only have one ethernet interface"); | |
309 | exit(1); | |
310 | } | |
311 | ||
312 | qemu_check_nic_model(&nd_table[0], "dp83932"); | |
313 | ||
314 | /* | |
315 | * MacSonic driver needs an Apple MAC address | |
316 | * Valid prefix are: | |
317 | * 00:05:02 Apple | |
318 | * 00:80:19 Dayna Communications, Inc. | |
319 | * 00:A0:40 Apple | |
320 | * 08:00:07 Apple | |
321 | * (Q800 use the last one) | |
322 | */ | |
323 | nd_table[0].macaddr.a[0] = 0x08; | |
324 | nd_table[0].macaddr.a[1] = 0x00; | |
325 | nd_table[0].macaddr.a[2] = 0x07; | |
326 | ||
804ae67e MCA |
327 | object_initialize_child(OBJECT(machine), "dp8393x", &m->dp8393x, |
328 | TYPE_DP8393X); | |
329 | dev = DEVICE(&m->dp8393x); | |
04e7ca8d LV |
330 | qdev_set_nic_properties(dev, &nd_table[0]); |
331 | qdev_prop_set_uint8(dev, "it_shift", 2); | |
332 | qdev_prop_set_bit(dev, "big_endian", true); | |
5325cc34 MA |
333 | object_property_set_link(OBJECT(dev), "dma_mr", |
334 | OBJECT(get_system_memory()), &error_abort); | |
04e7ca8d | 335 | sysbus = SYS_BUS_DEVICE(dev); |
804ae67e | 336 | sysbus_realize(sysbus, &error_fatal); |
f18a2886 MCA |
337 | memory_region_add_subregion(&m->macio, SONIC_BASE - IO_BASE, |
338 | sysbus_mmio_get_region(sysbus, 0)); | |
1ecc6ec1 MCA |
339 | sysbus_connect_irq(sysbus, 0, |
340 | qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_SONIC)); | |
04e7ca8d | 341 | |
408c5733 MCA |
342 | memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-q800.prom", |
343 | SONIC_PROM_SIZE, &error_fatal); | |
344 | memory_region_add_subregion(get_system_memory(), SONIC_PROM_BASE, | |
345 | dp8393x_prom); | |
346 | ||
347 | /* Add MAC address with valid checksum to PROM */ | |
348 | prom = memory_region_get_ram_ptr(dp8393x_prom); | |
349 | checksum = 0; | |
350 | for (i = 0; i < 6; i++) { | |
2f0e10a4 | 351 | prom[i] = revbit8(nd_table[0].macaddr.a[i]); |
846feac2 | 352 | checksum ^= prom[i]; |
408c5733 MCA |
353 | } |
354 | prom[7] = 0xff - checksum; | |
355 | ||
04e7ca8d LV |
356 | /* SCC */ |
357 | ||
836126c7 MCA |
358 | object_initialize_child(OBJECT(machine), "escc", &m->escc, |
359 | TYPE_ESCC); | |
360 | dev = DEVICE(&m->escc); | |
04e7ca8d LV |
361 | qdev_prop_set_uint32(dev, "disabled", 0); |
362 | qdev_prop_set_uint32(dev, "frequency", MAC_CLOCK); | |
363 | qdev_prop_set_uint32(dev, "it_shift", 1); | |
364 | qdev_prop_set_bit(dev, "bit_swap", true); | |
365 | qdev_prop_set_chr(dev, "chrA", serial_hd(0)); | |
366 | qdev_prop_set_chr(dev, "chrB", serial_hd(1)); | |
367 | qdev_prop_set_uint32(dev, "chnBtype", 0); | |
368 | qdev_prop_set_uint32(dev, "chnAtype", 0); | |
04e7ca8d | 369 | sysbus = SYS_BUS_DEVICE(dev); |
836126c7 | 370 | sysbus_realize(sysbus, &error_fatal); |
95264861 PM |
371 | |
372 | /* Logically OR both its IRQs together */ | |
1a7a3f00 MCA |
373 | object_initialize_child(OBJECT(machine), "escc_orgate", &m->escc_orgate, |
374 | TYPE_OR_IRQ); | |
375 | object_property_set_int(OBJECT(&m->escc_orgate), "num-lines", 2, | |
376 | &error_fatal); | |
377 | dev = DEVICE(&m->escc_orgate); | |
378 | qdev_realize(dev, NULL, &error_fatal); | |
379 | sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(dev, 0)); | |
380 | sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(dev, 1)); | |
381 | qdev_connect_gpio_out(dev, 0, | |
1ecc6ec1 MCA |
382 | qdev_get_gpio_in(DEVICE(&m->glue), |
383 | GLUE_IRQ_IN_ESCC)); | |
f18a2886 MCA |
384 | memory_region_add_subregion(&m->macio, SCC_BASE - IO_BASE, |
385 | sysbus_mmio_get_region(sysbus, 0)); | |
04e7ca8d LV |
386 | |
387 | /* SCSI */ | |
388 | ||
e78d17ca MCA |
389 | object_initialize_child(OBJECT(machine), "esp", &m->esp, |
390 | TYPE_SYSBUS_ESP); | |
391 | sysbus_esp = SYSBUS_ESP(&m->esp); | |
04e7ca8d LV |
392 | esp = &sysbus_esp->esp; |
393 | esp->dma_memory_read = NULL; | |
394 | esp->dma_memory_write = NULL; | |
395 | esp->dma_opaque = NULL; | |
396 | sysbus_esp->it_shift = 4; | |
397 | esp->dma_enabled = 1; | |
04e7ca8d | 398 | |
e78d17ca MCA |
399 | sysbus = SYS_BUS_DEVICE(&m->esp); |
400 | sysbus_realize(sysbus, &error_fatal); | |
b793b4ef | 401 | /* SCSI and SCSI data IRQs are negative edge triggered */ |
d7942e16 MCA |
402 | sysbus_connect_irq(sysbus, 0, |
403 | qemu_irq_invert( | |
404 | qdev_get_gpio_in(DEVICE(&m->via2), | |
405 | VIA2_IRQ_SCSI_BIT))); | |
406 | sysbus_connect_irq(sysbus, 1, | |
407 | qemu_irq_invert( | |
408 | qdev_get_gpio_in(DEVICE(&m->via2), | |
409 | VIA2_IRQ_SCSI_DATA_BIT))); | |
f18a2886 MCA |
410 | memory_region_add_subregion(&m->macio, ESP_BASE - IO_BASE, |
411 | sysbus_mmio_get_region(sysbus, 0)); | |
412 | memory_region_add_subregion(&m->macio, ESP_PDMA - IO_BASE, | |
413 | sysbus_mmio_get_region(sysbus, 1)); | |
04e7ca8d LV |
414 | |
415 | scsi_bus_legacy_handle_cmdline(&esp->bus); | |
416 | ||
417 | /* SWIM floppy controller */ | |
418 | ||
01f35a4f MCA |
419 | object_initialize_child(OBJECT(machine), "swim", &m->swim, |
420 | TYPE_SWIM); | |
421 | sysbus = SYS_BUS_DEVICE(&m->swim); | |
422 | sysbus_realize(sysbus, &error_fatal); | |
f18a2886 | 423 | memory_region_add_subregion(&m->macio, SWIM_BASE - IO_BASE, |
01f35a4f | 424 | sysbus_mmio_get_region(sysbus, 0)); |
04e7ca8d LV |
425 | |
426 | /* NuBus */ | |
427 | ||
36df1c5a MCA |
428 | object_initialize_child(OBJECT(machine), "mac-nubus-bridge", |
429 | &m->mac_nubus_bridge, | |
430 | TYPE_MAC_NUBUS_BRIDGE); | |
431 | sysbus = SYS_BUS_DEVICE(&m->mac_nubus_bridge); | |
432 | dev = DEVICE(&m->mac_nubus_bridge); | |
433 | qdev_prop_set_uint32(DEVICE(&m->mac_nubus_bridge), "slot-available-mask", | |
5ef25141 | 434 | Q800_NUBUS_SLOTS_AVAILABLE); |
36df1c5a MCA |
435 | sysbus_realize(sysbus, &error_fatal); |
436 | memory_region_add_subregion(get_system_memory(), | |
437 | MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE, | |
438 | sysbus_mmio_get_region(sysbus, 0)); | |
439 | memory_region_add_subregion(get_system_memory(), | |
440 | NUBUS_SLOT_BASE + | |
441 | MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE, | |
442 | sysbus_mmio_get_region(sysbus, 1)); | |
efd0c37e | 443 | qdev_connect_gpio_out(dev, 9, |
d7942e16 | 444 | qdev_get_gpio_in_named(DEVICE(&m->via2), "nubus-irq", |
efd0c37e MCA |
445 | VIA2_NUBUS_IRQ_INTVIDEO)); |
446 | for (i = 1; i < VIA2_NUBUS_IRQ_NB; i++) { | |
b297843e | 447 | qdev_connect_gpio_out(dev, 9 + i, |
d7942e16 MCA |
448 | qdev_get_gpio_in_named(DEVICE(&m->via2), |
449 | "nubus-irq", | |
b297843e MCA |
450 | VIA2_NUBUS_IRQ_9 + i)); |
451 | } | |
452 | ||
f7c6e12e MCA |
453 | /* |
454 | * Since the framebuffer in slot 0x9 uses a separate IRQ, wire the unused | |
455 | * IRQ via GLUE for use by SONIC Ethernet in classic mode | |
456 | */ | |
1ecc6ec1 | 457 | qdev_connect_gpio_out(DEVICE(&m->glue), GLUE_IRQ_NUBUS_9, |
d7942e16 | 458 | qdev_get_gpio_in_named(DEVICE(&m->via2), "nubus-irq", |
f7c6e12e MCA |
459 | VIA2_NUBUS_IRQ_9)); |
460 | ||
464085e8 | 461 | nubus = NUBUS_BUS(qdev_get_child_bus(dev, "nubus-bus.0")); |
04e7ca8d LV |
462 | |
463 | /* framebuffer in nubus slot #9 */ | |
464 | ||
7a1f3acb MCA |
465 | object_initialize_child(OBJECT(machine), "macfb", &m->macfb, |
466 | TYPE_NUBUS_MACFB); | |
467 | dev = DEVICE(&m->macfb); | |
efd0c37e | 468 | qdev_prop_set_uint32(dev, "slot", 9); |
04e7ca8d LV |
469 | qdev_prop_set_uint32(dev, "width", graphic_width); |
470 | qdev_prop_set_uint32(dev, "height", graphic_height); | |
471 | qdev_prop_set_uint8(dev, "depth", graphic_depth); | |
a56c12fb | 472 | if (graphic_width == 1152 && graphic_height == 870) { |
4317c518 MCA |
473 | qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_APPLE_21_COLOR); |
474 | } else { | |
475 | qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA); | |
476 | } | |
7a1f3acb | 477 | qdev_realize(dev, BUS(nubus), &error_fatal); |
04e7ca8d | 478 | |
df8abbba MCA |
479 | macfb_mode = (NUBUS_MACFB(dev)->macfb).mode; |
480 | ||
36e2e338 | 481 | cs = CPU(&m->cpu); |
04e7ca8d LV |
482 | if (linux_boot) { |
483 | uint64_t high; | |
281ac13e JD |
484 | void *param_blob, *param_ptr, *param_rng_seed; |
485 | ||
486 | if (kernel_cmdline) { | |
487 | param_blob = g_malloc(strlen(kernel_cmdline) + 1024); | |
488 | } else { | |
489 | param_blob = g_malloc(1024); | |
490 | } | |
491 | ||
04e7ca8d | 492 | kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, |
6cdda0ff | 493 | &elf_entry, NULL, &high, NULL, 1, |
04e7ca8d LV |
494 | EM_68K, 0, 0); |
495 | if (kernel_size < 0) { | |
496 | error_report("could not load kernel '%s'", kernel_filename); | |
497 | exit(1); | |
498 | } | |
499 | stl_phys(cs->as, 4, elf_entry); /* reset initial PC */ | |
500 | parameters_base = (high + 1) & ~1; | |
281ac13e JD |
501 | param_ptr = param_blob; |
502 | ||
503 | BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_MAC); | |
504 | BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040); | |
505 | BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040); | |
506 | BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040); | |
507 | BOOTINFO1(param_ptr, BI_MAC_CPUID, CPUB_68040); | |
508 | BOOTINFO1(param_ptr, BI_MAC_MODEL, MAC_MODEL_Q800); | |
509 | BOOTINFO1(param_ptr, | |
04e7ca8d | 510 | BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */ |
281ac13e JD |
511 | BOOTINFO2(param_ptr, BI_MEMCHUNK, 0, ram_size); |
512 | BOOTINFO1(param_ptr, BI_MAC_VADDR, | |
df8abbba | 513 | VIDEO_BASE + macfb_mode->offset); |
281ac13e JD |
514 | BOOTINFO1(param_ptr, BI_MAC_VDEPTH, graphic_depth); |
515 | BOOTINFO1(param_ptr, BI_MAC_VDIM, | |
04e7ca8d | 516 | (graphic_height << 16) | graphic_width); |
281ac13e JD |
517 | BOOTINFO1(param_ptr, BI_MAC_VROW, macfb_mode->stride); |
518 | BOOTINFO1(param_ptr, BI_MAC_SCCBASE, SCC_BASE); | |
04e7ca8d | 519 | |
0b9b41fb | 520 | memory_region_init_ram_ptr(&m->rom, NULL, "m68k_fake_mac.rom", |
e24e58e8 | 521 | sizeof(fake_mac_rom), fake_mac_rom); |
0b9b41fb MCA |
522 | memory_region_set_readonly(&m->rom, true); |
523 | memory_region_add_subregion(get_system_memory(), MACROM_ADDR, &m->rom); | |
e24e58e8 | 524 | |
04e7ca8d | 525 | if (kernel_cmdline) { |
281ac13e | 526 | BOOTINFOSTR(param_ptr, BI_COMMAND_LINE, |
04e7ca8d LV |
527 | kernel_cmdline); |
528 | } | |
529 | ||
693869a6 | 530 | /* Pass seed to RNG. */ |
281ac13e | 531 | param_rng_seed = param_ptr; |
693869a6 | 532 | qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); |
281ac13e | 533 | BOOTINFODATA(param_ptr, BI_RNG_SEED, |
693869a6 JD |
534 | rng_seed, sizeof(rng_seed)); |
535 | ||
04e7ca8d LV |
536 | /* load initrd */ |
537 | if (initrd_filename) { | |
538 | initrd_size = get_image_size(initrd_filename); | |
539 | if (initrd_size < 0) { | |
540 | error_report("could not load initial ram disk '%s'", | |
541 | initrd_filename); | |
542 | exit(1); | |
543 | } | |
544 | ||
545 | initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; | |
546 | load_image_targphys(initrd_filename, initrd_base, | |
547 | ram_size - initrd_base); | |
281ac13e | 548 | BOOTINFO2(param_ptr, BI_RAMDISK, initrd_base, |
04e7ca8d LV |
549 | initrd_size); |
550 | } else { | |
551 | initrd_base = 0; | |
552 | initrd_size = 0; | |
553 | } | |
281ac13e JD |
554 | BOOTINFO0(param_ptr, BI_LAST); |
555 | rom_add_blob_fixed_as("bootinfo", param_blob, param_ptr - param_blob, | |
556 | parameters_base, cs->as); | |
fbbbe7eb JD |
557 | qemu_register_reset_nosnapshotload(rerandomize_rng_seed, |
558 | rom_ptr_for_as(cs->as, parameters_base, | |
559 | param_ptr - param_blob) + | |
560 | (param_rng_seed - param_blob)); | |
281ac13e | 561 | g_free(param_blob); |
04e7ca8d LV |
562 | } else { |
563 | uint8_t *ptr; | |
564 | /* allocate and load BIOS */ | |
0b9b41fb | 565 | memory_region_init_rom(&m->rom, NULL, "m68k_mac.rom", MACROM_SIZE, |
04e7ca8d | 566 | &error_abort); |
04e7ca8d | 567 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); |
0b9b41fb | 568 | memory_region_add_subregion(get_system_memory(), MACROM_ADDR, &m->rom); |
04e7ca8d LV |
569 | |
570 | /* Load MacROM binary */ | |
571 | if (filename) { | |
572 | bios_size = load_image_targphys(filename, MACROM_ADDR, MACROM_SIZE); | |
573 | g_free(filename); | |
574 | } else { | |
575 | bios_size = -1; | |
576 | } | |
577 | ||
578 | /* Remove qtest_enabled() check once firmware files are in the tree */ | |
579 | if (!qtest_enabled()) { | |
0969e00b | 580 | if (bios_size <= 0 || bios_size > MACROM_SIZE) { |
04e7ca8d LV |
581 | error_report("could not load MacROM '%s'", bios_name); |
582 | exit(1); | |
583 | } | |
584 | ||
0969e00b LV |
585 | ptr = rom_ptr(MACROM_ADDR, bios_size); |
586 | assert(ptr != NULL); | |
04e7ca8d LV |
587 | stl_phys(cs->as, 0, ldl_p(ptr)); /* reset initial SP */ |
588 | stl_phys(cs->as, 4, | |
589 | MACROM_ADDR + ldl_p(ptr + 4)); /* reset initial PC */ | |
590 | } | |
591 | } | |
592 | } | |
593 | ||
f3582410 | 594 | static GlobalProperty hw_compat_q800[] = { |
26fcbf00 | 595 | { "scsi-hd", "quirk_mode_page_vendor_specific_apple", "on" }, |
0fc37ada MCA |
596 | { "scsi-hd", "vendor", " SEAGATE" }, |
597 | { "scsi-hd", "product", " ST225N" }, | |
598 | { "scsi-hd", "ver", "1.0 " }, | |
26fcbf00 MCA |
599 | { "scsi-cd", "quirk_mode_page_apple_vendor", "on" }, |
600 | { "scsi-cd", "quirk_mode_sense_rom_use_dbd", "on" }, | |
601 | { "scsi-cd", "quirk_mode_page_vendor_specific_apple", "on" }, | |
602 | { "scsi-cd", "quirk_mode_page_truncated", "on" }, | |
74518fb6 MCA |
603 | { "scsi-cd", "vendor", "MATSHITA" }, |
604 | { "scsi-cd", "product", "CD-ROM CR-8005" }, | |
605 | { "scsi-cd", "ver", "1.0k" }, | |
f3582410 MCA |
606 | }; |
607 | static const size_t hw_compat_q800_len = G_N_ELEMENTS(hw_compat_q800); | |
608 | ||
36e2e338 MCA |
609 | static const char *q800_machine_valid_cpu_types[] = { |
610 | M68K_CPU_TYPE_NAME("m68040"), | |
611 | NULL | |
612 | }; | |
613 | ||
04e7ca8d LV |
614 | static void q800_machine_class_init(ObjectClass *oc, void *data) |
615 | { | |
616 | MachineClass *mc = MACHINE_CLASS(oc); | |
1a514d3a | 617 | |
04e7ca8d | 618 | mc->desc = "Macintosh Quadra 800"; |
1a514d3a | 619 | mc->init = q800_machine_init; |
04e7ca8d | 620 | mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); |
36e2e338 | 621 | mc->valid_cpu_types = q800_machine_valid_cpu_types; |
04e7ca8d | 622 | mc->max_cpus = 1; |
04e7ca8d | 623 | mc->block_default_type = IF_SCSI; |
8591a179 | 624 | mc->default_ram_id = "m68k_mac.ram"; |
f3582410 | 625 | compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); |
04e7ca8d LV |
626 | } |
627 | ||
628 | static const TypeInfo q800_machine_typeinfo = { | |
629 | .name = MACHINE_TYPE_NAME("q800"), | |
630 | .parent = TYPE_MACHINE, | |
a8019229 | 631 | .instance_size = sizeof(Q800MachineState), |
04e7ca8d LV |
632 | .class_init = q800_machine_class_init, |
633 | }; | |
634 | ||
635 | static void q800_machine_register_types(void) | |
636 | { | |
637 | type_register_static(&q800_machine_typeinfo); | |
638 | } | |
639 | ||
640 | type_init(q800_machine_register_types) |