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