+#include "hw/loader.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+#include "trace.h"
+
+#define TYPE_MPC105_PCI_HOST_BRIDGE "mpc105-pcihost"
+#define MPC105_PCI_HOST_BRIDGE(obj) \
+ OBJECT_CHECK(Mpc105HostState, (obj), TYPE_MPC105_PCI_HOST_BRIDGE)
+
+#define TYPE_MPC105 "mpc105"
+#define MPC105(obj) \
+ OBJECT_CHECK(Mpc105State, (obj), TYPE_MPC105)
+
+#define MEM_STA_03 0x0080
+#define MEM_STA_47 0x0084
+#define EXT_MEM_STA_03 0x0088
+#define EXT_MEM_STA_47 0x008c
+#define MEM_END_03 0x0090
+#define MEM_END_47 0x0094
+#define EXT_MEM_END_03 0x0098
+#define EXT_MEM_END_47 0x009c
+#define MEM_BANK_EN 0x00a0
+#define PROC_CFG_A8 0x00a8
+#define PROC_CFG_AC 0x00ac
+#define ALT_OSV_1 0x00ba
+#define ERR_EN_REG1 0x00c0
+#define ERR_DR1 0x00c1
+#define ERR_EN_REG2 0x00c4
+#define MEM_CFG_1 0x00f0
+#define MEM_CFG_2 0x00f4
+#define MEM_CFG_4 0x00fc
+
+#define MEM_CFG_1_MEMGO (1 << 19)
+
+#define BIOS_SIZE (1024 * 1024)
+
+typedef struct Mpc105State {
+ PCIDevice parent_obj;
+ uint32_t ram_size;
+ uint32_t elf_machine;
+ uint32_t x_auto_conf;
+ char *bios_name;
+ MemoryRegion bios;
+ MemoryRegion simm[8];
+ bool use_sizer[8];
+ /* use a sizer to allow access to only part of a simm */
+ MemoryRegion sizer[8];
+} Mpc105State;
+
+static uint64_t mpc105_unassigned_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ trace_mpc105_unassigned_mem_read(addr);
+ return 0;
+}
+
+static void mpc105_unassigned_write(void *opaque, hwaddr addr,
uint64_t data,
+ unsigned int size)
+{
+ trace_mpc105_unassigned_mem_write(addr, data);
+}
+
+static const MemoryRegionOps mpc105_unassigned_ops = {
+ .read = mpc105_unassigned_read,
+ .write = mpc105_unassigned_write,
+};
+
+static void mpc105_update_memory_mappings(Mpc105State *s)
+{
+ uint32_t start_address, end_address;
+ uint32_t start, ext_start, end, ext_end;
+ uint32_t cfg1;
+ uint64_t simm_size;
+ uint8_t *pci_conf;
+ uint8_t en;
+ bool enabled;
+ int i;
+
+ pci_conf = PCI_DEVICE(s)->config;
+ cfg1 = pci_get_long(pci_conf + MEM_CFG_1);
+
+ memory_region_transaction_begin();
+ if (cfg1 & MEM_CFG_1_MEMGO) {
+ en = pci_get_byte(pci_conf + MEM_BANK_EN);
+ } else {
+ en = 0;
+ }
+
+ for (i = 0; i < 8; i++) {
+ enabled = (en & (1 << i));
+
+ start = pci_get_byte(pci_conf + MEM_STA_03 + i);
+ ext_start = pci_get_byte(pci_conf + EXT_MEM_STA_03 + i) & 0x3;
+ end = pci_get_byte(pci_conf + MEM_END_03 + i);
+ ext_end = pci_get_byte(pci_conf + EXT_MEM_STA_03 + i) & 0x3;
+ start_address = (ext_start << 28) | (start << 20);
+ end_address = (ext_end << 28) | (end << 20) | 0xfffff;
+
+ enabled &= start_address < end_address;
+
+ if (enabled) {
+ trace_mpc105_simm_enable(i, start_address, end_address
+ 1);
+ } else {
+ trace_mpc105_simm_disable(i);
+ }
+
+ simm_size = memory_region_size(&s->simm[i]);
+ if (simm_size == 0) {
+ continue;
+ }
+
+ /* Clean links between system memory, sizer and simm */
+ if (s->use_sizer[i]) {
+ memory_region_del_subregion(get_system_memory(),
&s->sizer[i]);
+ memory_region_del_subregion(&s->sizer[i], &s->simm[i]);
+ s->use_sizer[i] = false;
+ } else {
+ memory_region_del_subregion(get_system_memory(),
&s->simm[i]);
+ }
+
+ /* Recreate links compatible with new memory layout */
+ if (enabled && end_address - start_address + 1 < simm_size) {
+ memory_region_init_io(&s->sizer[i],
&mpc105_unassigned_ops,
+ s, memory_region_name(&s->sizer[i]),
+ end_address - start_address + 1);
+ memory_region_add_subregion(&s->sizer[i], 0, &s->simm[i]);
+ memory_region_add_subregion(get_system_memory(),
start_address,
+ &s->sizer[i]);
+ s->use_sizer[i] = true;
+ } else {
+ memory_region_add_subregion(get_system_memory(),
start_address,
+ &s->simm[i]);
+ }
+ memory_region_set_enabled(&s->simm[i], enabled);
+ }
+ memory_region_transaction_commit();
+}
+
+static void mpc105_write_config(PCIDevice *dev, uint32_t addr,
uint32_t val,
+ int l)
+{
+ Mpc105State *s = MPC105(dev);
+
+ pci_default_write_config(dev, addr, val, l);
+ if ((addr >= MEM_STA_03 && addr <= MEM_BANK_EN) || addr ==
MEM_CFG_1) {
+ mpc105_update_memory_mappings(s);
+ }
+}
+
+static void mpc105_reset(Mpc105State *s)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint8_t *pci_conf;
+ int id;
+
+ pci_conf = pci->config;
+
+ memset(pci_conf + PCI_CONFIG_HEADER_SIZE, 0,
+ PCI_CONFIG_SPACE_SIZE - PCI_CONFIG_HEADER_SIZE);
+ pci_conf[PCI_COMMAND] = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_conf[PCI_STATUS] = PCI_STATUS_FAST_BACK;
+ pci_set_long(pci_conf + PROC_CFG_A8, 0xff000010);
+ pci_set_long(pci_conf + PROC_CFG_AC, 0x000c060c);
+ pci_set_byte(pci_conf + ALT_OSV_1, 0x04);
+ pci_set_byte(pci_conf + ERR_EN_REG1, 0x01);
+ pci_set_long(pci_conf + MEM_CFG_1, 0xff020000);
+ pci_set_long(pci_conf + MEM_CFG_2, 0x00000003);
+ pci_set_long(pci_conf + MEM_CFG_4, 0x00100000);
+
+ memset(pci->wmask + PCI_CONFIG_HEADER_SIZE, 0,
+ MEM_CFG_1 - PCI_CONFIG_HEADER_SIZE);
+ memset(pci->wmask + 0x70, 0xff, 2);
+ memset(pci->wmask + MEM_STA_03, 0xff, MEM_BANK_EN - MEM_STA_03
+ 1);
+ memset(pci->wmask + PROC_CFG_A8, 0xff, 8);
+ pci_set_word(pci->wmask + ALT_OSV_1, 0xffff);
+ pci_set_byte(pci->wmask + ERR_EN_REG1, 0xff);
+ pci_set_byte(pci->w1cmask + ERR_DR1, 0xff);
+ pci_set_byte(pci->w1cmask + 0xc3, 0xff);
+ pci_set_byte(pci->wmask + ERR_EN_REG2, 0xff);
+ pci_set_byte(pci->w1cmask + 0xc5, 0xff);
+ pci_set_byte(pci->w1cmask + 0xc7, 0xff);
+
+ for (id = 0; id < 8; ++id) {
+ memory_region_set_enabled(&s->simm[id], false);
+ }
+
+ if (s->x_auto_conf) {
+ /* enable all memory banks, starting from address 0 */
+ uint32_t start_address = 0, end_address = 0;
+ uint8_t ext_start, ext_end, enabled = 0;
+ int i;
+ for (i = 0; i < 8; i++) {
+ if (!memory_region_size(&s->simm[i])) {
+ continue;
+ }
+ end_address += memory_region_size(&s->simm[i]);
+ ext_start = pci_get_byte(pci_conf + EXT_MEM_STA_03 + i)
& ~0x3;
+ ext_end = pci_get_byte(pci_conf + EXT_MEM_STA_03 + i) &
~0x3;
+ ext_start |= (start_address >> 28) & 0x3;
+ ext_end |= ((end_address - 1) >> 28) & 0x3;
+ pci_set_byte(pci_conf + MEM_STA_03 + i, start_address
>> 20);
+ pci_set_byte(pci_conf + EXT_MEM_STA_03 + i, ext_start);
+ pci_set_byte(pci_conf + MEM_END_03 + i, (end_address -
1) >> 20);
+ pci_set_byte(pci_conf + EXT_MEM_END_03 + i, ext_end);
+ start_address = end_address;
+ enabled |= 1 << i;
+ }
+ pci_set_byte(pci_conf + MEM_BANK_EN, enabled);
+ pci_long_test_and_set_mask(pci_conf + MEM_CFG_1,
MEM_CFG_1_MEMGO);
+ mpc105_update_memory_mappings(s);
+ }
+}
+
+static void qdev_mpc105_reset(DeviceState *dev)
+{
+ Mpc105State *s = MPC105(dev);
+ mpc105_reset(s);
+}
+
+static int mpc105_post_load(void *opaque, int version_id)
+{
+ Mpc105State *s = opaque;
+ mpc105_update_memory_mappings(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_mpc105 = {
+ .name = "mpc105",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 1,
+ .post_load = mpc105_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, Mpc105State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static uint64_t mpc105_intack_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ return pic_read_irq(isa_pic);
+}
+
+static const MemoryRegionOps mpc105_intack_ops = {
+ .read = mpc105_intack_read,
+ .valid = {
+ .max_access_size = 1,
+ },
+};
+
+static int mpc105_initfn(PCIDevice *dev)
+{
+ Mpc105State *s = MPC105(dev);
+ char *filename;
+ int bios_size = -1;
+ int i = 0;
+ uint32_t simm_size[8] = { 0 };
+
+ unsigned int ram_size = s->ram_size / (1024 * 1024);
+ while (i < 8) {
+ int idx = qemu_fls(ram_size);
+ if (idx < 5) {
+ /* Need at least 16 Mb for a slot */
+ break;
+ } else if (idx >= 8) {
+ /* Limit to 128 Mb by slot (at max) */
+ idx = 8;
+ }
+ simm_size[i] = 1 << (idx - 1);
+ ram_size -= simm_size[i];
+ i++;
+ }
+
+ for (i = 0; i < 8; i++) {
+ char name[] = "simm.?";
+ name[5] = i + '0';
+ if (simm_size[i]) {
+ trace_mpc105_simm_size(i, simm_size[i]);
+ memory_region_init_ram(&s->simm[i], name,
+ simm_size[i] * 1024 * 1024);
+ vmstate_register_ram_global(&s->simm[i]);
+ } else {
+ memory_region_init(&s->simm[i], name, 0);
+ }
+ memory_region_init(&s->sizer[i], "sizer", 0);
+ memory_region_add_subregion_overlap(get_system_memory(), 0,
+ &s->simm[i], i);
+ memory_region_set_enabled(&s->simm[i], false);
+ }
+
+ memory_region_init_ram(&s->bios, "bios", BIOS_SIZE);
+ memory_region_set_readonly(&s->bios, true);
+ memory_region_add_subregion(get_system_memory(),
(uint32_t)(-BIOS_SIZE),
+ &s->bios);
+ vmstate_register_ram_global(&s->bios);
+ if (s->bios_name) {