]>
Commit | Line | Data |
---|---|---|
e1cecdca | 1 | /* |
65b4c8c7 | 2 | * SPDX-License-Identifier: GPL-2.0-or-later |
e1cecdca LV |
3 | * |
4 | * QEMU Vitual M68K Machine | |
5 | * | |
6 | * (c) 2020 Laurent Vivier <laurent@vivier.eu> | |
7 | * | |
8 | */ | |
9 | ||
10 | #include "qemu/osdep.h" | |
11 | #include "qemu/units.h" | |
e1cecdca LV |
12 | #include "sysemu/sysemu.h" |
13 | #include "cpu.h" | |
e1cecdca | 14 | #include "hw/boards.h" |
e1cecdca LV |
15 | #include "hw/qdev-properties.h" |
16 | #include "elf.h" | |
17 | #include "hw/loader.h" | |
18 | #include "ui/console.h" | |
e1cecdca LV |
19 | #include "hw/sysbus.h" |
20 | #include "standard-headers/asm-m68k/bootinfo.h" | |
21 | #include "standard-headers/asm-m68k/bootinfo-virt.h" | |
22 | #include "bootinfo.h" | |
23 | #include "net/net.h" | |
24 | #include "qapi/error.h" | |
25 | #include "sysemu/qtest.h" | |
26 | #include "sysemu/runstate.h" | |
27 | #include "sysemu/reset.h" | |
28 | ||
29 | #include "hw/intc/m68k_irqc.h" | |
30 | #include "hw/misc/virt_ctrl.h" | |
31 | #include "hw/char/goldfish_tty.h" | |
32 | #include "hw/rtc/goldfish_rtc.h" | |
33 | #include "hw/intc/goldfish_pic.h" | |
34 | #include "hw/virtio/virtio-mmio.h" | |
35 | #include "hw/virtio/virtio-blk.h" | |
36 | ||
37 | /* | |
38 | * 6 goldfish-pic for CPU IRQ #1 to IRQ #6 | |
39 | * CPU IRQ #1 -> PIC #1 | |
40 | * IRQ #1 to IRQ #31 -> unused | |
41 | * IRQ #32 -> goldfish-tty | |
42 | * CPU IRQ #2 -> PIC #2 | |
43 | * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32 | |
44 | * CPU IRQ #3 -> PIC #3 | |
45 | * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64 | |
46 | * CPU IRQ #4 -> PIC #4 | |
47 | * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96 | |
48 | * CPU IRQ #5 -> PIC #5 | |
49 | * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128 | |
50 | * CPU IRQ #6 -> PIC #6 | |
51 | * IRQ #1 -> goldfish-rtc | |
52 | * IRQ #2 to IRQ #32 -> unused | |
53 | * CPU IRQ #7 -> NMI | |
54 | */ | |
55 | ||
56 | #define PIC_IRQ_BASE(num) (8 + (num - 1) * 32) | |
57 | #define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1) | |
58 | #define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], \ | |
59 | (pic_irq - 8) % 32)) | |
60 | ||
61 | #define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */ | |
62 | #define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */ | |
63 | #define VIRT_GF_PIC_NB 6 | |
64 | ||
65 | /* 2 goldfish-rtc (and timer) */ | |
66 | #define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */ | |
67 | #define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */ | |
68 | #define VIRT_GF_RTC_NB 2 | |
69 | ||
70 | /* 1 goldfish-tty */ | |
71 | #define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */ | |
72 | #define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */ | |
73 | ||
74 | /* 1 virt-ctrl */ | |
75 | #define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */ | |
76 | #define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */ | |
77 | ||
78 | /* | |
79 | * virtio-mmio size is 0x200 bytes | |
80 | * we use 4 goldfish-pic to attach them, | |
81 | * we can attach 32 virtio devices / goldfish-pic | |
82 | * -> we can manage 32 * 4 = 128 virtio devices | |
83 | */ | |
84 | #define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */ | |
85 | #define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */ | |
86 | ||
e48b140e LV |
87 | typedef struct { |
88 | M68kCPU *cpu; | |
89 | hwaddr initial_pc; | |
90 | hwaddr initial_stack; | |
91 | } ResetInfo; | |
92 | ||
e1cecdca LV |
93 | static void main_cpu_reset(void *opaque) |
94 | { | |
e48b140e LV |
95 | ResetInfo *reset_info = opaque; |
96 | M68kCPU *cpu = reset_info->cpu; | |
e1cecdca LV |
97 | CPUState *cs = CPU(cpu); |
98 | ||
99 | cpu_reset(cs); | |
e48b140e LV |
100 | cpu->env.aregs[7] = reset_info->initial_stack; |
101 | cpu->env.pc = reset_info->initial_pc; | |
e1cecdca LV |
102 | } |
103 | ||
104 | static void virt_init(MachineState *machine) | |
105 | { | |
106 | M68kCPU *cpu = NULL; | |
107 | int32_t kernel_size; | |
108 | uint64_t elf_entry; | |
109 | ram_addr_t initrd_base; | |
110 | int32_t initrd_size; | |
111 | ram_addr_t ram_size = machine->ram_size; | |
112 | const char *kernel_filename = machine->kernel_filename; | |
113 | const char *initrd_filename = machine->initrd_filename; | |
114 | const char *kernel_cmdline = machine->kernel_cmdline; | |
115 | hwaddr parameters_base; | |
116 | DeviceState *dev; | |
117 | DeviceState *irqc_dev; | |
118 | DeviceState *pic_dev[VIRT_GF_PIC_NB]; | |
119 | SysBusDevice *sysbus; | |
120 | hwaddr io_base; | |
121 | int i; | |
e48b140e | 122 | ResetInfo *reset_info; |
e1cecdca LV |
123 | |
124 | if (ram_size > 3399672 * KiB) { | |
125 | /* | |
126 | * The physical memory can be up to 4 GiB - 16 MiB, but linux | |
127 | * kernel crashes after this limit (~ 3.2 GiB) | |
128 | */ | |
129 | error_report("Too much memory for this machine: %" PRId64 " KiB, " | |
130 | "maximum 3399672 KiB", ram_size / KiB); | |
131 | exit(1); | |
132 | } | |
133 | ||
b21e2380 | 134 | reset_info = g_new0(ResetInfo, 1); |
e48b140e | 135 | |
e1cecdca LV |
136 | /* init CPUs */ |
137 | cpu = M68K_CPU(cpu_create(machine->cpu_type)); | |
e48b140e LV |
138 | |
139 | reset_info->cpu = cpu; | |
140 | qemu_register_reset(main_cpu_reset, reset_info); | |
e1cecdca LV |
141 | |
142 | /* RAM */ | |
143 | memory_region_add_subregion(get_system_memory(), 0, machine->ram); | |
144 | ||
145 | /* IRQ Controller */ | |
146 | ||
147 | irqc_dev = qdev_new(TYPE_M68K_IRQC); | |
148 | sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal); | |
149 | ||
150 | /* | |
151 | * 6 goldfish-pic | |
152 | * | |
153 | * map: 0xff000000 - 0xff006fff = 28 KiB | |
154 | * IRQ: #1 (lower priority) -> #6 (higher priority) | |
155 | * | |
156 | */ | |
157 | io_base = VIRT_GF_PIC_MMIO_BASE; | |
158 | for (i = 0; i < VIRT_GF_PIC_NB; i++) { | |
159 | pic_dev[i] = qdev_new(TYPE_GOLDFISH_PIC); | |
160 | sysbus = SYS_BUS_DEVICE(pic_dev[i]); | |
161 | qdev_prop_set_uint8(pic_dev[i], "index", i); | |
162 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
163 | ||
164 | sysbus_mmio_map(sysbus, 0, io_base); | |
165 | sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(irqc_dev, i)); | |
166 | ||
167 | io_base += 0x1000; | |
168 | } | |
169 | ||
170 | /* goldfish-rtc */ | |
171 | io_base = VIRT_GF_RTC_MMIO_BASE; | |
172 | for (i = 0; i < VIRT_GF_RTC_NB; i++) { | |
173 | dev = qdev_new(TYPE_GOLDFISH_RTC); | |
174 | sysbus = SYS_BUS_DEVICE(dev); | |
175 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
176 | sysbus_mmio_map(sysbus, 0, io_base); | |
177 | sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_RTC_IRQ_BASE + i)); | |
178 | ||
179 | io_base += 0x1000; | |
180 | } | |
181 | ||
182 | /* goldfish-tty */ | |
183 | dev = qdev_new(TYPE_GOLDFISH_TTY); | |
184 | sysbus = SYS_BUS_DEVICE(dev); | |
185 | qdev_prop_set_chr(dev, "chardev", serial_hd(0)); | |
186 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
187 | sysbus_mmio_map(sysbus, 0, VIRT_GF_TTY_MMIO_BASE); | |
188 | sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE)); | |
189 | ||
190 | /* virt controller */ | |
191 | dev = qdev_new(TYPE_VIRT_CTRL); | |
192 | sysbus = SYS_BUS_DEVICE(dev); | |
193 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
194 | sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE); | |
195 | sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE)); | |
196 | ||
197 | /* virtio-mmio */ | |
198 | io_base = VIRT_VIRTIO_MMIO_BASE; | |
199 | for (i = 0; i < 128; i++) { | |
200 | dev = qdev_new(TYPE_VIRTIO_MMIO); | |
201 | qdev_prop_set_bit(dev, "force-legacy", false); | |
202 | sysbus = SYS_BUS_DEVICE(dev); | |
203 | sysbus_realize_and_unref(sysbus, &error_fatal); | |
204 | sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_VIRTIO_IRQ_BASE + i)); | |
205 | sysbus_mmio_map(sysbus, 0, io_base); | |
206 | io_base += 0x200; | |
207 | } | |
208 | ||
209 | if (kernel_filename) { | |
210 | CPUState *cs = CPU(cpu); | |
211 | uint64_t high; | |
212 | ||
213 | kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, | |
214 | &elf_entry, NULL, &high, NULL, 1, | |
215 | EM_68K, 0, 0); | |
216 | if (kernel_size < 0) { | |
217 | error_report("could not load kernel '%s'", kernel_filename); | |
218 | exit(1); | |
219 | } | |
e48b140e | 220 | reset_info->initial_pc = elf_entry; |
e1cecdca LV |
221 | parameters_base = (high + 1) & ~1; |
222 | ||
223 | BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT); | |
224 | BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040); | |
225 | BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040); | |
226 | BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040); | |
227 | BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size); | |
228 | ||
229 | BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION, | |
230 | ((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) | | |
231 | (QEMU_VERSION_MICRO << 8))); | |
232 | BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE, | |
233 | VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE); | |
234 | BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE, | |
235 | VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE); | |
236 | BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE, | |
237 | VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE); | |
238 | BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE, | |
239 | VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE); | |
240 | BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE, | |
241 | VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE); | |
242 | ||
243 | if (kernel_cmdline) { | |
244 | BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE, | |
245 | kernel_cmdline); | |
246 | } | |
247 | ||
248 | /* load initrd */ | |
249 | if (initrd_filename) { | |
250 | initrd_size = get_image_size(initrd_filename); | |
251 | if (initrd_size < 0) { | |
252 | error_report("could not load initial ram disk '%s'", | |
253 | initrd_filename); | |
254 | exit(1); | |
255 | } | |
256 | ||
257 | initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; | |
258 | load_image_targphys(initrd_filename, initrd_base, | |
259 | ram_size - initrd_base); | |
260 | BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base, | |
261 | initrd_size); | |
262 | } else { | |
263 | initrd_base = 0; | |
264 | initrd_size = 0; | |
265 | } | |
266 | BOOTINFO0(cs->as, parameters_base, BI_LAST); | |
267 | } | |
268 | } | |
269 | ||
270 | static void virt_machine_class_init(ObjectClass *oc, void *data) | |
271 | { | |
272 | MachineClass *mc = MACHINE_CLASS(oc); | |
273 | mc->desc = "QEMU M68K Virtual Machine"; | |
274 | mc->init = virt_init; | |
275 | mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); | |
276 | mc->max_cpus = 1; | |
277 | mc->no_floppy = 1; | |
278 | mc->no_parallel = 1; | |
279 | mc->default_ram_id = "m68k_virt.ram"; | |
280 | } | |
281 | ||
282 | static const TypeInfo virt_machine_info = { | |
283 | .name = MACHINE_TYPE_NAME("virt"), | |
284 | .parent = TYPE_MACHINE, | |
285 | .abstract = true, | |
286 | .class_init = virt_machine_class_init, | |
287 | }; | |
288 | ||
289 | static void virt_machine_register_types(void) | |
290 | { | |
291 | type_register_static(&virt_machine_info); | |
292 | } | |
293 | ||
294 | type_init(virt_machine_register_types) | |
295 | ||
296 | #define DEFINE_VIRT_MACHINE(major, minor, latest) \ | |
297 | static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ | |
298 | void *data) \ | |
299 | { \ | |
300 | MachineClass *mc = MACHINE_CLASS(oc); \ | |
301 | virt_machine_##major##_##minor##_options(mc); \ | |
302 | mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \ | |
303 | if (latest) { \ | |
304 | mc->alias = "virt"; \ | |
305 | } \ | |
306 | } \ | |
307 | static const TypeInfo machvirt_##major##_##minor##_info = { \ | |
308 | .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ | |
309 | .parent = MACHINE_TYPE_NAME("virt"), \ | |
310 | .class_init = virt_##major##_##minor##_class_init, \ | |
311 | }; \ | |
312 | static void machvirt_machine_##major##_##minor##_init(void) \ | |
313 | { \ | |
314 | type_register_static(&machvirt_##major##_##minor##_info); \ | |
315 | } \ | |
316 | type_init(machvirt_machine_##major##_##minor##_init); | |
317 | ||
214bdf8e LV |
318 | static void virt_machine_7_0_options(MachineClass *mc) |
319 | { | |
320 | } | |
321 | DEFINE_VIRT_MACHINE(7, 0, true) | |
322 | ||
6ed25621 LV |
323 | static void virt_machine_6_2_options(MachineClass *mc) |
324 | { | |
214bdf8e LV |
325 | virt_machine_7_0_options(mc); |
326 | compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); | |
6ed25621 | 327 | } |
214bdf8e | 328 | DEFINE_VIRT_MACHINE(6, 2, false) |
6ed25621 | 329 | |
6837f299 LV |
330 | static void virt_machine_6_1_options(MachineClass *mc) |
331 | { | |
6ed25621 LV |
332 | virt_machine_6_2_options(mc); |
333 | compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); | |
6837f299 | 334 | } |
6ed25621 | 335 | DEFINE_VIRT_MACHINE(6, 1, false) |
6837f299 | 336 | |
e1cecdca LV |
337 | static void virt_machine_6_0_options(MachineClass *mc) |
338 | { | |
6837f299 LV |
339 | virt_machine_6_1_options(mc); |
340 | compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); | |
e1cecdca | 341 | } |
6837f299 | 342 | DEFINE_VIRT_MACHINE(6, 0, false) |