]>
Commit | Line | Data |
---|---|---|
df1d8a1f PB |
1 | /* |
2 | * MIPS Boston development board emulation. | |
3 | * | |
4 | * Copyright (c) 2016 Imagination Technologies | |
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 | |
4a129ccd | 9 | * version 2.1 of the License, or (at your option) any later version. |
df1d8a1f PB |
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" | |
fc6b3cf9 | 21 | #include "qemu/units.h" |
df1d8a1f | 22 | |
10e3f30f | 23 | #include "elf.h" |
df1d8a1f PB |
24 | #include "hw/boards.h" |
25 | #include "hw/char/serial.h" | |
df1d8a1f | 26 | #include "hw/ide/pci.h" |
d407be08 | 27 | #include "hw/ide/ahci-pci.h" |
df1d8a1f PB |
28 | #include "hw/loader.h" |
29 | #include "hw/loader-fit.h" | |
112658eb | 30 | #include "hw/mips/bootloader.h" |
df1d8a1f | 31 | #include "hw/mips/cps.h" |
df1d8a1f | 32 | #include "hw/pci-host/xilinx-pcie.h" |
6b290b41 | 33 | #include "hw/qdev-clock.h" |
a27bd6c7 | 34 | #include "hw/qdev-properties.h" |
df1d8a1f | 35 | #include "qapi/error.h" |
df1d8a1f | 36 | #include "qemu/error-report.h" |
5e19cc68 | 37 | #include "qemu/guest-random.h" |
df1d8a1f | 38 | #include "qemu/log.h" |
8228e353 | 39 | #include "chardev/char.h" |
df1d8a1f PB |
40 | #include "sysemu/device_tree.h" |
41 | #include "sysemu/sysemu.h" | |
42 | #include "sysemu/qtest.h" | |
54d31236 | 43 | #include "sysemu/runstate.h" |
4fbae244 | 44 | #include "sysemu/reset.h" |
df1d8a1f PB |
45 | |
46 | #include <libfdt.h> | |
db1015e9 | 47 | #include "qom/object.h" |
df1d8a1f | 48 | |
27cf0896 | 49 | #define TYPE_BOSTON "mips-boston" |
db1015e9 | 50 | typedef struct BostonState BostonState; |
8110fa1d | 51 | DECLARE_INSTANCE_CHECKER(BostonState, BOSTON, |
27cf0896 | 52 | TYPE_BOSTON) |
df1d8a1f | 53 | |
72303899 JY |
54 | #define FDT_IRQ_TYPE_NONE 0 |
55 | #define FDT_IRQ_TYPE_LEVEL_HIGH 4 | |
56 | #define FDT_GIC_SHARED 0 | |
57 | #define FDT_GIC_LOCAL 1 | |
58 | #define FDT_BOSTON_CLK_SYS 1 | |
59 | #define FDT_BOSTON_CLK_CPU 2 | |
60 | #define FDT_PCI_IRQ_MAP_PINS 4 | |
61 | #define FDT_PCI_IRQ_MAP_DESCS 6 | |
62 | ||
db1015e9 | 63 | struct BostonState { |
df1d8a1f PB |
64 | SysBusDevice parent_obj; |
65 | ||
66 | MachineState *mach; | |
2d5fac80 | 67 | MIPSCPSState cps; |
490a9d9b | 68 | SerialMM *uart; |
6b290b41 | 69 | Clock *cpuclk; |
df1d8a1f PB |
70 | |
71 | CharBackend lcd_display; | |
72 | char lcd_content[8]; | |
73 | bool lcd_inited; | |
74 | ||
75 | hwaddr kernel_entry; | |
76 | hwaddr fdt_base; | |
db1015e9 | 77 | }; |
df1d8a1f | 78 | |
e07f3e26 JY |
79 | enum { |
80 | BOSTON_LOWDDR, | |
81 | BOSTON_PCIE0, | |
82 | BOSTON_PCIE1, | |
83 | BOSTON_PCIE2, | |
84 | BOSTON_PCIE2_MMIO, | |
85 | BOSTON_CM, | |
86 | BOSTON_GIC, | |
87 | BOSTON_CDMM, | |
88 | BOSTON_CPC, | |
89 | BOSTON_PLATREG, | |
90 | BOSTON_UART, | |
91 | BOSTON_LCD, | |
92 | BOSTON_FLASH, | |
93 | BOSTON_PCIE1_MMIO, | |
94 | BOSTON_PCIE0_MMIO, | |
95 | BOSTON_HIGHDDR, | |
96 | }; | |
97 | ||
98 | static const MemMapEntry boston_memmap[] = { | |
99 | [BOSTON_LOWDDR] = { 0x0, 0x10000000 }, | |
100 | [BOSTON_PCIE0] = { 0x10000000, 0x2000000 }, | |
101 | [BOSTON_PCIE1] = { 0x12000000, 0x2000000 }, | |
102 | [BOSTON_PCIE2] = { 0x14000000, 0x2000000 }, | |
103 | [BOSTON_PCIE2_MMIO] = { 0x16000000, 0x100000 }, | |
104 | [BOSTON_CM] = { 0x16100000, 0x20000 }, | |
105 | [BOSTON_GIC] = { 0x16120000, 0x20000 }, | |
106 | [BOSTON_CDMM] = { 0x16140000, 0x8000 }, | |
107 | [BOSTON_CPC] = { 0x16200000, 0x8000 }, | |
108 | [BOSTON_PLATREG] = { 0x17ffd000, 0x1000 }, | |
109 | [BOSTON_UART] = { 0x17ffe000, 0x20 }, | |
110 | [BOSTON_LCD] = { 0x17fff000, 0x8 }, | |
111 | [BOSTON_FLASH] = { 0x18000000, 0x8000000 }, | |
112 | [BOSTON_PCIE1_MMIO] = { 0x20000000, 0x20000000 }, | |
113 | [BOSTON_PCIE0_MMIO] = { 0x40000000, 0x40000000 }, | |
114 | [BOSTON_HIGHDDR] = { 0x80000000, 0x0 }, | |
115 | }; | |
116 | ||
df1d8a1f PB |
117 | enum boston_plat_reg { |
118 | PLAT_FPGA_BUILD = 0x00, | |
119 | PLAT_CORE_CL = 0x04, | |
120 | PLAT_WRAPPER_CL = 0x08, | |
121 | PLAT_SYSCLK_STATUS = 0x0c, | |
122 | PLAT_SOFTRST_CTL = 0x10, | |
123 | #define PLAT_SOFTRST_CTL_SYSRESET (1 << 4) | |
124 | PLAT_DDR3_STATUS = 0x14, | |
125 | #define PLAT_DDR3_STATUS_LOCKED (1 << 0) | |
126 | #define PLAT_DDR3_STATUS_CALIBRATED (1 << 2) | |
127 | PLAT_PCIE_STATUS = 0x18, | |
128 | #define PLAT_PCIE_STATUS_PCIE0_LOCKED (1 << 0) | |
129 | #define PLAT_PCIE_STATUS_PCIE1_LOCKED (1 << 8) | |
130 | #define PLAT_PCIE_STATUS_PCIE2_LOCKED (1 << 16) | |
131 | PLAT_FLASH_CTL = 0x1c, | |
132 | PLAT_SPARE0 = 0x20, | |
133 | PLAT_SPARE1 = 0x24, | |
134 | PLAT_SPARE2 = 0x28, | |
135 | PLAT_SPARE3 = 0x2c, | |
136 | PLAT_MMCM_DIV = 0x30, | |
137 | #define PLAT_MMCM_DIV_CLK0DIV_SHIFT 0 | |
138 | #define PLAT_MMCM_DIV_INPUT_SHIFT 8 | |
139 | #define PLAT_MMCM_DIV_MUL_SHIFT 16 | |
140 | #define PLAT_MMCM_DIV_CLK1DIV_SHIFT 24 | |
141 | PLAT_BUILD_CFG = 0x34, | |
142 | #define PLAT_BUILD_CFG_IOCU_EN (1 << 0) | |
143 | #define PLAT_BUILD_CFG_PCIE0_EN (1 << 1) | |
144 | #define PLAT_BUILD_CFG_PCIE1_EN (1 << 2) | |
145 | #define PLAT_BUILD_CFG_PCIE2_EN (1 << 3) | |
146 | PLAT_DDR_CFG = 0x38, | |
147 | #define PLAT_DDR_CFG_SIZE (0xf << 0) | |
148 | #define PLAT_DDR_CFG_MHZ (0xfff << 4) | |
149 | PLAT_NOC_PCIE0_ADDR = 0x3c, | |
150 | PLAT_NOC_PCIE1_ADDR = 0x40, | |
151 | PLAT_NOC_PCIE2_ADDR = 0x44, | |
152 | PLAT_SYS_CTL = 0x48, | |
153 | }; | |
154 | ||
083b266f | 155 | static void boston_lcd_event(void *opaque, QEMUChrEvent event) |
df1d8a1f PB |
156 | { |
157 | BostonState *s = opaque; | |
158 | if (event == CHR_EVENT_OPENED && !s->lcd_inited) { | |
159 | qemu_chr_fe_printf(&s->lcd_display, " "); | |
160 | s->lcd_inited = true; | |
161 | } | |
162 | } | |
163 | ||
164 | static uint64_t boston_lcd_read(void *opaque, hwaddr addr, | |
165 | unsigned size) | |
166 | { | |
167 | BostonState *s = opaque; | |
168 | uint64_t val = 0; | |
169 | ||
170 | switch (size) { | |
171 | case 8: | |
172 | val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56; | |
173 | val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48; | |
174 | val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40; | |
175 | val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32; | |
176 | /* fall through */ | |
177 | case 4: | |
178 | val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24; | |
179 | val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16; | |
180 | /* fall through */ | |
181 | case 2: | |
182 | val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8; | |
183 | /* fall through */ | |
184 | case 1: | |
185 | val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7]; | |
186 | break; | |
187 | } | |
188 | ||
189 | return val; | |
190 | } | |
191 | ||
192 | static void boston_lcd_write(void *opaque, hwaddr addr, | |
193 | uint64_t val, unsigned size) | |
194 | { | |
195 | BostonState *s = opaque; | |
196 | ||
197 | switch (size) { | |
198 | case 8: | |
199 | s->lcd_content[(addr + 7) & 0x7] = val >> 56; | |
200 | s->lcd_content[(addr + 6) & 0x7] = val >> 48; | |
201 | s->lcd_content[(addr + 5) & 0x7] = val >> 40; | |
202 | s->lcd_content[(addr + 4) & 0x7] = val >> 32; | |
203 | /* fall through */ | |
204 | case 4: | |
205 | s->lcd_content[(addr + 3) & 0x7] = val >> 24; | |
206 | s->lcd_content[(addr + 2) & 0x7] = val >> 16; | |
207 | /* fall through */ | |
208 | case 2: | |
209 | s->lcd_content[(addr + 1) & 0x7] = val >> 8; | |
210 | /* fall through */ | |
211 | case 1: | |
212 | s->lcd_content[(addr + 0) & 0x7] = val; | |
213 | break; | |
214 | } | |
215 | ||
216 | qemu_chr_fe_printf(&s->lcd_display, | |
217 | "\r%-8.8s", s->lcd_content); | |
218 | } | |
219 | ||
220 | static const MemoryRegionOps boston_lcd_ops = { | |
221 | .read = boston_lcd_read, | |
222 | .write = boston_lcd_write, | |
223 | .endianness = DEVICE_NATIVE_ENDIAN, | |
224 | }; | |
225 | ||
226 | static uint64_t boston_platreg_read(void *opaque, hwaddr addr, | |
227 | unsigned size) | |
228 | { | |
229 | BostonState *s = opaque; | |
230 | uint32_t gic_freq, val; | |
231 | ||
232 | if (size != 4) { | |
c4c98835 | 233 | qemu_log_mask(LOG_UNIMP, "%uB platform register read\n", size); |
df1d8a1f PB |
234 | return 0; |
235 | } | |
236 | ||
237 | switch (addr & 0xffff) { | |
238 | case PLAT_FPGA_BUILD: | |
239 | case PLAT_CORE_CL: | |
240 | case PLAT_WRAPPER_CL: | |
241 | return 0; | |
242 | case PLAT_DDR3_STATUS: | |
243 | return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED; | |
244 | case PLAT_MMCM_DIV: | |
2d5fac80 | 245 | gic_freq = mips_gictimer_get_freq(s->cps.gic.gic_timer) / 1000000; |
df1d8a1f PB |
246 | val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT; |
247 | val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT; | |
248 | val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT; | |
249 | val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT; | |
250 | return val; | |
251 | case PLAT_BUILD_CFG: | |
252 | val = PLAT_BUILD_CFG_PCIE0_EN; | |
253 | val |= PLAT_BUILD_CFG_PCIE1_EN; | |
254 | val |= PLAT_BUILD_CFG_PCIE2_EN; | |
255 | return val; | |
256 | case PLAT_DDR_CFG: | |
d23b6caa | 257 | val = s->mach->ram_size / GiB; |
df1d8a1f PB |
258 | assert(!(val & ~PLAT_DDR_CFG_SIZE)); |
259 | val |= PLAT_DDR_CFG_MHZ; | |
260 | return val; | |
261 | default: | |
c4c98835 | 262 | qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx "\n", |
df1d8a1f PB |
263 | addr & 0xffff); |
264 | return 0; | |
265 | } | |
266 | } | |
267 | ||
268 | static void boston_platreg_write(void *opaque, hwaddr addr, | |
269 | uint64_t val, unsigned size) | |
270 | { | |
271 | if (size != 4) { | |
c4c98835 | 272 | qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", size); |
df1d8a1f PB |
273 | return; |
274 | } | |
275 | ||
276 | switch (addr & 0xffff) { | |
277 | case PLAT_FPGA_BUILD: | |
278 | case PLAT_CORE_CL: | |
279 | case PLAT_WRAPPER_CL: | |
280 | case PLAT_DDR3_STATUS: | |
281 | case PLAT_PCIE_STATUS: | |
282 | case PLAT_MMCM_DIV: | |
283 | case PLAT_BUILD_CFG: | |
284 | case PLAT_DDR_CFG: | |
285 | /* read only */ | |
286 | break; | |
287 | case PLAT_SOFTRST_CTL: | |
288 | if (val & PLAT_SOFTRST_CTL_SYSRESET) { | |
cf83f140 | 289 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
df1d8a1f PB |
290 | } |
291 | break; | |
292 | default: | |
293 | qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx | |
c4c98835 | 294 | " = 0x%" PRIx64 "\n", addr & 0xffff, val); |
df1d8a1f PB |
295 | break; |
296 | } | |
297 | } | |
298 | ||
299 | static const MemoryRegionOps boston_platreg_ops = { | |
300 | .read = boston_platreg_read, | |
301 | .write = boston_platreg_write, | |
302 | .endianness = DEVICE_NATIVE_ENDIAN, | |
303 | }; | |
304 | ||
6b290b41 PMD |
305 | static void mips_boston_instance_init(Object *obj) |
306 | { | |
307 | BostonState *s = BOSTON(obj); | |
308 | ||
309 | s->cpuclk = qdev_init_clock_out(DEVICE(obj), "cpu-refclk"); | |
310 | clock_set_hz(s->cpuclk, 1000000000); /* 1 GHz */ | |
311 | } | |
312 | ||
df1d8a1f | 313 | static const TypeInfo boston_device = { |
27cf0896 | 314 | .name = TYPE_BOSTON, |
df1d8a1f PB |
315 | .parent = TYPE_SYS_BUS_DEVICE, |
316 | .instance_size = sizeof(BostonState), | |
6b290b41 | 317 | .instance_init = mips_boston_instance_init, |
df1d8a1f PB |
318 | }; |
319 | ||
320 | static void boston_register_types(void) | |
321 | { | |
322 | type_register_static(&boston_device); | |
323 | } | |
324 | type_init(boston_register_types) | |
325 | ||
cd5066f8 | 326 | static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) |
df1d8a1f | 327 | { |
e07f3e26 | 328 | uint64_t regaddr; |
df1d8a1f PB |
329 | |
330 | /* Move CM GCRs */ | |
e07f3e26 JY |
331 | regaddr = cpu_mips_phys_to_kseg1(NULL, GCR_BASE_ADDR + GCR_BASE_OFS), |
332 | bl_gen_write_ulong(&p, regaddr, | |
333 | boston_memmap[BOSTON_CM].base); | |
df1d8a1f PB |
334 | |
335 | /* Move & enable GIC GCRs */ | |
e07f3e26 JY |
336 | regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base |
337 | + GCR_GIC_BASE_OFS), | |
338 | bl_gen_write_ulong(&p, regaddr, | |
339 | boston_memmap[BOSTON_GIC].base | GCR_GIC_BASE_GICEN_MSK); | |
df1d8a1f PB |
340 | |
341 | /* Move & enable CPC GCRs */ | |
e07f3e26 JY |
342 | regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base |
343 | + GCR_CPC_BASE_OFS), | |
344 | bl_gen_write_ulong(&p, regaddr, | |
345 | boston_memmap[BOSTON_CPC].base | GCR_CPC_BASE_CPCEN_MSK); | |
df1d8a1f PB |
346 | |
347 | /* | |
348 | * Setup argument registers to follow the UHI boot protocol: | |
349 | * | |
350 | * a0/$4 = -2 | |
351 | * a1/$5 = virtual address of FDT | |
352 | * a2/$6 = 0 | |
353 | * a3/$7 = 0 | |
354 | */ | |
36d7487b PMD |
355 | bl_gen_jump_kernel(&p, |
356 | true, 0, true, (int32_t)-2, | |
357 | true, fdt_addr, true, 0, true, 0, | |
358 | kernel_entry); | |
df1d8a1f PB |
359 | } |
360 | ||
361 | static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, | |
362 | const void *match_data, hwaddr *load_addr) | |
363 | { | |
364 | BostonState *s = BOSTON(opaque); | |
365 | MachineState *machine = s->mach; | |
366 | const char *cmdline; | |
367 | int err; | |
bf4ee88a PM |
368 | size_t ram_low_sz, ram_high_sz; |
369 | size_t fdt_sz = fdt_totalsize(fdt_orig) * 2; | |
370 | g_autofree void *fdt = g_malloc0(fdt_sz); | |
5e19cc68 | 371 | uint8_t rng_seed[32]; |
df1d8a1f PB |
372 | |
373 | err = fdt_open_into(fdt_orig, fdt, fdt_sz); | |
374 | if (err) { | |
375 | fprintf(stderr, "unable to open FDT\n"); | |
376 | return NULL; | |
377 | } | |
378 | ||
5e19cc68 JD |
379 | qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); |
380 | qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); | |
381 | ||
df1d8a1f PB |
382 | cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0]) |
383 | ? machine->kernel_cmdline : " "; | |
384 | err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); | |
385 | if (err < 0) { | |
386 | fprintf(stderr, "couldn't set /chosen/bootargs\n"); | |
387 | return NULL; | |
388 | } | |
389 | ||
d23b6caa | 390 | ram_low_sz = MIN(256 * MiB, machine->ram_size); |
df1d8a1f PB |
391 | ram_high_sz = machine->ram_size - ram_low_sz; |
392 | qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg", | |
e07f3e26 JY |
393 | 1, boston_memmap[BOSTON_LOWDDR].base, 1, ram_low_sz, |
394 | 1, boston_memmap[BOSTON_HIGHDDR].base + ram_low_sz, | |
395 | 1, ram_high_sz); | |
df1d8a1f PB |
396 | |
397 | fdt = g_realloc(fdt, fdt_totalsize(fdt)); | |
398 | qemu_fdt_dumpdtb(fdt, fdt_sz); | |
399 | ||
400 | s->fdt_base = *load_addr; | |
401 | ||
bf4ee88a | 402 | return g_steal_pointer(&fdt); |
df1d8a1f PB |
403 | } |
404 | ||
405 | static const void *boston_kernel_filter(void *opaque, const void *kernel, | |
406 | hwaddr *load_addr, hwaddr *entry_addr) | |
407 | { | |
408 | BostonState *s = BOSTON(opaque); | |
409 | ||
410 | s->kernel_entry = *entry_addr; | |
411 | ||
412 | return kernel; | |
413 | } | |
414 | ||
415 | static const struct fit_loader_match boston_matches[] = { | |
416 | { "img,boston" }, | |
417 | { NULL }, | |
418 | }; | |
419 | ||
420 | static const struct fit_loader boston_fit_loader = { | |
421 | .matches = boston_matches, | |
422 | .addr_to_phys = cpu_mips_kseg0_to_phys, | |
423 | .fdt_filter = boston_fdt_filter, | |
424 | .kernel_filter = boston_kernel_filter, | |
425 | }; | |
426 | ||
427 | static inline XilinxPCIEHost * | |
428 | xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, | |
429 | hwaddr cfg_base, uint64_t cfg_size, | |
430 | hwaddr mmio_base, uint64_t mmio_size, | |
3c43fc33 | 431 | qemu_irq irq) |
df1d8a1f PB |
432 | { |
433 | DeviceState *dev; | |
434 | MemoryRegion *cfg, *mmio; | |
435 | ||
3e80f690 | 436 | dev = qdev_new(TYPE_XILINX_PCIE_HOST); |
df1d8a1f PB |
437 | |
438 | qdev_prop_set_uint32(dev, "bus_nr", bus_nr); | |
439 | qdev_prop_set_uint64(dev, "cfg_base", cfg_base); | |
440 | qdev_prop_set_uint64(dev, "cfg_size", cfg_size); | |
441 | qdev_prop_set_uint64(dev, "mmio_base", mmio_base); | |
442 | qdev_prop_set_uint64(dev, "mmio_size", mmio_size); | |
df1d8a1f | 443 | |
3c6ef471 | 444 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
df1d8a1f PB |
445 | |
446 | cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); | |
447 | memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0); | |
448 | ||
449 | mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); | |
450 | memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0); | |
451 | ||
452 | qdev_connect_gpio_out_named(dev, "interrupt_out", 0, irq); | |
453 | ||
454 | return XILINX_PCIE_HOST(dev); | |
455 | } | |
456 | ||
72303899 JY |
457 | |
458 | static void fdt_create_pcie(void *fdt, int gic_ph, int irq, hwaddr reg_base, | |
459 | hwaddr reg_size, hwaddr mmio_base, hwaddr mmio_size) | |
460 | { | |
461 | int i; | |
462 | char *name, *intc_name; | |
463 | uint32_t intc_ph; | |
464 | uint32_t interrupt_map[FDT_PCI_IRQ_MAP_PINS][FDT_PCI_IRQ_MAP_DESCS]; | |
465 | ||
466 | intc_ph = qemu_fdt_alloc_phandle(fdt); | |
467 | name = g_strdup_printf("/soc/pci@%" HWADDR_PRIx, reg_base); | |
468 | qemu_fdt_add_subnode(fdt, name); | |
469 | qemu_fdt_setprop_string(fdt, name, "compatible", | |
470 | "xlnx,axi-pcie-host-1.00.a"); | |
471 | qemu_fdt_setprop_string(fdt, name, "device_type", "pci"); | |
472 | qemu_fdt_setprop_cells(fdt, name, "reg", reg_base, reg_size); | |
473 | ||
474 | qemu_fdt_setprop_cell(fdt, name, "#address-cells", 3); | |
475 | qemu_fdt_setprop_cell(fdt, name, "#size-cells", 2); | |
476 | qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 1); | |
477 | ||
478 | qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph); | |
479 | qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, irq, | |
480 | FDT_IRQ_TYPE_LEVEL_HIGH); | |
481 | ||
482 | qemu_fdt_setprop_cells(fdt, name, "ranges", 0x02000000, 0, mmio_base, | |
483 | mmio_base, 0, mmio_size); | |
484 | qemu_fdt_setprop_cells(fdt, name, "bus-range", 0x00, 0xff); | |
485 | ||
486 | ||
487 | ||
488 | intc_name = g_strdup_printf("%s/interrupt-controller", name); | |
489 | qemu_fdt_add_subnode(fdt, intc_name); | |
490 | qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); | |
491 | qemu_fdt_setprop_cell(fdt, intc_name, "#address-cells", 0); | |
492 | qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); | |
493 | qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_ph); | |
494 | ||
495 | qemu_fdt_setprop_cells(fdt, name, "interrupt-map-mask", 0, 0, 0, 7); | |
496 | for (i = 0; i < FDT_PCI_IRQ_MAP_PINS; i++) { | |
497 | uint32_t *irqmap = interrupt_map[i]; | |
498 | ||
499 | irqmap[0] = cpu_to_be32(0); | |
500 | irqmap[1] = cpu_to_be32(0); | |
501 | irqmap[2] = cpu_to_be32(0); | |
502 | irqmap[3] = cpu_to_be32(i + 1); | |
503 | irqmap[4] = cpu_to_be32(intc_ph); | |
504 | irqmap[5] = cpu_to_be32(i + 1); | |
505 | } | |
506 | qemu_fdt_setprop(fdt, name, "interrupt-map", | |
507 | &interrupt_map, sizeof(interrupt_map)); | |
508 | ||
509 | g_free(intc_name); | |
510 | g_free(name); | |
511 | } | |
512 | ||
513 | static const void *create_fdt(BostonState *s, | |
514 | const MemMapEntry *memmap, int *dt_size) | |
515 | { | |
516 | void *fdt; | |
517 | int cpu; | |
02633461 | 518 | MachineState *ms = s->mach; |
72303899 JY |
519 | uint32_t platreg_ph, gic_ph, clk_ph; |
520 | char *name, *gic_name, *platreg_name, *stdout_name; | |
521 | static const char * const syscon_compat[2] = { | |
522 | "img,boston-platform-regs", "syscon" | |
523 | }; | |
524 | ||
525 | fdt = create_device_tree(dt_size); | |
526 | if (!fdt) { | |
527 | error_report("create_device_tree() failed"); | |
528 | exit(1); | |
529 | } | |
530 | ||
531 | platreg_ph = qemu_fdt_alloc_phandle(fdt); | |
532 | gic_ph = qemu_fdt_alloc_phandle(fdt); | |
533 | clk_ph = qemu_fdt_alloc_phandle(fdt); | |
534 | ||
535 | qemu_fdt_setprop_string(fdt, "/", "model", "img,boston"); | |
536 | qemu_fdt_setprop_string(fdt, "/", "compatible", "img,boston"); | |
537 | qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); | |
538 | qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1); | |
539 | ||
540 | ||
541 | qemu_fdt_add_subnode(fdt, "/cpus"); | |
542 | qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); | |
543 | qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); | |
544 | ||
02633461 | 545 | for (cpu = 0; cpu < ms->smp.cpus; cpu++) { |
72303899 JY |
546 | name = g_strdup_printf("/cpus/cpu@%d", cpu); |
547 | qemu_fdt_add_subnode(fdt, name); | |
548 | qemu_fdt_setprop_string(fdt, name, "compatible", "img,mips"); | |
549 | qemu_fdt_setprop_string(fdt, name, "status", "okay"); | |
550 | qemu_fdt_setprop_cell(fdt, name, "reg", cpu); | |
551 | qemu_fdt_setprop_string(fdt, name, "device_type", "cpu"); | |
552 | qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU); | |
553 | g_free(name); | |
554 | } | |
555 | ||
556 | qemu_fdt_add_subnode(fdt, "/soc"); | |
557 | qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); | |
558 | qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); | |
559 | qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1); | |
560 | qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x1); | |
561 | ||
562 | fdt_create_pcie(fdt, gic_ph, 2, | |
563 | memmap[BOSTON_PCIE0].base, memmap[BOSTON_PCIE0].size, | |
564 | memmap[BOSTON_PCIE0_MMIO].base, memmap[BOSTON_PCIE0_MMIO].size); | |
565 | ||
566 | fdt_create_pcie(fdt, gic_ph, 1, | |
567 | memmap[BOSTON_PCIE1].base, memmap[BOSTON_PCIE1].size, | |
568 | memmap[BOSTON_PCIE1_MMIO].base, memmap[BOSTON_PCIE1_MMIO].size); | |
569 | ||
570 | fdt_create_pcie(fdt, gic_ph, 0, | |
571 | memmap[BOSTON_PCIE2].base, memmap[BOSTON_PCIE2].size, | |
572 | memmap[BOSTON_PCIE2_MMIO].base, memmap[BOSTON_PCIE2_MMIO].size); | |
573 | ||
574 | /* GIC with it's timer node */ | |
575 | gic_name = g_strdup_printf("/soc/interrupt-controller@%" HWADDR_PRIx, | |
576 | memmap[BOSTON_GIC].base); | |
577 | qemu_fdt_add_subnode(fdt, gic_name); | |
578 | qemu_fdt_setprop_string(fdt, gic_name, "compatible", "mti,gic"); | |
579 | qemu_fdt_setprop_cells(fdt, gic_name, "reg", memmap[BOSTON_GIC].base, | |
580 | memmap[BOSTON_GIC].size); | |
581 | qemu_fdt_setprop(fdt, gic_name, "interrupt-controller", NULL, 0); | |
582 | qemu_fdt_setprop_cell(fdt, gic_name, "#interrupt-cells", 3); | |
583 | qemu_fdt_setprop_cell(fdt, gic_name, "phandle", gic_ph); | |
584 | ||
585 | name = g_strdup_printf("%s/timer", gic_name); | |
586 | qemu_fdt_add_subnode(fdt, name); | |
587 | qemu_fdt_setprop_string(fdt, name, "compatible", "mti,gic-timer"); | |
588 | qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_LOCAL, 1, | |
589 | FDT_IRQ_TYPE_NONE); | |
590 | qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU); | |
591 | g_free(name); | |
592 | g_free(gic_name); | |
593 | ||
594 | /* CDMM node */ | |
595 | name = g_strdup_printf("/soc/cdmm@%" HWADDR_PRIx, memmap[BOSTON_CDMM].base); | |
596 | qemu_fdt_add_subnode(fdt, name); | |
597 | qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cdmm"); | |
598 | qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CDMM].base, | |
599 | memmap[BOSTON_CDMM].size); | |
600 | g_free(name); | |
601 | ||
602 | /* CPC node */ | |
603 | name = g_strdup_printf("/soc/cpc@%" HWADDR_PRIx, memmap[BOSTON_CPC].base); | |
604 | qemu_fdt_add_subnode(fdt, name); | |
605 | qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cpc"); | |
606 | qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CPC].base, | |
607 | memmap[BOSTON_CPC].size); | |
608 | g_free(name); | |
609 | ||
610 | /* platreg and it's clk node */ | |
611 | platreg_name = g_strdup_printf("/soc/system-controller@%" HWADDR_PRIx, | |
612 | memmap[BOSTON_PLATREG].base); | |
613 | qemu_fdt_add_subnode(fdt, platreg_name); | |
614 | qemu_fdt_setprop_string_array(fdt, platreg_name, "compatible", | |
615 | (char **)&syscon_compat, | |
616 | ARRAY_SIZE(syscon_compat)); | |
617 | qemu_fdt_setprop_cells(fdt, platreg_name, "reg", | |
618 | memmap[BOSTON_PLATREG].base, | |
619 | memmap[BOSTON_PLATREG].size); | |
620 | qemu_fdt_setprop_cell(fdt, platreg_name, "phandle", platreg_ph); | |
621 | ||
622 | name = g_strdup_printf("%s/clock", platreg_name); | |
623 | qemu_fdt_add_subnode(fdt, name); | |
624 | qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-clock"); | |
625 | qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 1); | |
626 | qemu_fdt_setprop_cell(fdt, name, "phandle", clk_ph); | |
627 | g_free(name); | |
628 | g_free(platreg_name); | |
629 | ||
630 | /* reboot node */ | |
631 | name = g_strdup_printf("/soc/reboot"); | |
632 | qemu_fdt_add_subnode(fdt, name); | |
633 | qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-reboot"); | |
634 | qemu_fdt_setprop_cell(fdt, name, "regmap", platreg_ph); | |
635 | qemu_fdt_setprop_cell(fdt, name, "offset", 0x10); | |
636 | qemu_fdt_setprop_cell(fdt, name, "mask", 0x10); | |
637 | g_free(name); | |
638 | ||
639 | /* uart node */ | |
640 | name = g_strdup_printf("/soc/uart@%" HWADDR_PRIx, memmap[BOSTON_UART].base); | |
641 | qemu_fdt_add_subnode(fdt, name); | |
642 | qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a"); | |
643 | qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_UART].base, | |
644 | memmap[BOSTON_UART].size); | |
645 | qemu_fdt_setprop_cell(fdt, name, "reg-shift", 0x2); | |
646 | qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph); | |
647 | qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, 3, | |
648 | FDT_IRQ_TYPE_LEVEL_HIGH); | |
649 | qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_SYS); | |
650 | ||
651 | qemu_fdt_add_subnode(fdt, "/chosen"); | |
652 | stdout_name = g_strdup_printf("%s:115200", name); | |
653 | qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", stdout_name); | |
654 | g_free(stdout_name); | |
655 | g_free(name); | |
656 | ||
657 | /* lcd node */ | |
658 | name = g_strdup_printf("/soc/lcd@%" HWADDR_PRIx, memmap[BOSTON_LCD].base); | |
659 | qemu_fdt_add_subnode(fdt, name); | |
660 | qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-lcd"); | |
661 | qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_LCD].base, | |
662 | memmap[BOSTON_LCD].size); | |
663 | g_free(name); | |
664 | ||
665 | name = g_strdup_printf("/memory@0"); | |
666 | qemu_fdt_add_subnode(fdt, name); | |
667 | qemu_fdt_setprop_string(fdt, name, "device_type", "memory"); | |
668 | g_free(name); | |
669 | ||
670 | return fdt; | |
671 | } | |
672 | ||
df1d8a1f PB |
673 | static void boston_mach_init(MachineState *machine) |
674 | { | |
675 | DeviceState *dev; | |
676 | BostonState *s; | |
9389d6ce | 677 | MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg; |
df1d8a1f PB |
678 | MemoryRegion *sys_mem = get_system_memory(); |
679 | XilinxPCIEHost *pcie2; | |
41c05b41 | 680 | PCIDevice *pdev; |
e6097f18 | 681 | AHCIPCIState *ich9; |
df1d8a1f PB |
682 | DriveInfo *hd[6]; |
683 | Chardev *chr; | |
684 | int fw_size, fit_err; | |
df1d8a1f | 685 | |
d23b6caa PMD |
686 | if ((machine->ram_size % GiB) || |
687 | (machine->ram_size > (2 * GiB))) { | |
df1d8a1f PB |
688 | error_report("Memory size must be 1GB or 2GB"); |
689 | exit(1); | |
690 | } | |
691 | ||
27cf0896 | 692 | dev = qdev_new(TYPE_BOSTON); |
3c6ef471 | 693 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
df1d8a1f PB |
694 | |
695 | s = BOSTON(dev); | |
696 | s->mach = machine; | |
df1d8a1f | 697 | |
ac70f976 | 698 | if (!cpu_type_supports_cps_smp(machine->cpu_type)) { |
df1d8a1f PB |
699 | error_report("Boston requires CPUs which support CPS"); |
700 | exit(1); | |
701 | } | |
702 | ||
0074fce6 | 703 | object_initialize_child(OBJECT(machine), "cps", &s->cps, TYPE_MIPS_CPS); |
5325cc34 | 704 | object_property_set_str(OBJECT(&s->cps), "cpu-type", machine->cpu_type, |
932d3a65 | 705 | &error_fatal); |
10997f2d | 706 | object_property_set_uint(OBJECT(&s->cps), "num-vp", machine->smp.cpus, |
932d3a65 | 707 | &error_fatal); |
6b290b41 PMD |
708 | qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", |
709 | qdev_get_clock_out(dev, "cpu-refclk")); | |
0074fce6 | 710 | sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal); |
df1d8a1f | 711 | |
2d5fac80 | 712 | sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1); |
df1d8a1f PB |
713 | |
714 | flash = g_new(MemoryRegion, 1); | |
e07f3e26 JY |
715 | memory_region_init_rom(flash, NULL, "boston.flash", |
716 | boston_memmap[BOSTON_FLASH].size, &error_fatal); | |
717 | memory_region_add_subregion_overlap(sys_mem, | |
718 | boston_memmap[BOSTON_FLASH].base, | |
719 | flash, 0); | |
df1d8a1f | 720 | |
e07f3e26 JY |
721 | memory_region_add_subregion_overlap(sys_mem, |
722 | boston_memmap[BOSTON_HIGHDDR].base, | |
723 | machine->ram, 0); | |
df1d8a1f PB |
724 | |
725 | ddr_low_alias = g_new(MemoryRegion, 1); | |
726 | memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", | |
9389d6ce IM |
727 | machine->ram, 0, |
728 | MIN(machine->ram_size, (256 * MiB))); | |
df1d8a1f PB |
729 | memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); |
730 | ||
731 | xilinx_pcie_init(sys_mem, 0, | |
e07f3e26 JY |
732 | boston_memmap[BOSTON_PCIE0].base, |
733 | boston_memmap[BOSTON_PCIE0].size, | |
734 | boston_memmap[BOSTON_PCIE0_MMIO].base, | |
735 | boston_memmap[BOSTON_PCIE0_MMIO].size, | |
3c43fc33 | 736 | get_cps_irq(&s->cps, 2)); |
df1d8a1f PB |
737 | |
738 | xilinx_pcie_init(sys_mem, 1, | |
e07f3e26 JY |
739 | boston_memmap[BOSTON_PCIE1].base, |
740 | boston_memmap[BOSTON_PCIE1].size, | |
741 | boston_memmap[BOSTON_PCIE1_MMIO].base, | |
742 | boston_memmap[BOSTON_PCIE1_MMIO].size, | |
3c43fc33 | 743 | get_cps_irq(&s->cps, 1)); |
df1d8a1f PB |
744 | |
745 | pcie2 = xilinx_pcie_init(sys_mem, 2, | |
e07f3e26 JY |
746 | boston_memmap[BOSTON_PCIE2].base, |
747 | boston_memmap[BOSTON_PCIE2].size, | |
748 | boston_memmap[BOSTON_PCIE2_MMIO].base, | |
749 | boston_memmap[BOSTON_PCIE2_MMIO].size, | |
3c43fc33 | 750 | get_cps_irq(&s->cps, 0)); |
df1d8a1f PB |
751 | |
752 | platreg = g_new(MemoryRegion, 1); | |
753 | memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, | |
e07f3e26 JY |
754 | "boston-platregs", |
755 | boston_memmap[BOSTON_PLATREG].size); | |
756 | memory_region_add_subregion_overlap(sys_mem, | |
757 | boston_memmap[BOSTON_PLATREG].base, platreg, 0); | |
df1d8a1f | 758 | |
e07f3e26 | 759 | s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2, |
2d5fac80 | 760 | get_cps_irq(&s->cps, 3), 10000000, |
9bca0edb | 761 | serial_hd(0), DEVICE_NATIVE_ENDIAN); |
df1d8a1f PB |
762 | |
763 | lcd = g_new(MemoryRegion, 1); | |
764 | memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); | |
e07f3e26 JY |
765 | memory_region_add_subregion_overlap(sys_mem, |
766 | boston_memmap[BOSTON_LCD].base, lcd, 0); | |
df1d8a1f | 767 | |
4ad6f6cb | 768 | chr = qemu_chr_new("lcd", "vc:320x240", NULL); |
df1d8a1f PB |
769 | qemu_chr_fe_init(&s->lcd_display, chr, NULL); |
770 | qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, | |
81517ba3 | 771 | boston_lcd_event, NULL, s, NULL, true); |
df1d8a1f | 772 | |
41c05b41 | 773 | pdev = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, |
e052944a | 774 | PCI_DEVFN(0, 0), TYPE_ICH9_AHCI); |
e6097f18 PMD |
775 | ich9 = ICH9_AHCI(pdev); |
776 | g_assert(ARRAY_SIZE(hd) == ich9->ahci.ports); | |
777 | ide_drive_get(hd, ich9->ahci.ports); | |
e2f8d280 | 778 | ahci_ide_create_devs(&ich9->ahci, hd); |
df1d8a1f PB |
779 | |
780 | if (machine->firmware) { | |
781 | fw_size = load_image_targphys(machine->firmware, | |
d23b6caa | 782 | 0x1fc00000, 4 * MiB); |
df1d8a1f | 783 | if (fw_size == -1) { |
036a2604 | 784 | error_report("unable to load firmware image '%s'", |
df1d8a1f PB |
785 | machine->firmware); |
786 | exit(1); | |
787 | } | |
788 | } else if (machine->kernel_filename) { | |
d77c462b JY |
789 | uint64_t kernel_entry, kernel_high; |
790 | ssize_t kernel_size; | |
10e3f30f JY |
791 | |
792 | kernel_size = load_elf(machine->kernel_filename, NULL, | |
793 | cpu_mips_kseg0_to_phys, NULL, | |
794 | &kernel_entry, NULL, &kernel_high, | |
795 | NULL, 0, EM_MIPS, 1, 0); | |
796 | ||
d77c462b | 797 | if (kernel_size > 0) { |
72303899 | 798 | int dt_size; |
b1f66fab BB |
799 | g_autofree const void *dtb_file_data = NULL; |
800 | g_autofree const void *dtb_load_data = NULL; | |
10e3f30f JY |
801 | hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); |
802 | hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); | |
803 | ||
804 | s->kernel_entry = kernel_entry; | |
805 | if (machine->dtb) { | |
10e3f30f | 806 | dtb_file_data = load_device_tree(machine->dtb, &dt_size); |
72303899 JY |
807 | } else { |
808 | dtb_file_data = create_fdt(s, boston_memmap, &dt_size); | |
10e3f30f | 809 | } |
72303899 JY |
810 | |
811 | dtb_load_data = boston_fdt_filter(s, dtb_file_data, | |
812 | NULL, &dtb_vaddr); | |
813 | ||
814 | /* Calculate real fdt size after filter */ | |
815 | dt_size = fdt_totalsize(dtb_load_data); | |
816 | rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); | |
4fbae244 JD |
817 | qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, |
818 | rom_ptr(dtb_paddr, dt_size)); | |
10e3f30f JY |
819 | } else { |
820 | /* Try to load file as FIT */ | |
821 | fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); | |
822 | if (fit_err) { | |
823 | error_report("unable to load kernel image"); | |
824 | exit(1); | |
825 | } | |
df1d8a1f PB |
826 | } |
827 | ||
828 | gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000, | |
283eae17 | 829 | s->kernel_entry, s->fdt_base); |
df1d8a1f | 830 | } else if (!qtest_enabled()) { |
036a2604 | 831 | error_report("Please provide either a -kernel or -bios argument"); |
df1d8a1f PB |
832 | exit(1); |
833 | } | |
834 | } | |
835 | ||
836 | static void boston_mach_class_init(MachineClass *mc) | |
837 | { | |
838 | mc->desc = "MIPS Boston"; | |
839 | mc->init = boston_mach_init; | |
840 | mc->block_default_type = IF_IDE; | |
d23b6caa | 841 | mc->default_ram_size = 1 * GiB; |
9389d6ce | 842 | mc->default_ram_id = "boston.ddr"; |
df1d8a1f | 843 | mc->max_cpus = 16; |
a7519f2b | 844 | mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); |
df1d8a1f PB |
845 | } |
846 | ||
847 | DEFINE_MACHINE("boston", boston_mach_class_init) |