diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index 006aae63b9..e2b2faa4a3 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -8,7 +8,8 @@ #define FIRMWARE_END 0xf0100000 #define FIRMWARE_HIGH 0xfffffff0 /* upper 32-bits of 64-bit firmware address */ -#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */ +#define RAM_MAP_HIGH1 0x0100000000 /* memory above 4 GB */ +#define RAM_MAP_HIGH2 0x4040000000 /* memory between 1 G and 3.75 GB */ #define MEM_PDC_ENTRY 0x4800 /* PDC entry address */ diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 58e76bee2e..3b03ad44d2 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -43,6 +43,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(HppaMachineState, HPPA_COMMON_MACHINE) struct HppaMachineState { MachineState parent_obj; + + uint64_t memsplit_addr; }; #define MIN_SEABIOS_HPPA_VERSION 22 /* require at least this fw version */ @@ -208,6 +210,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, const char qemu_version[] = QEMU_VERSION; MachineClass *mc = MACHINE_GET_CLASS(ms); int btlb_entries = HPPA_BTLB_ENTRIES(&cpu[0]->env); + struct HppaMachineState *hpm = HPPA_COMMON_MACHINE(ms); int len; fw_cfg = fw_cfg_init_mem_nodma(addr, addr + 4, 1); @@ -231,6 +234,10 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, fw_cfg_add_file(fw_cfg, "/etc/hppa/machine", g_memdup2(mc->name, len), len); + val = cpu_to_le64(hpm->memsplit_addr); + fw_cfg_add_file(fw_cfg, "/etc/hppa/memsplit-addr", + g_memdup2(&val, sizeof(val)), sizeof(val)); + val = cpu_to_le64(soft_power_reg); fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr", g_memdup2(&val, sizeof(val)), sizeof(val)); @@ -287,6 +294,8 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) TranslateFn *translate; MemoryRegion *cpu_region; uint64_t ram_max; + struct HppaMachineState *hpm; + hwaddr splitaddr; /* Create CPUs. */ for (unsigned int i = 0; i < smp_cpus; i++) { @@ -347,21 +356,36 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB); machine->ram_size = ram_max; } - if (machine->ram_size <= FIRMWARE_START) { - /* contiguous memory up to 3.75 GB RAM */ - memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); - } else { + + hpm = HPPA_COMMON_MACHINE(machine); + if (!hpm->memsplit_addr) { /* non-contiguous: Memory above 3.75 GB is mapped at RAM_MAP_HIGH */ - MemoryRegion *mem_region; - mem_region = g_new(MemoryRegion, 2); - memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, - "LowMem", machine->ram, 0, FIRMWARE_START); - memory_region_init_alias(&mem_region[1], &addr_space->parent_obj, - "HighMem", machine->ram, FIRMWARE_START, - machine->ram_size - FIRMWARE_START); - memory_region_add_subregion_overlap(addr_space, 0, &mem_region[0], -1); - memory_region_add_subregion_overlap(addr_space, RAM_MAP_HIGH, - &mem_region[1], -1); + hpm->memsplit_addr = FIRMWARE_START; + } + splitaddr = hpm->memsplit_addr; + + MemoryRegion *mem_region; + mem_region = g_new(MemoryRegion, 1); + memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, + "memory0", machine->ram, 0, splitaddr); + memory_region_add_subregion_overlap(addr_space, 0, &mem_region[0], -1); + if (hppa_is_pa20(&cpu[0]->env)) { + if (machine->ram_size > 4 * GiB) { + mem_region = g_new(MemoryRegion, 1); + memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, + "memory1", machine->ram, 4 * GiB, + machine->ram_size - 4 * GiB); + memory_region_add_subregion_overlap(addr_space, RAM_MAP_HIGH1, + &mem_region[0], -1); + } + if (machine->ram_size > splitaddr) { + mem_region = g_new(MemoryRegion, 1); + memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, + "memory2", machine->ram, splitaddr, + 4 * GiB - splitaddr); + memory_region_add_subregion_overlap(addr_space, RAM_MAP_HIGH2, + &mem_region[0], -1); + } } return translate; @@ -757,6 +781,10 @@ static void machine_HP_C3700_init(MachineState *machine) */ static void machine_HP_A400_init(MachineState *machine) { + struct HppaMachineState *hpm; + + hpm = HPPA_COMMON_MACHINE(machine); + hpm->memsplit_addr = 1 * GiB; machine_HP_C3700_init(machine); } @@ -815,7 +843,7 @@ static void hppa_machine_common_class_init(ObjectClass *oc, const void *data) mc->default_cpus = 1; mc->max_cpus = HPPA_MAX_CPUS; mc->default_boot_order = "cd"; - mc->default_ram_id = "ram"; + mc->default_ram_id = "hppa.ram"; mc->default_nic = "tulip"; nc->nmi_monitor_handler = hppa_nmi; diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 59d8007a1a..8c61c696bd 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -14,7 +14,6 @@ * - All user-added devices are currently attached to the first * Elroy (PCI bus) only for now. To fix this additional work in * SeaBIOS and this driver is needed. See "user_creatable" flag below. - * - GMMIO (Greater than 4 GB MMIO) register */ #define TYPE_ASTRO_IOMMU_MEMORY_REGION "astro-iommu-memory-region" @@ -37,6 +36,11 @@ #include "qom/object.h" #include "exec/target_page.h" +static const int elroy_hpa_offsets[ELROY_NUM] = { + 0x30000, 0x32000, 0x38000, 0x3c000 }; +static const char elroy_rope_nr[ELROY_NUM] = { + 0, 1, 4, 6 }; /* busnum path, e.g. [10:6] */ + /* * Helper functions */ @@ -525,14 +529,90 @@ static ElroyState *elroy_init(int num) * Astro Runway chip. */ +static void adjust_LMMIO_mapping(AstroState *s) +{ + MemoryRegion *lmmio; + uint64_t map_addr, map_size, align_mask; + uint32_t map_route, map_enabled, i; + + lmmio = &s->lmmio; + + /* read LMMIO distributed route and calculate size */ + map_route = s->ioc_ranges[(0x370 - 0x300) / 8] >> 58; + map_route = MIN(MAX(map_route, 20), 23); + + /* calculate size of each mapping, sum of all is 8-64 MB */ + map_size = 1ULL << map_route; + align_mask = ~(map_size - 1); + + /* read LMMIO_DIST_BASE for mapping address */ + map_addr = s->ioc_ranges[(0x360 - 0x300) / 8]; + map_enabled = map_addr & 1; + map_addr &= MAKE_64BIT_MASK(24, 5); + map_addr |= MAKE_64BIT_MASK(29, 36); + map_addr &= align_mask; + s->ioc_ranges[(0x360 - 0x300) / 8] = map_addr | map_enabled; + + /* make sure the lmmio region is initially turned off */ + if (lmmio->enabled) { + memory_region_set_enabled(lmmio, false); + } + + /* exit if range is not enabled */ + if (!map_enabled) { + return; + } + + if (!lmmio->name) { + memory_region_init_io(lmmio, OBJECT(s), &unassigned_io_ops, s, + "LMMIO", ROPES_PER_IOC * map_size); + memory_region_add_subregion_overlap(get_system_memory(), + map_addr, lmmio, 1); + } + + memory_region_set_address(lmmio, map_addr); + memory_region_set_size(lmmio, ROPES_PER_IOC * map_size); + memory_region_set_enabled(lmmio, true); + + for (i = 0; i < ELROY_NUM; i++) { + MemoryRegion *alias; + ElroyState *elroy; + int rope; + + elroy = s->elroy[i]; + alias = &elroy->lmmio_alias; + rope = elroy_rope_nr[i]; + if (alias->enabled) { + memory_region_set_enabled(alias, false); + } + + if (!alias->name) { + memory_region_init_alias(alias, OBJECT(elroy), + "lmmio-alias", &elroy->pci_mmio, 0, map_size); + memory_region_add_subregion_overlap(lmmio, rope * map_size, + alias, 2); + } + + memory_region_set_address(alias, rope * map_size); + memory_region_set_alias_offset(alias, + (uint32_t) (map_addr + rope * map_size)); + memory_region_set_size(alias, map_size); + memory_region_set_enabled(alias, true); + } +} + static void adjust_LMMIO_DIRECT_mapping(AstroState *s, unsigned int reg_index) { MemoryRegion *lmmio_alias; unsigned int lmmio_index, map_route; hwaddr map_addr; - uint32_t map_size; + uint32_t map_size, map_enabled; struct ElroyState *elroy; + /* each LMMIO may access from 1 MB up to 64 MB */ + const unsigned int lmmio_mask = ~(1 * MiB - 1); + const unsigned int lmmio_max_size = 64 * MiB; + /* pointer to LMMIO_DIRECT entry */ lmmio_index = reg_index / 3; lmmio_alias = &s->lmmio_direct[lmmio_index]; @@ -545,30 +625,104 @@ static void adjust_LMMIO_DIRECT_mapping(AstroState *s, unsigned int reg_index) map_route &= (ELROY_NUM - 1); elroy = s->elroy[map_route]; + /* make sure the lmmio region is initially turned off */ if (lmmio_alias->enabled) { memory_region_set_enabled(lmmio_alias, false); } + /* do sanity checks and calculate mmio size */ + map_enabled = map_addr & 1; + map_addr &= lmmio_mask; + map_size &= lmmio_mask; + map_size = MIN(map_size, lmmio_max_size); map_addr = F_EXTEND(map_addr); - map_addr &= TARGET_PAGE_MASK; - map_size = (~map_size) + 1; - map_size &= TARGET_PAGE_MASK; - /* exit if disabled or zero map size */ - if (!(map_addr & 1) || !map_size) { + /* exit if disabled or has zero size. */ + if (!map_enabled || !map_size) { return; } - if (!memory_region_size(lmmio_alias)) { + if (!lmmio_alias->name) { + char lmmio_name[32]; + snprintf(lmmio_name, sizeof(lmmio_name), + "LMMIO-DIRECT-%u", lmmio_index); memory_region_init_alias(lmmio_alias, OBJECT(elroy), - "pci-lmmmio-alias", &elroy->pci_mmio, + lmmio_name, &elroy->pci_mmio, (uint32_t) map_addr, map_size); - memory_region_add_subregion(get_system_memory(), map_addr, - lmmio_alias); - } else { - memory_region_set_alias_offset(lmmio_alias, map_addr); - memory_region_set_size(lmmio_alias, map_size); - memory_region_set_enabled(lmmio_alias, true); + memory_region_add_subregion_overlap(get_system_memory(), + map_addr, lmmio_alias, 3); + } + + memory_region_set_address(lmmio_alias, map_addr); + memory_region_set_alias_offset(lmmio_alias, (uint32_t) map_addr); + memory_region_set_size(lmmio_alias, map_size); + memory_region_set_enabled(lmmio_alias, true); +} + +static void adjust_GMMIO_mapping(AstroState *s) +{ + MemoryRegion *gmmio; + uint64_t map_addr, map_size, align_mask; + uint32_t map_route, map_enabled, i; + + gmmio = &s->gmmio; + map_addr = s->ioc_ranges[(0x378 - 0x300) / 8]; /* GMMIO_DIST_BASE */ + map_enabled = map_addr & 1; + map_addr &= MAKE_64BIT_MASK(32, 8); + s->ioc_ranges[(0x378 - 0x300) / 8] = map_addr | map_enabled; + + map_route = s->ioc_ranges[(0x388 - 0x300) / 8] >> 58; /* GMMIO_DIST_ROUTE */ + map_route = MIN(MAX(map_route, 29), 33); /* between 4-16 GB total */ + map_size = 1ULL << map_route; /* size of each mapping */ + align_mask = ~(map_size - 1); + + /* make sure the lmmio region is initially turned off */ + if (gmmio->enabled) { + memory_region_set_enabled(gmmio, false); + } + + /* do sanity checks and calculate mmio size */ + map_addr &= align_mask; + + /* exit if disabled */ + if (!map_enabled) { + return; + } + + if (!gmmio->name) { + memory_region_init_io(gmmio, OBJECT(s), &unassigned_io_ops, s, + "GMMIO", ROPES_PER_IOC * map_size); + memory_region_add_subregion_overlap(get_system_memory(), + map_addr, gmmio, 1); + } + + memory_region_set_address(gmmio, map_addr); + memory_region_set_size(gmmio, ROPES_PER_IOC * map_size); + memory_region_set_enabled(gmmio, true); + + for (i = 0; i < ELROY_NUM; i++) { + MemoryRegion *alias; + ElroyState *elroy; + int rope; + + elroy = s->elroy[i]; + alias = &elroy->gmmio_alias; + rope = elroy_rope_nr[i]; + if (alias->enabled) { + memory_region_set_enabled(alias, false); + } + + if (!alias->name) { + memory_region_init_alias(alias, OBJECT(elroy), + "gmmio-alias", &elroy->pci_mmio, 0, map_size); + memory_region_add_subregion_overlap(gmmio, rope * map_size, + alias, 2); + } + + memory_region_set_address(alias, rope * map_size); + memory_region_set_alias_offset(alias, map_addr + rope * map_size); + memory_region_set_size(alias, map_size); + memory_region_set_enabled(alias, true); } } @@ -684,6 +838,12 @@ static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr, if (index < LMMIO_DIRECT_RANGES * 3) { adjust_LMMIO_DIRECT_mapping(s, index); } + if (addr >= 0x360 && addr <= 0x370 + 7) { + adjust_LMMIO_mapping(s); + } + if (addr >= 0x378 && addr <= 0x388 + 7) { + adjust_GMMIO_mapping(s); + } break; case 0x10200: case 0x10220: @@ -799,13 +959,13 @@ static void astro_reset(DeviceState *dev) * The LBA BASE/MASK registers control IO -> System routing (in Elroy) */ memset(&s->ioc_ranges, 0, sizeof(s->ioc_ranges)); - s->ioc_ranges[(0x360 - 0x300) / 8] = LMMIO_DIST_BASE_ADDR | 0x01; /* LMMIO_DIST_BASE (SBA) */ + s->ioc_ranges[(0x360 - 0x300) / 8] = F_EXTEND(LMMIO_DIST_BASE_ADDR) | 0x01; s->ioc_ranges[(0x368 - 0x300) / 8] = 0xfc000000; /* LMMIO_DIST_MASK */ s->ioc_ranges[(0x370 - 0x300) / 8] = 0; /* LMMIO_DIST_ROUTE */ - s->ioc_ranges[(0x390 - 0x300) / 8] = IOS_DIST_BASE_ADDR | 0x01; /* IOS_DIST_BASE */ + s->ioc_ranges[(0x390 - 0x300) / 8] = F_EXTEND(IOS_DIST_BASE_ADDR) | 0x01; s->ioc_ranges[(0x398 - 0x300) / 8] = 0xffffff0000; /* IOS_DIST_MASK */ s->ioc_ranges[(0x3a0 - 0x300) / 8] = 0x3400000000000000ULL; /* IOS_DIST_ROUTE */ - s->ioc_ranges[(0x3c0 - 0x300) / 8] = 0xfffee00000; /* IOS_DIRECT_BASE */ + s->ioc_ranges[(0x3c0 - 0x300) / 8] = IOS_DIST_BASE_ADDR; /* IOS_DIRECT_BASE */ s->ioc_ranges[(0x3c8 - 0x300) / 8] = 0xffffff0000; /* IOS_DIRECT_MASK */ s->ioc_ranges[(0x3d0 - 0x300) / 8] = 0x0; /* IOS_DIRECT_ROUTE */ @@ -843,10 +1003,6 @@ static void astro_realize(DeviceState *obj, Error **errp) /* Create Elroys (PCI host bus chips). */ for (i = 0; i < ELROY_NUM; i++) { - static const int elroy_hpa_offsets[ELROY_NUM] = { - 0x30000, 0x32000, 0x38000, 0x3c000 }; - static const char elroy_rope_nr[ELROY_NUM] = { - 0, 1, 4, 6 }; /* busnum path, e.g. [10:6] */ int addr_offset; ElroyState *elroy; hwaddr map_addr; @@ -891,15 +1047,6 @@ static void astro_realize(DeviceState *obj, Error **errp) elroy->mmio_base[(0x0240 - 0x200) / 8] = rope * map_size | 0x01; elroy->mmio_base[(0x0248 - 0x200) / 8] = 0x0000e000; - /* map elroys mmio */ - map_size = LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC; - map_addr = F_EXTEND(LMMIO_DIST_BASE_ADDR + rope * map_size); - memory_region_init_alias(&elroy->pci_mmio_alias, OBJECT(elroy), - "pci-mmio-alias", - &elroy->pci_mmio, (uint32_t) map_addr, map_size); - memory_region_add_subregion(get_system_memory(), map_addr, - &elroy->pci_mmio_alias); - /* map elroys io */ map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC; map_addr = F_EXTEND(IOS_DIST_BASE_ADDR + rope * map_size); diff --git a/include/hw/pci-host/astro.h b/include/hw/pci-host/astro.h index fce052c9f8..0cd384bceb 100644 --- a/include/hw/pci-host/astro.h +++ b/include/hw/pci-host/astro.h @@ -26,8 +26,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(ElroyState, ELROY_PCI_HOST_BRIDGE) #define LMMIO_DIRECT_RANGES 4 -#define IOS_DIST_BASE_ADDR 0xfffee00000ULL -#define IOS_DIST_BASE_SIZE 0x10000ULL +#define IOS_DIST_BASE_ADDR 0xffffee00000ULL +#define IOS_DIST_BASE_SIZE 0x10000ULL #define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ @@ -61,9 +61,10 @@ struct ElroyState { MemoryRegion this_mem; MemoryRegion pci_mmio; - MemoryRegion pci_mmio_alias; - MemoryRegion pci_hole; MemoryRegion pci_io; + + MemoryRegion gmmio_alias; + MemoryRegion lmmio_alias; }; struct AstroState { @@ -89,6 +90,9 @@ struct AstroState { MemoryRegion this_mem; MemoryRegion lmmio_direct[LMMIO_DIRECT_RANGES]; + MemoryRegion lmmio; + MemoryRegion gmmio; + IOMMUMemoryRegion iommu; AddressSpace iommu_as; }; diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index 5245d6022e..5c1de0eace 100644 Binary files a/pc-bios/hppa-firmware.img and b/pc-bios/hppa-firmware.img differ diff --git a/pc-bios/hppa-firmware64.img b/pc-bios/hppa-firmware64.img index cf98ced1c3..5dd7490605 100644 Binary files a/pc-bios/hppa-firmware64.img and b/pc-bios/hppa-firmware64.img differ diff --git a/roms/seabios-hppa b/roms/seabios-hppa index cf3a472f4d..d9560852a3 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit cf3a472f4ddea30d2d483a8ba4b005505f0dabb3 +Subproject commit d9560852a34f156155b3777745baa0d96d553f22 diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index d5a20cd549..3e87b9a001 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -203,7 +203,12 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* step 7 */ if (i == EXCP_TOC) { - env->iaoq_f = hppa_form_gva(env, 0, FIRMWARE_START); + hwaddr pdc_toc_addr = FIRMWARE_START; + + /* for 64-bit include the high bits of PDC */ + pdc_toc_addr |= ((uint64_t) FIRMWARE_HIGH) << 32; + env->iaoq_f = hppa_form_gva(env, 0, pdc_toc_addr); + /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */ env->gr[24] = env->cr_back[0]; env->gr[25] = env->cr_back[1];