[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation
From: |
Alexander Graf |
Subject: |
Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation |
Date: |
Thu, 2 May 2013 23:01:57 +0200 |
Am 02.05.2013 um 22:08 schrieb Hervé Poussineau <address@hidden>:
> Non-contiguous I/O is not implemented.
>
> There is also somewhere a bug in the memory controller, which means
> that some real firmwares may not detect the correct amount of memory.
> This can be bypassed by adding '-m 1G' on the command line.
>
> Add x-auto-conf property, to automatically configure the memory
> controller at startup. This will be required by OpenBIOS, which
> doesn't know how to do it.
Why not teach it? I'd prefer to see that logic in firmware.
>
> Signed-off-by: Hervé Poussineau <address@hidden>
> ---
> default-configs/ppc-softmmu.mak | 1 +
> hw/pci-host/Makefile.objs | 1 +
> hw/pci-host/mpc105.c | 488 +++++++++++++++++++++++++++++++++++++++
> include/hw/pci/pci_ids.h | 1 +
> trace-events | 7 +
> 5 files changed, 498 insertions(+)
> create mode 100644 hw/pci-host/mpc105.c
>
> diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
> index cc3587f..f79b058 100644
> --- a/default-configs/ppc-softmmu.mak
> +++ b/default-configs/ppc-softmmu.mak
> @@ -28,6 +28,7 @@ CONFIG_MAC_NVRAM=y
> CONFIG_MAC_DBDMA=y
> CONFIG_HEATHROW_PIC=y
> CONFIG_GRACKLE_PCI=y
> +CONFIG_MPC105_PCI=y
> CONFIG_UNIN_PCI=y
> CONFIG_DEC_PCI=y
> CONFIG_PPCE500_PCI=y
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index 909e702..ec4427b 100644
> --- a/hw/pci-host/Makefile.objs
> +++ b/hw/pci-host/Makefile.objs
> @@ -3,6 +3,7 @@ common-obj-y += pam.o
> # PPC devices
> common-obj-$(CONFIG_PREP_PCI) += prep.o
> common-obj-$(CONFIG_GRACKLE_PCI) += grackle.o
> +common-obj-$(CONFIG_MPC105_PCI) += mpc105.o
> # NewWorld PowerMac
> common-obj-$(CONFIG_UNIN_PCI) += uninorth.o
> common-obj-$(CONFIG_DEC_PCI) += dec.o
> diff --git a/hw/pci-host/mpc105.c b/hw/pci-host/mpc105.c
> new file mode 100644
> index 0000000..8e4cc95
> --- /dev/null
> +++ b/hw/pci-host/mpc105.c
> @@ -0,0 +1,488 @@
> +/*
> + * QEMU MPC-105 Eagle PCI host
> + *
> + * Copyright (c) 2013 Hervé Poussineau
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) version 3 or any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_bus.h"
> +#include "hw/pci/pci_host.h"
> +#include "hw/i386/pc.h"
That include sounds odd :).
> +#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) {
Can't you just reuse the normal -bios logic?
> + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name);
> + if (filename) {
> + if (s->elf_machine != EM_NONE) {
Elf machine?
Alex
> + bios_size = load_elf(filename, NULL, NULL, NULL,
> + NULL, NULL, 1, s->elf_machine, 0);
> + }
> + if (bios_size < 0) {
> + bios_size = get_image_size(filename);
> + if (bios_size > 0 && bios_size <= BIOS_SIZE) {
> + hwaddr bios_addr;
> + bios_size = (bios_size + 0xfff) & ~0xfff;
> + bios_addr = (uint32_t)(-BIOS_SIZE);
> + bios_size = load_image_targphys(filename, bios_addr,
> + bios_size);
> + }
> + }
> + }
> + if (bios_size < 0 || bios_size > BIOS_SIZE) {
> + hw_error("qemu: could not load bios image '%s'\n", s->bios_name);
> + }
> + if (filename) {
> + g_free(filename);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void mpc105_class_init(ObjectClass *klass, void *data)
> +{
> + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + k->init = mpc105_initfn;
> + k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
> + k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC105;
> + k->class_id = PCI_CLASS_BRIDGE_HOST;
> + k->config_write = mpc105_write_config;
> + dc->desc = "MPC105 PCI bridge/Memory controller";
> + dc->reset = qdev_mpc105_reset;
> + dc->vmsd = &vmstate_mpc105;
> + dc->no_user = 1;
> +}
> +
> +static TypeInfo mpc105_info = {
> + .name = TYPE_MPC105,
> + .parent = TYPE_PCI_DEVICE,
> + .instance_size = sizeof(Mpc105State),
> + .class_init = mpc105_class_init,
> +};
> +
> +typedef struct Mpc105HostState {
> + PCIHostState host_state;
> + uint32_t ram_size;
> + qemu_irq irq[PCI_NUM_PINS];
> + PCIBus pci_bus;
> + MemoryRegion pci_io;
> + MemoryRegion isa_io;
> + MemoryRegion pci_intack;
> + MemoryRegion pci_memory;
> + MemoryRegion rom;
> + Mpc105State pci_dev;
> +} Mpc105HostState;
> +
> +static void mpc105_set_irq(void *opaque, int irq_num, int level)
> +{
> + qemu_irq *pic = opaque;
> +
> + qemu_set_irq(pic[irq_num] , level);
> +}
> +
> +static int mpc105_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> + return (irq_num + (pci_dev->devfn >> 3)) & 1;
> +}
> +
> +static void mpc105_pcihost_realizefn(DeviceState *d, Error **errp)
> +{
> + SysBusDevice *dev = SYS_BUS_DEVICE(d);
> + PCIHostState *h = PCI_HOST_BRIDGE(dev);
> + Mpc105HostState *s = MPC105_PCI_HOST_BRIDGE(dev);
> + int i;
> +
> + for (i = 0; i < PCI_NUM_PINS; i++) {
> + sysbus_init_irq(dev, &s->irq[i]);
> + }
> +
> + pci_bus_irqs(&s->pci_bus, mpc105_set_irq, mpc105_map_irq, s->irq, 4);
> +
> + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, s,
> + "pci-conf-idx", 1);
> + memory_region_add_subregion(get_system_io(), 0xcf8, &h->conf_mem);
> +
> + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, s,
> + "pci-conf-data", 4);
> +
- [Qemu-ppc] [PATCH 0/7] ppc/prep: add IBM RS/6000 43p machine, Hervé Poussineau, 2013/05/02
- [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Hervé Poussineau, 2013/05/02
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation,
Alexander Graf <=
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Hervé Poussineau, 2013/05/03
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Alexander Graf, 2013/05/06
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Hervé Poussineau, 2013/05/06
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Alexander Graf, 2013/05/06
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Andreas Färber, 2013/05/06
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Hervé Poussineau, 2013/05/07
- Re: [Qemu-ppc] [PATCH 1/7] pci: add MPC105 PCI host bridge emulation, Blue Swirl, 2013/05/09
[Qemu-ppc] [PATCH 2/7] qom: handle registration of new types when initializing the first ones, Hervé Poussineau, 2013/05/02