1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
/*
* QEMU Eyetech AmigaOne/Mai Logic Teron emulation
*
* Copyright (c) 2023 BALATON Zoltan
*
* This work is licensed under the GNU GPL license version 2 or later.
*
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/datadir.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/ppc/ppc.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/pci-host/articia.h"
#include "hw/isa/vt82c686.h"
#include "hw/ide/pci.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/ppc/ppc.h"
#include "sysemu/qtest.h"
#include "sysemu/reset.h"
#include "kvm_ppc.h"
#define BUS_FREQ_HZ 100000000
/*
* Firmware binary available at
* https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28
* then "tail -c 524288 updater.image >u-boot-amigaone.bin"
*
* BIOS emulator in firmware cannot run QEMU vgabios and hangs on it, use
* -device VGA,romfile=VGABIOS-lgpl-latest.bin
* from http://www.nongnu.org/vgabios/ instead.
*/
#define PROM_FILENAME "u-boot-amigaone.bin"
#define PROM_ADDR 0xfff00000
#define PROM_SIZE (512 * KiB)
static void amigaone_cpu_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
cpu_reset(CPU(cpu));
cpu_ppc_tb_reset(&cpu->env);
}
static void fix_spd_data(uint8_t *spd)
{
uint32_t bank_size = 4 * MiB * spd[31];
uint32_t rows = bank_size / spd[13] / spd[17];
spd[3] = ctz32(rows) - spd[4];
}
static void amigaone_init(MachineState *machine)
{
PowerPCCPU *cpu;
CPUPPCState *env;
MemoryRegion *rom, *pci_mem, *mr;
const char *fwname = machine->firmware ?: PROM_FILENAME;
char *filename;
ssize_t sz;
PCIBus *pci_bus;
Object *via;
DeviceState *dev;
I2CBus *i2c_bus;
uint8_t *spd_data;
int i;
/* init CPU */
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
env = &cpu->env;
if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
error_report("Incompatible CPU, only 6xx bus supported");
exit(1);
}
cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4);
qemu_register_reset(amigaone_cpu_reset, cpu);
/* RAM */
if (machine->ram_size > 2 * GiB) {
error_report("RAM size more than 2 GiB is not supported");
exit(1);
}
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
if (machine->ram_size < 1 * GiB + 32 * KiB) {
/* Firmware uses this area for startup */
mr = g_new(MemoryRegion, 1);
memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal);
memory_region_add_subregion(get_system_memory(), 0x40000000, mr);
}
/* allocate and load firmware */
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, fwname);
if (filename) {
rom = g_new(MemoryRegion, 1);
memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal);
memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom);
sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE);
if (sz <= 0 || sz > PROM_SIZE) {
error_report("Could not load firmware '%s'", filename);
exit(1);
}
g_free(filename);
} else if (!qtest_enabled()) {
error_report("Could not find firmware '%s'", fwname);
exit(1);
}
/* Articia S */
dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL);
i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus"));
if (machine->ram_size > 512 * MiB) {
spd_data = spd_data_generate(SDR, machine->ram_size / 2);
} else {
spd_data = spd_data_generate(SDR, machine->ram_size);
}
fix_spd_data(spd_data);
smbus_eeprom_init_one(i2c_bus, 0x51, spd_data);
if (machine->ram_size > 512 * MiB) {
smbus_eeprom_init_one(i2c_bus, 0x52, spd_data);
}
pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
mr = g_new(MemoryRegion, 1);
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem,
0, 0x1000000);
memory_region_add_subregion(get_system_memory(), 0xfd000000, mr);
mr = g_new(MemoryRegion, 1);
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem,
0x80000000, 0x7d000000);
memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
/* VIA VT82c686B South Bridge (multifunction PCI device) */
via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(7, 0),
TYPE_VT82C686B_ISA));
object_property_add_alias(OBJECT(machine), "rtc-time",
object_resolve_path_component(via, "rtc"),
"date");
qdev_connect_gpio_out(DEVICE(via), 0,
qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT));
for (i = 0; i < PCI_NUM_PINS; i++) {
qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via),
"pirq", i));
}
pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide")));
pci_vga_init(pci_bus);
}
static void amigaone_machine_init(MachineClass *mc)
{
mc->desc = "Eyetech AmigaOne/Mai Logic Teron";
mc->init = amigaone_init;
mc->block_default_type = IF_IDE;
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2");
mc->default_display = "std";
mc->default_ram_id = "ram";
mc->default_ram_size = 512 * MiB;
}
DEFINE_MACHINE("amigaone", amigaone_machine_init)
|