| 1 | /* |
| 2 | * MIPS Boston-aia development board emulation. |
| 3 | * |
| 4 | * Copyright (c) 2016 Imagination Technologies |
| 5 | * |
| 6 | * Copyright (c) 2025 MIPS |
| 7 | * |
| 8 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 9 | * |
| 10 | */ |
| 11 | |
| 12 | #include "qemu/osdep.h" |
| 13 | #include "qemu/units.h" |
| 14 | |
| 15 | #include "hw/core/boards.h" |
| 16 | #include "hw/char/serial-mm.h" |
| 17 | #include "hw/ide/pci.h" |
| 18 | #include "hw/ide/ahci-pci.h" |
| 19 | #include "hw/core/loader.h" |
| 20 | #include "hw/riscv/cps.h" |
| 21 | #include "hw/riscv/machines-qom.h" |
| 22 | #include "hw/pci-host/xilinx-pcie.h" |
| 23 | #include "hw/core/qdev-properties.h" |
| 24 | #include "qapi/error.h" |
| 25 | #include "qemu/error-report.h" |
| 26 | #include "qemu/log.h" |
| 27 | #include "chardev/char.h" |
| 28 | #include "system/address-spaces.h" |
| 29 | #include "system/device_tree.h" |
| 30 | #include "system/system.h" |
| 31 | #include "system/qtest.h" |
| 32 | #include "system/runstate.h" |
| 33 | |
| 34 | #include <libfdt.h> |
| 35 | #include "qom/object.h" |
| 36 | |
| 37 | #define TYPE_MIPS_BOSTON_AIA "mips-boston-aia" |
| 38 | typedef struct BostonState BostonState; |
| 39 | DECLARE_INSTANCE_CHECKER(BostonState, BOSTON, |
| 40 | TYPE_MIPS_BOSTON_AIA) |
| 41 | |
| 42 | enum { |
| 43 | BOSTON_PCIE2, |
| 44 | BOSTON_PCIE2_MMIO, |
| 45 | BOSTON_PLATREG, |
| 46 | BOSTON_UART, |
| 47 | BOSTON_LCD, |
| 48 | BOSTON_FLASH, |
| 49 | BOSTON_HIGHDDR, |
| 50 | }; |
| 51 | |
| 52 | static const MemMapEntry boston_memmap[] = { |
| 53 | [BOSTON_PCIE2] = { 0x14000000, 0x2000000 }, |
| 54 | [BOSTON_PCIE2_MMIO] = { 0x16000000, 0x100000 }, |
| 55 | [BOSTON_PLATREG] = { 0x17ffd000, 0x1000 }, |
| 56 | [BOSTON_UART] = { 0x17ffe000, 0x20 }, |
| 57 | [BOSTON_LCD] = { 0x17fff000, 0x8 }, |
| 58 | [BOSTON_FLASH] = { 0x18000000, 0x8000000 }, |
| 59 | [BOSTON_HIGHDDR] = { 0x80000000, 0x0 }, |
| 60 | }; |
| 61 | |
| 62 | /* Interrupt numbers for APLIC. */ |
| 63 | #define UART_INT 4 |
| 64 | #define PCIE2_INT 7 |
| 65 | |
| 66 | struct BostonState { |
| 67 | SysBusDevice parent_obj; |
| 68 | |
| 69 | MachineState *mach; |
| 70 | RISCVCPSState cps; |
| 71 | SerialMM *uart; |
| 72 | |
| 73 | CharFrontend lcd_display; |
| 74 | char lcd_content[8]; |
| 75 | bool lcd_inited; |
| 76 | }; |
| 77 | |
| 78 | enum boston_plat_reg { |
| 79 | PLAT_FPGA_BUILD = 0x00, |
| 80 | PLAT_CORE_CL = 0x04, |
| 81 | PLAT_WRAPPER_CL = 0x08, |
| 82 | PLAT_SYSCLK_STATUS = 0x0c, |
| 83 | PLAT_SOFTRST_CTL = 0x10, |
| 84 | #define PLAT_SOFTRST_CTL_SYSRESET (1 << 4) |
| 85 | PLAT_DDR3_STATUS = 0x14, |
| 86 | #define PLAT_DDR3_STATUS_LOCKED (1 << 0) |
| 87 | #define PLAT_DDR3_STATUS_CALIBRATED (1 << 2) |
| 88 | #define PLAT_DDR3_INTERFACE_RESET (1 << 3) |
| 89 | PLAT_PCIE_STATUS = 0x18, |
| 90 | #define PLAT_PCIE_STATUS_PCIE0_LOCKED (1 << 0) |
| 91 | #define PLAT_PCIE_STATUS_PCIE1_LOCKED (1 << 8) |
| 92 | #define PLAT_PCIE_STATUS_PCIE2_LOCKED (1 << 16) |
| 93 | PLAT_FLASH_CTL = 0x1c, |
| 94 | PLAT_SPARE0 = 0x20, |
| 95 | PLAT_SPARE1 = 0x24, |
| 96 | PLAT_SPARE2 = 0x28, |
| 97 | PLAT_SPARE3 = 0x2c, |
| 98 | PLAT_MMCM_DIV = 0x30, |
| 99 | #define PLAT_MMCM_DIV_CLK0DIV_SHIFT 0 |
| 100 | #define PLAT_MMCM_DIV_INPUT_SHIFT 8 |
| 101 | #define PLAT_MMCM_DIV_MUL_SHIFT 16 |
| 102 | #define PLAT_MMCM_DIV_CLK1DIV_SHIFT 24 |
| 103 | PLAT_BUILD_CFG = 0x34, |
| 104 | #define PLAT_BUILD_CFG_IOCU_EN (1 << 0) |
| 105 | #define PLAT_BUILD_CFG_PCIE0_EN (1 << 1) |
| 106 | #define PLAT_BUILD_CFG_PCIE1_EN (1 << 2) |
| 107 | #define PLAT_BUILD_CFG_PCIE2_EN (1 << 3) |
| 108 | PLAT_DDR_CFG = 0x38, |
| 109 | #define PLAT_DDR_CFG_SIZE (0xf << 0) |
| 110 | #define PLAT_DDR_CFG_MHZ (0xfff << 4) |
| 111 | PLAT_NOC_PCIE0_ADDR = 0x3c, |
| 112 | PLAT_NOC_PCIE1_ADDR = 0x40, |
| 113 | PLAT_NOC_PCIE2_ADDR = 0x44, |
| 114 | PLAT_SYS_CTL = 0x48, |
| 115 | }; |
| 116 | |
| 117 | static void boston_lcd_event(void *opaque, QEMUChrEvent event) |
| 118 | { |
| 119 | BostonState *s = opaque; |
| 120 | if (event == CHR_EVENT_OPENED && !s->lcd_inited) { |
| 121 | qemu_chr_fe_printf(&s->lcd_display, " "); |
| 122 | s->lcd_inited = true; |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | static uint64_t boston_lcd_read(void *opaque, hwaddr addr, |
| 127 | unsigned size) |
| 128 | { |
| 129 | BostonState *s = opaque; |
| 130 | uint64_t val = 0; |
| 131 | |
| 132 | switch (size) { |
| 133 | case 8: |
| 134 | val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56; |
| 135 | val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48; |
| 136 | val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40; |
| 137 | val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32; |
| 138 | /* fall through */ |
| 139 | case 4: |
| 140 | val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24; |
| 141 | val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16; |
| 142 | /* fall through */ |
| 143 | case 2: |
| 144 | val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8; |
| 145 | /* fall through */ |
| 146 | case 1: |
| 147 | val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7]; |
| 148 | break; |
| 149 | } |
| 150 | |
| 151 | return val; |
| 152 | } |
| 153 | |
| 154 | static void boston_lcd_write(void *opaque, hwaddr addr, |
| 155 | uint64_t val, unsigned size) |
| 156 | { |
| 157 | BostonState *s = opaque; |
| 158 | |
| 159 | switch (size) { |
| 160 | case 8: |
| 161 | s->lcd_content[(addr + 7) & 0x7] = val >> 56; |
| 162 | s->lcd_content[(addr + 6) & 0x7] = val >> 48; |
| 163 | s->lcd_content[(addr + 5) & 0x7] = val >> 40; |
| 164 | s->lcd_content[(addr + 4) & 0x7] = val >> 32; |
| 165 | /* fall through */ |
| 166 | case 4: |
| 167 | s->lcd_content[(addr + 3) & 0x7] = val >> 24; |
| 168 | s->lcd_content[(addr + 2) & 0x7] = val >> 16; |
| 169 | /* fall through */ |
| 170 | case 2: |
| 171 | s->lcd_content[(addr + 1) & 0x7] = val >> 8; |
| 172 | /* fall through */ |
| 173 | case 1: |
| 174 | s->lcd_content[(addr + 0) & 0x7] = val; |
| 175 | break; |
| 176 | } |
| 177 | |
| 178 | qemu_chr_fe_printf(&s->lcd_display, |
| 179 | "\r%-8.8s", s->lcd_content); |
| 180 | } |
| 181 | |
| 182 | static const MemoryRegionOps boston_lcd_ops = { |
| 183 | .read = boston_lcd_read, |
| 184 | .write = boston_lcd_write, |
| 185 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 186 | }; |
| 187 | |
| 188 | static uint64_t boston_platreg_read(void *opaque, hwaddr addr, |
| 189 | unsigned size) |
| 190 | { |
| 191 | BostonState *s = opaque; |
| 192 | uint32_t gic_freq, val; |
| 193 | |
| 194 | switch (addr & 0xffff) { |
| 195 | case PLAT_FPGA_BUILD: |
| 196 | case PLAT_CORE_CL: |
| 197 | case PLAT_WRAPPER_CL: |
| 198 | return 0; |
| 199 | case PLAT_DDR3_STATUS: |
| 200 | return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED |
| 201 | | PLAT_DDR3_INTERFACE_RESET; |
| 202 | case PLAT_MMCM_DIV: |
| 203 | gic_freq = 25000000 / 1000000; |
| 204 | val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT; |
| 205 | val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT; |
| 206 | val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT; |
| 207 | val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT; |
| 208 | return val; |
| 209 | case PLAT_BUILD_CFG: |
| 210 | val = PLAT_BUILD_CFG_PCIE0_EN; |
| 211 | val |= PLAT_BUILD_CFG_PCIE1_EN; |
| 212 | val |= PLAT_BUILD_CFG_PCIE2_EN; |
| 213 | return val; |
| 214 | case PLAT_DDR_CFG: |
| 215 | val = s->mach->ram_size / GiB; |
| 216 | assert(!(val & ~PLAT_DDR_CFG_SIZE)); |
| 217 | val |= PLAT_DDR_CFG_MHZ; |
| 218 | return val; |
| 219 | default: |
| 220 | qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx "\n", |
| 221 | addr & 0xffff); |
| 222 | return 0; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | static void boston_platreg_write(void *opaque, hwaddr addr, |
| 227 | uint64_t val, unsigned size) |
| 228 | { |
| 229 | if (size != 4) { |
| 230 | qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", size); |
| 231 | return; |
| 232 | } |
| 233 | |
| 234 | switch (addr & 0xffff) { |
| 235 | case PLAT_FPGA_BUILD: |
| 236 | case PLAT_CORE_CL: |
| 237 | case PLAT_WRAPPER_CL: |
| 238 | case PLAT_DDR3_STATUS: |
| 239 | case PLAT_PCIE_STATUS: |
| 240 | case PLAT_MMCM_DIV: |
| 241 | case PLAT_BUILD_CFG: |
| 242 | case PLAT_DDR_CFG: |
| 243 | /* read only */ |
| 244 | break; |
| 245 | case PLAT_SOFTRST_CTL: |
| 246 | if (val & PLAT_SOFTRST_CTL_SYSRESET) { |
| 247 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 248 | } |
| 249 | break; |
| 250 | default: |
| 251 | qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx |
| 252 | " = 0x%" PRIx64 "\n", addr & 0xffff, val); |
| 253 | break; |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | static const MemoryRegionOps boston_platreg_ops = { |
| 258 | .read = boston_platreg_read, |
| 259 | .write = boston_platreg_write, |
| 260 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 261 | .impl = { |
| 262 | .min_access_size = 4, |
| 263 | .max_access_size = 4, |
| 264 | }, |
| 265 | }; |
| 266 | |
| 267 | static const TypeInfo boston_device = { |
| 268 | .name = TYPE_MIPS_BOSTON_AIA, |
| 269 | .parent = TYPE_SYS_BUS_DEVICE, |
| 270 | .instance_size = sizeof(BostonState), |
| 271 | }; |
| 272 | |
| 273 | static void boston_register_types(void) |
| 274 | { |
| 275 | type_register_static(&boston_device); |
| 276 | } |
| 277 | type_init(boston_register_types) |
| 278 | |
| 279 | #define NUM_INSNS 6 |
| 280 | static void gen_firmware(uint32_t *p) |
| 281 | { |
| 282 | int i; |
| 283 | uint32_t reset_vec[NUM_INSNS] = { |
| 284 | /* CM relocate */ |
| 285 | 0x1fb802b7, /* li t0,0x1fb80000 */ |
| 286 | 0x16100337, /* li t1,0x16100000 */ |
| 287 | 0x0062b423, /* sd t1,8(t0) */ |
| 288 | /* Jump to 0x80000000 */ |
| 289 | 0x00100293, /* li t0,1 */ |
| 290 | 0x01f29293, /* slli t0,t0,1f */ |
| 291 | 0x00028067 /* jr t0 */ |
| 292 | }; |
| 293 | |
| 294 | for (i = 0; i < NUM_INSNS; i++) { |
| 295 | *p++ = reset_vec[i]; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | static inline XilinxPCIEHost * |
| 300 | xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, |
| 301 | hwaddr cfg_base, uint64_t cfg_size, |
| 302 | hwaddr mmio_base, uint64_t mmio_size, |
| 303 | qemu_irq irq) |
| 304 | { |
| 305 | DeviceState *dev; |
| 306 | MemoryRegion *cfg, *mmio; |
| 307 | |
| 308 | dev = qdev_new(TYPE_XILINX_PCIE_HOST); |
| 309 | |
| 310 | qdev_prop_set_uint32(dev, "bus_nr", bus_nr); |
| 311 | qdev_prop_set_uint64(dev, "cfg_base", cfg_base); |
| 312 | qdev_prop_set_uint64(dev, "cfg_size", cfg_size); |
| 313 | qdev_prop_set_uint64(dev, "mmio_base", mmio_base); |
| 314 | qdev_prop_set_uint64(dev, "mmio_size", mmio_size); |
| 315 | |
| 316 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
| 317 | |
| 318 | cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); |
| 319 | memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0); |
| 320 | |
| 321 | mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); |
| 322 | memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0); |
| 323 | |
| 324 | qdev_connect_gpio_out_named(dev, "interrupt_out", 0, irq); |
| 325 | |
| 326 | return XILINX_PCIE_HOST(dev); |
| 327 | } |
| 328 | |
| 329 | static void boston_mach_init(MachineState *machine) |
| 330 | { |
| 331 | DeviceState *dev; |
| 332 | BostonState *s; |
| 333 | MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg; |
| 334 | MemoryRegion *sys_mem = get_system_memory(); |
| 335 | XilinxPCIEHost *pcie2; |
| 336 | PCIDevice *pdev; |
| 337 | AHCIPCIState *ich9; |
| 338 | DriveInfo *hd[6]; |
| 339 | Chardev *chr; |
| 340 | int fw_size; |
| 341 | |
| 342 | if ((machine->ram_size % GiB) || |
| 343 | (machine->ram_size > (4 * GiB))) { |
| 344 | error_report("Memory size must be 1GB, 2GB, 3GB, or 4GB"); |
| 345 | exit(1); |
| 346 | } |
| 347 | |
| 348 | if (machine->smp.cpus / machine->smp.cores / machine->smp.threads > 1) { |
| 349 | error_report( |
| 350 | "Invalid -smp x,cores=y,threads=z. The max number of clusters " |
| 351 | "supported is 1"); |
| 352 | exit(1); |
| 353 | } |
| 354 | |
| 355 | dev = qdev_new(TYPE_MIPS_BOSTON_AIA); |
| 356 | sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); |
| 357 | |
| 358 | s = BOSTON(dev); |
| 359 | s->mach = machine; |
| 360 | |
| 361 | object_initialize_child(OBJECT(machine), "cps", &s->cps, TYPE_RISCV_CPS); |
| 362 | object_property_set_str(OBJECT(&s->cps), "cpu-type", machine->cpu_type, |
| 363 | &error_fatal); |
| 364 | object_property_set_uint(OBJECT(&s->cps), "num-vp", machine->smp.cpus, |
| 365 | &error_fatal); |
| 366 | object_property_set_uint(OBJECT(&s->cps), "num-hart", machine->smp.threads, |
| 367 | &error_fatal); |
| 368 | object_property_set_uint(OBJECT(&s->cps), "num-core", machine->smp.cores, |
| 369 | &error_fatal); |
| 370 | object_property_set_uint(OBJECT(&s->cps), "gcr-base", GCR_BASE_ADDR, |
| 371 | &error_fatal); |
| 372 | sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal); |
| 373 | |
| 374 | sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1); |
| 375 | |
| 376 | flash = g_new(MemoryRegion, 1); |
| 377 | memory_region_init_rom(flash, NULL, "boston.flash", |
| 378 | boston_memmap[BOSTON_FLASH].size, &error_fatal); |
| 379 | memory_region_add_subregion_overlap(sys_mem, |
| 380 | boston_memmap[BOSTON_FLASH].base, |
| 381 | flash, 0); |
| 382 | |
| 383 | memory_region_add_subregion_overlap(sys_mem, |
| 384 | boston_memmap[BOSTON_HIGHDDR].base, |
| 385 | machine->ram, 0); |
| 386 | |
| 387 | ddr_low_alias = g_new(MemoryRegion, 1); |
| 388 | memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", |
| 389 | machine->ram, 0, |
| 390 | MIN(machine->ram_size, (256 * MiB))); |
| 391 | memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); |
| 392 | |
| 393 | pcie2 = xilinx_pcie_init(sys_mem, 2, |
| 394 | boston_memmap[BOSTON_PCIE2].base, |
| 395 | boston_memmap[BOSTON_PCIE2].size, |
| 396 | boston_memmap[BOSTON_PCIE2_MMIO].base, |
| 397 | boston_memmap[BOSTON_PCIE2_MMIO].size, |
| 398 | qdev_get_gpio_in(s->cps.aplic, PCIE2_INT)); |
| 399 | |
| 400 | platreg = g_new(MemoryRegion, 1); |
| 401 | memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, |
| 402 | "boston-platregs", |
| 403 | boston_memmap[BOSTON_PLATREG].size); |
| 404 | memory_region_add_subregion_overlap(sys_mem, |
| 405 | boston_memmap[BOSTON_PLATREG].base, platreg, 0); |
| 406 | |
| 407 | s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2, |
| 408 | qdev_get_gpio_in(s->cps.aplic, UART_INT), 10000000, |
| 409 | serial_hd(0), DEVICE_LITTLE_ENDIAN); |
| 410 | |
| 411 | lcd = g_new(MemoryRegion, 1); |
| 412 | memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); |
| 413 | memory_region_add_subregion_overlap(sys_mem, |
| 414 | boston_memmap[BOSTON_LCD].base, lcd, 0); |
| 415 | |
| 416 | chr = qemu_chr_new("lcd", "vc:320x240", NULL); |
| 417 | qemu_chr_fe_init(&s->lcd_display, chr, NULL); |
| 418 | qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, |
| 419 | boston_lcd_event, NULL, s, NULL, true); |
| 420 | |
| 421 | pdev = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, |
| 422 | PCI_DEVFN(0, 0), TYPE_ICH9_AHCI); |
| 423 | ich9 = ICH9_AHCI(pdev); |
| 424 | g_assert(ARRAY_SIZE(hd) == ich9->ahci.ports); |
| 425 | ide_drive_get(hd, ich9->ahci.ports); |
| 426 | ahci_ide_create_devs(&ich9->ahci, hd); |
| 427 | |
| 428 | /* Create e1000e using slot 0 func 1 */ |
| 429 | pci_init_nic_in_slot(&PCI_BRIDGE(&pcie2->root)->sec_bus, "e1000e", NULL, |
| 430 | "00.1"); |
| 431 | pci_init_nic_devices(&PCI_BRIDGE(&pcie2->root)->sec_bus, "e1000e"); |
| 432 | |
| 433 | if (machine->firmware) { |
| 434 | fw_size = load_image_targphys(machine->firmware, |
| 435 | 0x1fc00000, 4 * MiB, NULL); |
| 436 | if (fw_size == -1) { |
| 437 | error_report("unable to load firmware image '%s'", |
| 438 | machine->firmware); |
| 439 | exit(1); |
| 440 | } |
| 441 | if (machine->kernel_filename) { |
| 442 | fw_size = load_image_targphys(machine->kernel_filename, |
| 443 | 0x80000000, 64 * MiB, NULL); |
| 444 | if (fw_size == -1) { |
| 445 | error_report("unable to load kernel image '%s'", |
| 446 | machine->kernel_filename); |
| 447 | exit(1); |
| 448 | } |
| 449 | } |
| 450 | } else if (machine->kernel_filename) { |
| 451 | fw_size = load_image_targphys(machine->kernel_filename, |
| 452 | 0x80000000, 64 * MiB, NULL); |
| 453 | if (fw_size == -1) { |
| 454 | error_report("unable to load kernel image '%s'", |
| 455 | machine->kernel_filename); |
| 456 | exit(1); |
| 457 | } |
| 458 | |
| 459 | gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000); |
| 460 | } else if (!qtest_enabled()) { |
| 461 | error_report("Please provide either a -kernel or -bios argument"); |
| 462 | exit(1); |
| 463 | } |
| 464 | } |
| 465 | |
| 466 | static void boston_mach_class_init(MachineClass *mc) |
| 467 | { |
| 468 | mc->desc = "MIPS Boston-aia"; |
| 469 | mc->init = boston_mach_init; |
| 470 | mc->block_default_type = IF_IDE; |
| 471 | mc->default_ram_size = 2 * GiB; |
| 472 | mc->default_ram_id = "boston.ddr"; |
| 473 | mc->max_cpus = MAX_HARTS; |
| 474 | mc->default_cpu_type = TYPE_RISCV_CPU_MIPS_P8700; |
| 475 | } |
| 476 | |
| 477 | DEFINE_MACHINE_RISCV64("boston-aia", boston_mach_class_init) |