The Pegasos II is a redesign of the original Pegasos (later marked I)
that replaces the north bridge and has updated firmware but otherwise
these are very similar. The Pegasos uses the same north bridge that
AmigaOne used which we already emulate so we can also easily emulate
Pegasos I.
Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
---
hw/ppc/pegasos2.c | 156 ++++++++++++++++++++++++++++++++++------------
1 file changed, 117 insertions(+), 39 deletions(-)
diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c
index 7eaefcf9a0..a11f3c99ed 100644
--- a/hw/ppc/pegasos2.c
+++ b/hw/ppc/pegasos2.c
@@ -1,5 +1,5 @@
/*
- * QEMU PowerPC CHRP (Genesi/bPlan Pegasos II) hardware System Emulator
+ * QEMU PowerPC CHRP (Genesi/bPlan Pegasos I/II) hardware System Emulator
*
* Copyright (c) 2018-2021 BALATON Zoltan
*
@@ -15,6 +15,7 @@
#include "hw/pci/pci_host.h"
#include "hw/irq.h"
#include "hw/or-irq.h"
+#include "hw/pci-host/articia.h"
#include "hw/pci-host/mv64361.h"
#include "hw/isa/vt82c686.h"
#include "hw/ide/pci.h"
@@ -55,12 +56,18 @@
#define H_PRIVILEGE -3 /* Caller not privileged */
#define H_PARAMETER -4 /* Parameter invalid, out-of-range or conflicting */
+typedef enum {
+ PEGASOS1 = 1,
+ PEGASOS2 = 2,
+} PegasosMachineType;
+
#define TYPE_PEGASOS_MACHINE MACHINE_TYPE_NAME("pegasos")
OBJECT_DECLARE_SIMPLE_TYPE(PegasosMachineState, PEGASOS_MACHINE)
struct PegasosMachineState {
MachineState parent_obj;
+ PegasosMachineType type;
PowerPCCPU *cpu;
DeviceState *nb; /* north bridge */
DeviceState *sb; /* south bridge */
@@ -79,7 +86,7 @@ struct PegasosMachineState {
static void *pegasos2_build_fdt(PegasosMachineState *pm, int *fdt_size);
-static void pegasos2_cpu_reset(void *opaque)
+static void pegasos_cpu_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
PegasosMachineState *pm = PEGASOS_MACHINE(current_machine);
@@ -89,6 +96,8 @@ static void pegasos2_cpu_reset(void *opaque)
if (pm->vof) {
cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20;
cpu->env.nip = 0x100;
+ } else if (pm->type == PEGASOS1) {
+ cpu->env.nip = 0xfffc0100;
}
cpu_ppc_tb_reset(&cpu->env);
}
@@ -139,13 +148,15 @@ static void pegasos_init(MachineState *machine)
PegasosMachineState *pm = PEGASOS_MACHINE(machine);
CPUPPCState *env;
MemoryRegion *rom = g_new(MemoryRegion, 1);
- PCIBus *pci_bus;
+ PCIBus *pci_bus = NULL;
Object *via;
PCIDevice *dev;
I2CBus *i2c_bus;
const char *fwname = machine->firmware ?: PROM_FILENAME;
char *filename;
+ hwaddr prom_addr;
ssize_t sz;
+ int devfn;
uint8_t *spd_data;
/* init CPU */
@@ -158,7 +169,7 @@ static void pegasos_init(MachineState *machine)
/* Set time-base frequency */
cpu_ppc_tb_init(env, pm->bus_freq_hz / 4);
- qemu_register_reset(pegasos2_cpu_reset, pm->cpu);
+ qemu_register_reset(pegasos_cpu_reset, pm->cpu);
/* RAM */
if (machine->ram_size > 2 * GiB) {
@@ -176,12 +187,16 @@ static void pegasos_init(MachineState *machine)
if (!machine->firmware && !pm->vof) {
pm->vof = g_malloc0(sizeof(*pm->vof));
}
- memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal);
- memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom);
+ prom_addr = PROM_ADDR;
+ if (pm->type == PEGASOS1) {
+ prom_addr += PROM_SIZE;
+ }
+ memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), prom_addr, rom);
sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0);
if (sz <= 0) {
- sz = load_image_targphys(filename, pm->vof ? 0 : PROM_ADDR, PROM_SIZE);
+ sz = load_image_targphys(filename, pm->vof ? 0 : prom_addr, PROM_SIZE);
}
if (sz <= 0 || sz > PROM_SIZE) {
error_report("Could not load firmware '%s'", filename);
@@ -192,13 +207,37 @@ static void pegasos_init(MachineState *machine)
pm->vof->fw_size = sz;
}
- /* Marvell Discovery II system controller */
- pm->nb = DEVICE(sysbus_create_simple(TYPE_MV64361, -1,
- qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT)));
- pci_bus = mv64361_get_pci_bus(pm->nb, 1);
+ /* north bridge */
+ switch (pm->type) {
+ case PEGASOS1:
+ {
+ MemoryRegion *pci_mem, *mr;
+
+ /* Articia S */
+ pm->nb = DEVICE(sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL));
+ pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 1);
+ mr = g_new(MemoryRegion, 1);
+ memory_region_init_alias(mr, OBJECT(pm->nb), "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(pm->nb), "pci-mem-high", pci_mem,
+ 0x80000000, 0x7d000000);
+ memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
+ pci_bus = PCI_BUS(qdev_get_child_bus(pm->nb, "pci.0"));
+ break;
+ }
+ case PEGASOS2:
+ /* Marvell Discovery II system controller */
+ pm->nb = DEVICE(sysbus_create_simple(TYPE_MV64361, -1,
+ qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT)));
+ pci_bus = mv64361_get_pci_bus(pm->nb, 1);
+ break;
+ }
/* VIA VT8231 South Bridge (multifunction PCI device) */
- pm->sb = DEVICE(pci_new_multifunction(PCI_DEVFN(12, 0), TYPE_VT8231_ISA));
+ devfn = PCI_DEVFN(pm->type == PEGASOS1 ? 7 : 12, 0);
+ pm->sb = DEVICE(pci_new_multifunction(devfn, TYPE_VT8231_ISA));
via = OBJECT(pm->sb);
/* Set properties on individual devices before realizing the south bridge */
@@ -223,7 +262,21 @@ static void pegasos_init(MachineState *machine)
/* other PC hardware */
pci_vga_init(pci_bus);
- pegasos2_setup_pci_irq(pm);
+ /* pci interrupt routing */
+ switch (pm->type) {
+ case PEGASOS1:
+ qdev_connect_gpio_out_named(pm->sb, "intr", 0,
+ qdev_get_gpio_in(DEVICE(pm->cpu),
+ PPC6xx_INPUT_INT));
+ for (int i = 0; i < PCI_NUM_PINS; i++) {
+ qdev_connect_gpio_out(pm->nb, i,
+ qdev_get_gpio_in_named(pm->sb, "pirq", i));
+ }
+ break;
+ case PEGASOS2:
+ pegasos2_setup_pci_irq(pm);
+ break;
+ }
if (machine->kernel_filename) {
sz = load_elf(machine->kernel_filename, NULL, NULL, NULL,
@@ -376,7 +429,7 @@ static void pegasos2_chipset_reset(PegasosMachineState *pm)
PCI_INTERRUPT_LINE, 2, 0x309);
}
-static void pegasos2_machine_reset(MachineState *machine, ResetType type)
+static void pegasos_machine_reset(MachineState *machine, ResetType type)
{
PegasosMachineState *pm = PEGASOS_MACHINE(machine);
void *fdt;
@@ -387,6 +440,9 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type)
qemu_devices_reset(type);
if (!pm->vof) {
return; /* Firmware should set up machine so nothing to do */
+ } else if (pm->type == PEGASOS1) {
+ error_report("VOF is not supported by this machine");
+ exit(1);
}
/* Otherwise, set up devices that board firmware would normally do */
@@ -558,12 +614,12 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, PegasosMachineState *pm,
}
}
-static bool pegasos2_cpu_in_nested(PowerPCCPU *cpu)
+static bool pegasos_cpu_in_nested(PowerPCCPU *cpu)
{
return false;
}
-static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu)
+static void pegasos_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu)
{
PegasosMachineState *pm = PEGASOS_MACHINE(vhyp);
CPUPPCState *env = &cpu->env;
@@ -574,7 +630,7 @@ static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu)
if (FIELD_EX64(env->msr, MSR, PR)) {
qemu_log_mask(LOG_GUEST_ERROR, "Hypercall made with MSR[PR]=1\n");
env->gpr[3] = H_PRIVILEGE;
- } else if (env->gpr[3] == KVMPPC_H_RTAS) {
+ } else if (env->gpr[3] == KVMPPC_H_RTAS && pm->type == PEGASOS2) {
env->gpr[3] = pegasos2_rtas(cpu, pm, env->gpr[4]);
} else if (env->gpr[3] == KVMPPC_H_VOF_CLIENT) {
int ret = vof_client_call(MACHINE(pm), pm->vof, MACHINE(pm)->fdt,
@@ -596,56 +652,78 @@ static target_ulong vhyp_encode_hpt_for_kvm_pr(PPCVirtualHypervisor *vhyp)
return POWERPC_CPU(current_cpu)->env.spr[SPR_SDR1];
}
-static bool pegasos2_setprop(MachineState *ms, const char *path,
- const char *propname, void *val, int vallen)
+static bool pegasos_setprop(MachineState *ms, const char *path,
+ const char *propname, void *val, int vallen)
{
return true;
}
-static void pegasos2_machine_class_init(ObjectClass *oc, const void *data)
+static void pegasos_machine_init(MachineClass *mc)
{
- MachineClass *mc = MACHINE_CLASS(oc);
- PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc);
- VofMachineIfClass *vmc = VOF_MACHINE_CLASS(oc);
+ PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(mc);
+ VofMachineIfClass *vmc = VOF_MACHINE_CLASS(mc);
- mc->desc = "Genesi/bPlan Pegasos II";
mc->init = pegasos_init;
- mc->reset = pegasos2_machine_reset;
+ mc->reset = pegasos_machine_reset;
mc->block_default_type = IF_IDE;
mc->default_boot_order = "cd";
mc->default_display = "std";
- mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2");
- mc->default_ram_id = "pegasos2.ram";
+ mc->default_ram_id = "ram";
mc->default_ram_size = 512 * MiB;
machine_add_audiodev_property(mc);
- vhc->cpu_in_nested = pegasos2_cpu_in_nested;
- vhc->hypercall = pegasos2_hypercall;
+ vhc->cpu_in_nested = pegasos_cpu_in_nested;
+ vhc->hypercall = pegasos_hypercall;
vhc->cpu_exec_enter = vhyp_nop;
vhc->cpu_exec_exit = vhyp_nop;
vhc->encode_hpt_for_kvm_pr = vhyp_encode_hpt_for_kvm_pr;
- vmc->setprop = pegasos2_setprop;
+ vmc->setprop = pegasos_setprop;
+}
+
+static void pegasos1_init(Object *obj)
+{
+ PegasosMachineState *pm = PEGASOS_MACHINE(obj);
+
+ pm->type = PEGASOS1;
+ pm->bus_freq_hz = 33000000;
+}
+
+static void pegasos1_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Genesi/bPlan Pegasos I";
+ mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750cxe_v3.1b");
}
static void pegasos2_init(Object *obj)
{
PegasosMachineState *pm = PEGASOS_MACHINE(obj);
+ pm->type = PEGASOS2;
pm->bus_freq_hz = 133333333;
}
+static void pegasos2_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Genesi/bPlan Pegasos II";
+ mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2");
+}
+
+DEFINE_MACHINE_EXTENDED("pegasos", MACHINE, PegasosMachineState,
+ pegasos_machine_init, true, (const InterfaceInfo[]) {
+ { TYPE_PPC_VIRTUAL_HYPERVISOR },
+ { TYPE_VOF_MACHINE_IF }, { } })
+
static const TypeInfo pegasos_machine_types[] = {
{
- .name = TYPE_PEGASOS_MACHINE,
- .parent = TYPE_MACHINE,
- .instance_size = sizeof(PegasosMachineState),
- .abstract = true,
- .interfaces = (const InterfaceInfo[]) {
- { TYPE_PPC_VIRTUAL_HYPERVISOR },
- { TYPE_VOF_MACHINE_IF },
- { }
- },
+ .name = MACHINE_TYPE_NAME("pegasos1"),
+ .parent = TYPE_PEGASOS_MACHINE,
+ .class_init = pegasos1_machine_class_init,
+ .instance_init = pegasos1_init,
},
{
.name = MACHINE_TYPE_NAME("pegasos2"),
--
2.41.3
On 23/10/25 02:06, BALATON Zoltan wrote:
> The Pegasos II is a redesign of the original Pegasos (later marked I)
> that replaces the north bridge and has updated firmware but otherwise
> these are very similar. The Pegasos uses the same north bridge that
> AmigaOne used which we already emulate so we can also easily emulate
> Pegasos I.
>
> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
> ---
> hw/ppc/pegasos2.c | 156 ++++++++++++++++++++++++++++++++++------------
> 1 file changed, 117 insertions(+), 39 deletions(-)
> @@ -89,6 +96,8 @@ static void pegasos2_cpu_reset(void *opaque)
> if (pm->vof) {
> cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20;
> cpu->env.nip = 0x100;
> + } else if (pm->type == PEGASOS1) {
> + cpu->env.nip = 0xfffc0100;
Preferably add a comment explaining/describing this value (think
at your future you in 3 years ;) ).
> + case PEGASOS1:
> + {
> + MemoryRegion *pci_mem, *mr;
> +
> + /* Articia S */
> + pm->nb = DEVICE(sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL));
> + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 1);
> + mr = g_new(MemoryRegion, 1);
> + memory_region_init_alias(mr, OBJECT(pm->nb), "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(pm->nb), "pci-mem-high", pci_mem,
> + 0x80000000, 0x7d000000);
> + memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
Since I don't want to delay this series any further, as a future
cleanup consider passing the host MR (get_system_memory) as a link
property, and map PCI hi/lo regions in the host bridge realize.
> + pci_bus = PCI_BUS(qdev_get_child_bus(pm->nb, "pci.0"));
> + break;
> + }
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
On Thu, 23 Oct 2025, Philippe Mathieu-Daudé wrote:
> On 23/10/25 02:06, BALATON Zoltan wrote:
>> The Pegasos II is a redesign of the original Pegasos (later marked I)
>> that replaces the north bridge and has updated firmware but otherwise
>> these are very similar. The Pegasos uses the same north bridge that
>> AmigaOne used which we already emulate so we can also easily emulate
>> Pegasos I.
>>
>> Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
>> ---
>> hw/ppc/pegasos2.c | 156 ++++++++++++++++++++++++++++++++++------------
>> 1 file changed, 117 insertions(+), 39 deletions(-)
>
>
>> @@ -89,6 +96,8 @@ static void pegasos2_cpu_reset(void *opaque)
>> if (pm->vof) {
>> cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20;
>> cpu->env.nip = 0x100;
>> + } else if (pm->type == PEGASOS1) {
>> + cpu->env.nip = 0xfffc0100;
>
> Preferably add a comment explaining/describing this value (think
> at your future you in 3 years ;) ).
I think I can figure that out even 3 years from now :-). PPC reset vector
is 0x100 so it should point to somewhere in ROM at offset 0x100. I'm not
quite sure how real machine gets to this address (there are pins that set
initial vectors address that we don't model or maybe ROM is remapped at
startup) but it seems to be the right start address so not much I can
explain about that.
>> + case PEGASOS1:
>> + {
>> + MemoryRegion *pci_mem, *mr;
>> +
>> + /* Articia S */
>> + pm->nb = DEVICE(sysbus_create_simple(TYPE_ARTICIA, 0xfe000000,
>> NULL));
>> + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 1);
>> + mr = g_new(MemoryRegion, 1);
>> + memory_region_init_alias(mr, OBJECT(pm->nb), "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(pm->nb), "pci-mem-high",
>> pci_mem,
>> + 0x80000000, 0x7d000000);
>> + memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
>
> Since I don't want to delay this series any further, as a future
> cleanup consider passing the host MR (get_system_memory) as a link
> property, and map PCI hi/lo regions in the host bridge realize.
I'll consider this as future clean up. These should really be controlled
by registers in the north bridge but I don't have docs on Articia S so I
don't know how this should work but these are probably the default values
that we could move to the articia model but have to check if that works
for amigaone too. I'd skip the link property though to keep it simple.
This is a simple machine and other host bridges also access system memory
currently. Until get_system_memory goes away I don't see a reason to make
it more complicated.
Regards,
BALATON Zoltan
>> + pci_bus = PCI_BUS(qdev_get_child_bus(pm->nb, "pci.0"));
>> + break;
>> + }
>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>
>
© 2016 - 2025 Red Hat, Inc.