From 7162bdea75e9f63afdd2cacfe26ee4b472a59362 Mon Sep 17 00:00:00 2001 From: Julio Guerra Date: Mon, 24 Jun 2013 23:15:54 +0200 Subject: e600 core for MPC86xx processors MPC86xx processors are based on the e600 core, which is not the case in qemu where it is based on the 7400 processor. This patch creates the e600 core and instantiates the MPC86xx processors based on it. Therefore, adding the high BATs, the SPRG 4..7 registers, which are e600-specific [1], and a HW MMU model (as 7400). This allows to define the MPC8610 processor too. Tested with a kernel using the HW TLB misses. [1] http://cache.freescale.com/files/32bit/doc/ref_manual/E600CORERM.pdf Signed-off-by: Julio Guerra Signed-off-by: Alexander Graf --- target-ppc/cpu-models.c | 10 ++-- target-ppc/cpu-models.h | 4 +- target-ppc/translate_init.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 9 deletions(-) diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 9bb68c8191..13dd670218 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -792,17 +792,15 @@ POWERPC_DEF_SVR("MPC8572E", "MPC8572E", CPU_POWERPC_MPC8572E, POWERPC_SVR_8572E, e500v2) /* e600 family */ - POWERPC_DEF("e600", CPU_POWERPC_e600, 7400, + POWERPC_DEF("e600", CPU_POWERPC_e600, e600, "PowerPC e600 core") /* PowerPC e600 microcontrollers */ -#if defined(TODO) POWERPC_DEF_SVR("MPC8610", "MPC8610", - CPU_POWERPC_MPC8610, POWERPC_SVR_8610, 7400) -#endif + CPU_POWERPC_MPC8610, POWERPC_SVR_8610, e600) POWERPC_DEF_SVR("MPC8641", "MPC8641", - CPU_POWERPC_MPC8641, POWERPC_SVR_8641, 7400) + CPU_POWERPC_MPC8641, POWERPC_SVR_8641, e600) POWERPC_DEF_SVR("MPC8641D", "MPC8641D", - CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, 7400) + CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, e600) /* 32 bits "classic" PowerPC */ /* PowerPC 6xx family */ POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index ae8f7c743e..fcd3a151d6 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -732,9 +732,7 @@ enum { POWERPC_SVR_8568E = 0x807D0011 | POWERPC_SVR_E500, POWERPC_SVR_8572 = 0x80E00010 | POWERPC_SVR_E500, POWERPC_SVR_8572E = 0x80E80010 | POWERPC_SVR_E500, -#if 0 - POWERPC_SVR_8610 = xxx, -#endif + POWERPC_SVR_8610 = 0x80A00011, POWERPC_SVR_8641 = 0x80900021, POWERPC_SVR_8641D = 0x80900121, }; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index b62f04abef..76483d9aa2 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -6479,6 +6479,131 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } +static void init_proc_e600 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); + /* 74xx specific SPR */ + gen_spr_74xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_UBAMR, "UBAMR", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_LDSTCR, "LDSTCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_ICTRL, "ICTRL", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MSSSR0, "MSSSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC5, "PMC5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_UPMC5, "UPMC5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC6, "PMC6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_UPMC6, "UPMC6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* SPRGs */ + spr_register(env, SPR_SPRG4, "SPRG4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG4, "USPRG4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG5, "SPRG5", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG5, "USPRG5", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG6, "SPRG6", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG6, "USPRG6", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPRG7, "SPRG7", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_USPRG7, "USPRG7", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Memory management */ + gen_low_BATs(env); + gen_high_BATs(env); + gen_74xx_soft_tlb(env, 128, 2); + init_excp_7450(env); + env->dcache_line_size = 32; + env->icache_line_size = 32; + /* Allocate hardware IRQ controller */ + ppc6xx_irq_init(env); +} + +POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC e600"; + pcc->init_proc = init_proc_e600; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + #if defined (TARGET_PPC64) #if defined(CONFIG_USER_ONLY) #define POWERPC970_HID5_INIT 0x00000080 -- cgit v1.2.3 From 9a39970df783cf8317e7dbf00a8af184ce868b1b Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sat, 29 Jun 2013 15:47:26 +0200 Subject: spapr: Fix compiler warnings for some versions of gcc i686-w64-mingw32-gcc (GCC) 4.6.3 from Debian wheezy reports these warnings: hw/ppc/spapr_hcall.c:188:1: warning: control reaches end of non-void function [-Wreturn-type] hw/ppc/spapr_pci.c:454:1: warning: control reaches end of non-void function [-Wreturn-type] Both warnings are fixed by using g_assert_not_reached instead of assert. A second line with assert(0) in spapr_pci.c which did not raise a compiler warning was modified, too, because g_assert_not_reached documents the purpose of that statement and is not removed in release builds. Signed-off-by: Stefan Weil Acked-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr_hcall.c | 2 +- hw/ppc/spapr_pci.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index e6f321d538..8bad27fb8a 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -184,7 +184,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_HARDWARE; } - assert(0); + g_assert_not_reached(); } #define H_BULK_REMOVE_TYPE 0xc000000000000000ULL diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 18f2e2f842..318bc9d6ef 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -451,7 +451,7 @@ static uint64_t spapr_io_read(void *opaque, hwaddr addr, case 4: return cpu_inl(addr); } - assert(0); + g_assert_not_reached(); } static void spapr_io_write(void *opaque, hwaddr addr, @@ -468,7 +468,7 @@ static void spapr_io_write(void *opaque, hwaddr addr, cpu_outl(addr, data); return; } - assert(0); + g_assert_not_reached(); } static const MemoryRegionOps spapr_io_ops = { -- cgit v1.2.3 From a3801402aa01e2ecb617f89b2e11f212d948b5af Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Mon, 24 Jun 2013 19:48:47 +0200 Subject: spapr: Use named enum for function remove_hpte The function returned a target_ulong which was made from unnamed enum values. The target_ulong was then assigned to an int variable which was used in a switch statement. Using a named enum in both cases makes reviews easier. Signed-off-by: Stefan Weil Signed-off-by: Alexander Graf --- hw/ppc/spapr_hcall.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8bad27fb8a..ed32decebf 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -121,14 +121,14 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } -enum { +typedef enum { REMOVE_SUCCESS = 0, REMOVE_NOT_FOUND = 1, REMOVE_PARM = 2, REMOVE_HW = 3, -}; +} RemoveResult; -static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, +static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong avpn, target_ulong flags, target_ulong *vp, target_ulong *rp) @@ -165,7 +165,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - int ret; + RemoveResult ret; ret = remove_hpte(env, pte_index, avpn, flags, &args[0], &args[1]); -- cgit v1.2.3 From 8e7ea787a20e30d44232cafb5a6e9a9fea364c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Wed, 3 Jul 2013 21:26:50 +0200 Subject: spapr: Respect -bios command line option for SLOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the user to override the firmware file name rather than always using "slof.bin". Reported-by: Dinar Valeev Signed-off-by: Andreas Färber Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5c31ad36bd..48ae09283d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -940,7 +940,10 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME); + if (bios_name == NULL) { + bios_name = FW_FILE_NAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); if (fw_size < 0) { hw_error("qemu: could not load LPAR rtas '%s'\n", filename); -- cgit v1.2.3 From 42e5b4c9884c2c69825d6b6c6ddfbf25290856db Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Sat, 6 Jul 2013 23:53:58 +1000 Subject: pseries: move interrupt controllers to hw/intc/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexey Kardashevskiy Reviewed-by: Andreas Färber Signed-off-by: Alexander Graf --- default-configs/ppc64-softmmu.mak | 2 + hw/intc/Makefile.objs | 1 + hw/intc/xics.c | 587 ++++++++++++++++++++++++++++++++++++++ hw/ppc/Makefile.objs | 2 +- hw/ppc/xics.c | 587 -------------------------------------- 5 files changed, 591 insertions(+), 588 deletions(-) create mode 100644 hw/intc/xics.c delete mode 100644 hw/ppc/xics.c diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 5a72b5f038..6d1933bbf9 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -45,5 +45,7 @@ CONFIG_OPENPIC=y CONFIG_PSERIES=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) +# For pSeries +CONFIG_XICS=$(CONFIG_PSERIES) # For PReP CONFIG_MC146818RTC=y diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 86f9d5b9df..2851eed25f 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -22,3 +22,4 @@ obj-$(CONFIG_IOAPIC) += ioapic.o obj-$(CONFIG_OMAP) += omap_intc.o obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_SH4) += sh_intc.o +obj-$(CONFIG_XICS) += xics.o diff --git a/hw/intc/xics.c b/hw/intc/xics.c new file mode 100644 index 0000000000..091912e2ca --- /dev/null +++ b/hw/intc/xics.c @@ -0,0 +1,587 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "hw/hw.h" +#include "trace.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/xics.h" + +/* + * ICP: Presentation layer + */ + +struct icp_server_state { + uint32_t xirr; + uint8_t pending_priority; + uint8_t mfrr; + qemu_irq output; +}; + +#define XISR_MASK 0x00ffffff +#define CPPR_MASK 0xff000000 + +#define XISR(ss) (((ss)->xirr) & XISR_MASK) +#define CPPR(ss) (((ss)->xirr) >> 24) + +struct ics_state; + +struct icp_state { + long nr_servers; + struct icp_server_state *ss; + struct ics_state *ics; +}; + +static void ics_reject(struct ics_state *ics, int nr); +static void ics_resend(struct ics_state *ics); +static void ics_eoi(struct ics_state *ics, int nr); + +static void icp_check_ipi(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { + return; + } + + trace_xics_icp_check_ipi(server, ss->mfrr); + + if (XISR(ss)) { + ics_reject(icp->ics, XISR(ss)); + } + + ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; + ss->pending_priority = ss->mfrr; + qemu_irq_raise(ss->output); +} + +static void icp_resend(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (ss->mfrr < CPPR(ss)) { + icp_check_ipi(icp, server); + } + ics_resend(icp->ics); +} + +static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr) +{ + struct icp_server_state *ss = icp->ss + server; + uint8_t old_cppr; + uint32_t old_xisr; + + old_cppr = CPPR(ss); + ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); + + if (cppr < old_cppr) { + if (XISR(ss) && (cppr <= ss->pending_priority)) { + old_xisr = XISR(ss); + ss->xirr &= ~XISR_MASK; /* Clear XISR */ + ss->pending_priority = 0xff; + qemu_irq_lower(ss->output); + ics_reject(icp->ics, old_xisr); + } + } else { + if (!XISR(ss)) { + icp_resend(icp, server); + } + } +} + +static void icp_set_mfrr(struct icp_state *icp, int server, uint8_t mfrr) +{ + struct icp_server_state *ss = icp->ss + server; + + ss->mfrr = mfrr; + if (mfrr < CPPR(ss)) { + icp_check_ipi(icp, server); + } +} + +static uint32_t icp_accept(struct icp_server_state *ss) +{ + uint32_t xirr = ss->xirr; + + qemu_irq_lower(ss->output); + ss->xirr = ss->pending_priority << 24; + ss->pending_priority = 0xff; + + trace_xics_icp_accept(xirr, ss->xirr); + + return xirr; +} + +static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr) +{ + struct icp_server_state *ss = icp->ss + server; + + /* Send EOI -> ICS */ + ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); + trace_xics_icp_eoi(server, xirr, ss->xirr); + ics_eoi(icp->ics, xirr & XISR_MASK); + if (!XISR(ss)) { + icp_resend(icp, server); + } +} + +static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority) +{ + struct icp_server_state *ss = icp->ss + server; + + trace_xics_icp_irq(server, nr, priority); + + if ((priority >= CPPR(ss)) + || (XISR(ss) && (ss->pending_priority <= priority))) { + ics_reject(icp->ics, nr); + } else { + if (XISR(ss)) { + ics_reject(icp->ics, XISR(ss)); + } + ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); + ss->pending_priority = priority; + trace_xics_icp_raise(ss->xirr, ss->pending_priority); + qemu_irq_raise(ss->output); + } +} + +/* + * ICS: Source layer + */ + +struct ics_irq_state { + int server; + uint8_t priority; + uint8_t saved_priority; +#define XICS_STATUS_ASSERTED 0x1 +#define XICS_STATUS_SENT 0x2 +#define XICS_STATUS_REJECTED 0x4 +#define XICS_STATUS_MASKED_PENDING 0x8 + uint8_t status; +}; + +struct ics_state { + int nr_irqs; + int offset; + qemu_irq *qirqs; + bool *islsi; + struct ics_irq_state *irqs; + struct icp_state *icp; +}; + +static int ics_valid_irq(struct ics_state *ics, uint32_t nr) +{ + return (nr >= ics->offset) + && (nr < (ics->offset + ics->nr_irqs)); +} + +static void resend_msi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + /* FIXME: filter by server#? */ + if (irq->status & XICS_STATUS_REJECTED) { + irq->status &= ~XICS_STATUS_REJECTED; + if (irq->priority != 0xff) { + icp_irq(ics->icp, irq->server, srcno + ics->offset, + irq->priority); + } + } +} + +static void resend_lsi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + if ((irq->priority != 0xff) + && (irq->status & XICS_STATUS_ASSERTED) + && !(irq->status & XICS_STATUS_SENT)) { + irq->status |= XICS_STATUS_SENT; + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); + } +} + +static void set_irq_msi(struct ics_state *ics, int srcno, int val) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + trace_xics_set_irq_msi(srcno, srcno + ics->offset); + + if (val) { + if (irq->priority == 0xff) { + irq->status |= XICS_STATUS_MASKED_PENDING; + trace_xics_masked_pending(); + } else { + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); + } + } +} + +static void set_irq_lsi(struct ics_state *ics, int srcno, int val) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + trace_xics_set_irq_lsi(srcno, srcno + ics->offset); + if (val) { + irq->status |= XICS_STATUS_ASSERTED; + } else { + irq->status &= ~XICS_STATUS_ASSERTED; + } + resend_lsi(ics, srcno); +} + +static void ics_set_irq(void *opaque, int srcno, int val) +{ + struct ics_state *ics = (struct ics_state *)opaque; + + if (ics->islsi[srcno]) { + set_irq_lsi(ics, srcno, val); + } else { + set_irq_msi(ics, srcno, val); + } +} + +static void write_xive_msi(struct ics_state *ics, int srcno) +{ + struct ics_irq_state *irq = ics->irqs + srcno; + + if (!(irq->status & XICS_STATUS_MASKED_PENDING) + || (irq->priority == 0xff)) { + return; + } + + irq->status &= ~XICS_STATUS_MASKED_PENDING; + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); +} + +static void write_xive_lsi(struct ics_state *ics, int srcno) +{ + resend_lsi(ics, srcno); +} + +static void ics_write_xive(struct ics_state *ics, int nr, int server, + uint8_t priority, uint8_t saved_priority) +{ + int srcno = nr - ics->offset; + struct ics_irq_state *irq = ics->irqs + srcno; + + irq->server = server; + irq->priority = priority; + irq->saved_priority = saved_priority; + + trace_xics_ics_write_xive(nr, srcno, server, priority); + + if (ics->islsi[srcno]) { + write_xive_lsi(ics, srcno); + } else { + write_xive_msi(ics, srcno); + } +} + +static void ics_reject(struct ics_state *ics, int nr) +{ + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + + trace_xics_ics_reject(nr, nr - ics->offset); + irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ + irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ +} + +static void ics_resend(struct ics_state *ics) +{ + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + /* FIXME: filter by server#? */ + if (ics->islsi[i]) { + resend_lsi(ics, i); + } else { + resend_msi(ics, i); + } + } +} + +static void ics_eoi(struct ics_state *ics, int nr) +{ + int srcno = nr - ics->offset; + struct ics_irq_state *irq = ics->irqs + srcno; + + trace_xics_ics_eoi(nr); + + if (ics->islsi[srcno]) { + irq->status &= ~XICS_STATUS_SENT; + } +} + +/* + * Exported functions + */ + +qemu_irq xics_get_qirq(struct icp_state *icp, int irq) +{ + if (!ics_valid_irq(icp->ics, irq)) { + return NULL; + } + + return icp->ics->qirqs[irq - icp->ics->offset]; +} + +void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi) +{ + assert(ics_valid_irq(icp->ics, irq)); + + icp->ics->islsi[irq - icp->ics->offset] = lsi; +} + +static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + target_ulong cppr = args[0]; + + icp_set_cppr(spapr->icp, cs->cpu_index, cppr); + return H_SUCCESS; +} + +static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong server = args[0]; + target_ulong mfrr = args[1]; + + if (server >= spapr->icp->nr_servers) { + return H_PARAMETER; + } + + icp_set_mfrr(spapr->icp, server, mfrr); + return H_SUCCESS; +} + +static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); + + args[0] = xirr; + return H_SUCCESS; +} + +static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + target_ulong xirr = args[0]; + + icp_eoi(spapr->icp, cs->cpu_index, xirr); + return H_SUCCESS; +} + +static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr, server, priority; + + if ((nargs != 3) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + server = rtas_ld(args, 1); + priority = rtas_ld(args, 2); + + if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) + || (priority > 0xff)) { + rtas_st(rets, 0, -3); + return; + } + + ics_write_xive(ics, nr, server, priority, priority); + + rtas_st(rets, 0, 0); /* Success */ +} + +static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 3)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); + rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); +} + +static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, + ics->irqs[nr - ics->offset].priority); + + rtas_st(rets, 0, 0); /* Success */ +} + +static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, + ics->irqs[nr - ics->offset].saved_priority, + ics->irqs[nr - ics->offset].saved_priority); + + rtas_st(rets, 0, 0); /* Success */ +} + +static void xics_reset(void *opaque) +{ + struct icp_state *icp = (struct icp_state *)opaque; + struct ics_state *ics = icp->ics; + int i; + + for (i = 0; i < icp->nr_servers; i++) { + icp->ss[i].xirr = 0; + icp->ss[i].pending_priority = 0xff; + icp->ss[i].mfrr = 0xff; + /* Make all outputs are deasserted */ + qemu_set_irq(icp->ss[i].output, 0); + } + + memset(ics->irqs, 0, sizeof(struct ics_irq_state) * ics->nr_irqs); + for (i = 0; i < ics->nr_irqs; i++) { + ics->irqs[i].priority = 0xff; + ics->irqs[i].saved_priority = 0xff; + } +} + +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct icp_server_state *ss = &icp->ss[cs->cpu_index]; + + assert(cs->cpu_index < icp->nr_servers); + + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + fprintf(stderr, "XICS interrupt controller does not support this CPU " + "bus model\n"); + abort(); + } +} + +struct icp_state *xics_system_init(int nr_servers, int nr_irqs) +{ + struct icp_state *icp; + struct ics_state *ics; + + icp = g_malloc0(sizeof(*icp)); + icp->nr_servers = nr_servers; + icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); + + ics = g_malloc0(sizeof(*ics)); + ics->nr_irqs = nr_irqs; + ics->offset = XICS_IRQ_BASE; + ics->irqs = g_malloc0(nr_irqs * sizeof(struct ics_irq_state)); + ics->islsi = g_malloc0(nr_irqs * sizeof(bool)); + + icp->ics = ics; + ics->icp = icp; + + ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs); + + spapr_register_hypercall(H_CPPR, h_cppr); + spapr_register_hypercall(H_IPI, h_ipi); + spapr_register_hypercall(H_XIRR, h_xirr); + spapr_register_hypercall(H_EOI, h_eoi); + + spapr_rtas_register("ibm,set-xive", rtas_set_xive); + spapr_rtas_register("ibm,get-xive", rtas_get_xive); + spapr_rtas_register("ibm,int-off", rtas_int_off); + spapr_rtas_register("ibm,int-on", rtas_int_on); + + qemu_register_reset(xics_reset, icp); + + return icp; +} diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index be00d1da3b..7a1cd5d89e 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -1,7 +1,7 @@ # shared objects obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) -obj-$(CONFIG_PSERIES) += spapr.o xics.o spapr_vio.o spapr_events.o +obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o # PowerPC 4xx boards diff --git a/hw/ppc/xics.c b/hw/ppc/xics.c deleted file mode 100644 index 091912e2ca..0000000000 --- a/hw/ppc/xics.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics - * - * Copyright (c) 2010,2011 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "hw/hw.h" -#include "trace.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/xics.h" - -/* - * ICP: Presentation layer - */ - -struct icp_server_state { - uint32_t xirr; - uint8_t pending_priority; - uint8_t mfrr; - qemu_irq output; -}; - -#define XISR_MASK 0x00ffffff -#define CPPR_MASK 0xff000000 - -#define XISR(ss) (((ss)->xirr) & XISR_MASK) -#define CPPR(ss) (((ss)->xirr) >> 24) - -struct ics_state; - -struct icp_state { - long nr_servers; - struct icp_server_state *ss; - struct ics_state *ics; -}; - -static void ics_reject(struct ics_state *ics, int nr); -static void ics_resend(struct ics_state *ics); -static void ics_eoi(struct ics_state *ics, int nr); - -static void icp_check_ipi(struct icp_state *icp, int server) -{ - struct icp_server_state *ss = icp->ss + server; - - if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { - return; - } - - trace_xics_icp_check_ipi(server, ss->mfrr); - - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); - } - - ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; - ss->pending_priority = ss->mfrr; - qemu_irq_raise(ss->output); -} - -static void icp_resend(struct icp_state *icp, int server) -{ - struct icp_server_state *ss = icp->ss + server; - - if (ss->mfrr < CPPR(ss)) { - icp_check_ipi(icp, server); - } - ics_resend(icp->ics); -} - -static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr) -{ - struct icp_server_state *ss = icp->ss + server; - uint8_t old_cppr; - uint32_t old_xisr; - - old_cppr = CPPR(ss); - ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); - - if (cppr < old_cppr) { - if (XISR(ss) && (cppr <= ss->pending_priority)) { - old_xisr = XISR(ss); - ss->xirr &= ~XISR_MASK; /* Clear XISR */ - ss->pending_priority = 0xff; - qemu_irq_lower(ss->output); - ics_reject(icp->ics, old_xisr); - } - } else { - if (!XISR(ss)) { - icp_resend(icp, server); - } - } -} - -static void icp_set_mfrr(struct icp_state *icp, int server, uint8_t mfrr) -{ - struct icp_server_state *ss = icp->ss + server; - - ss->mfrr = mfrr; - if (mfrr < CPPR(ss)) { - icp_check_ipi(icp, server); - } -} - -static uint32_t icp_accept(struct icp_server_state *ss) -{ - uint32_t xirr = ss->xirr; - - qemu_irq_lower(ss->output); - ss->xirr = ss->pending_priority << 24; - ss->pending_priority = 0xff; - - trace_xics_icp_accept(xirr, ss->xirr); - - return xirr; -} - -static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr) -{ - struct icp_server_state *ss = icp->ss + server; - - /* Send EOI -> ICS */ - ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); - trace_xics_icp_eoi(server, xirr, ss->xirr); - ics_eoi(icp->ics, xirr & XISR_MASK); - if (!XISR(ss)) { - icp_resend(icp, server); - } -} - -static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority) -{ - struct icp_server_state *ss = icp->ss + server; - - trace_xics_icp_irq(server, nr, priority); - - if ((priority >= CPPR(ss)) - || (XISR(ss) && (ss->pending_priority <= priority))) { - ics_reject(icp->ics, nr); - } else { - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); - } - ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); - ss->pending_priority = priority; - trace_xics_icp_raise(ss->xirr, ss->pending_priority); - qemu_irq_raise(ss->output); - } -} - -/* - * ICS: Source layer - */ - -struct ics_irq_state { - int server; - uint8_t priority; - uint8_t saved_priority; -#define XICS_STATUS_ASSERTED 0x1 -#define XICS_STATUS_SENT 0x2 -#define XICS_STATUS_REJECTED 0x4 -#define XICS_STATUS_MASKED_PENDING 0x8 - uint8_t status; -}; - -struct ics_state { - int nr_irqs; - int offset; - qemu_irq *qirqs; - bool *islsi; - struct ics_irq_state *irqs; - struct icp_state *icp; -}; - -static int ics_valid_irq(struct ics_state *ics, uint32_t nr) -{ - return (nr >= ics->offset) - && (nr < (ics->offset + ics->nr_irqs)); -} - -static void resend_msi(struct ics_state *ics, int srcno) -{ - struct ics_irq_state *irq = ics->irqs + srcno; - - /* FIXME: filter by server#? */ - if (irq->status & XICS_STATUS_REJECTED) { - irq->status &= ~XICS_STATUS_REJECTED; - if (irq->priority != 0xff) { - icp_irq(ics->icp, irq->server, srcno + ics->offset, - irq->priority); - } - } -} - -static void resend_lsi(struct ics_state *ics, int srcno) -{ - struct ics_irq_state *irq = ics->irqs + srcno; - - if ((irq->priority != 0xff) - && (irq->status & XICS_STATUS_ASSERTED) - && !(irq->status & XICS_STATUS_SENT)) { - irq->status |= XICS_STATUS_SENT; - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); - } -} - -static void set_irq_msi(struct ics_state *ics, int srcno, int val) -{ - struct ics_irq_state *irq = ics->irqs + srcno; - - trace_xics_set_irq_msi(srcno, srcno + ics->offset); - - if (val) { - if (irq->priority == 0xff) { - irq->status |= XICS_STATUS_MASKED_PENDING; - trace_xics_masked_pending(); - } else { - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); - } - } -} - -static void set_irq_lsi(struct ics_state *ics, int srcno, int val) -{ - struct ics_irq_state *irq = ics->irqs + srcno; - - trace_xics_set_irq_lsi(srcno, srcno + ics->offset); - if (val) { - irq->status |= XICS_STATUS_ASSERTED; - } else { - irq->status &= ~XICS_STATUS_ASSERTED; - } - resend_lsi(ics, srcno); -} - -static void ics_set_irq(void *opaque, int srcno, int val) -{ - struct ics_state *ics = (struct ics_state *)opaque; - - if (ics->islsi[srcno]) { - set_irq_lsi(ics, srcno, val); - } else { - set_irq_msi(ics, srcno, val); - } -} - -static void write_xive_msi(struct ics_state *ics, int srcno) -{ - struct ics_irq_state *irq = ics->irqs + srcno; - - if (!(irq->status & XICS_STATUS_MASKED_PENDING) - || (irq->priority == 0xff)) { - return; - } - - irq->status &= ~XICS_STATUS_MASKED_PENDING; - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); -} - -static void write_xive_lsi(struct ics_state *ics, int srcno) -{ - resend_lsi(ics, srcno); -} - -static void ics_write_xive(struct ics_state *ics, int nr, int server, - uint8_t priority, uint8_t saved_priority) -{ - int srcno = nr - ics->offset; - struct ics_irq_state *irq = ics->irqs + srcno; - - irq->server = server; - irq->priority = priority; - irq->saved_priority = saved_priority; - - trace_xics_ics_write_xive(nr, srcno, server, priority); - - if (ics->islsi[srcno]) { - write_xive_lsi(ics, srcno); - } else { - write_xive_msi(ics, srcno); - } -} - -static void ics_reject(struct ics_state *ics, int nr) -{ - struct ics_irq_state *irq = ics->irqs + nr - ics->offset; - - trace_xics_ics_reject(nr, nr - ics->offset); - irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ - irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ -} - -static void ics_resend(struct ics_state *ics) -{ - int i; - - for (i = 0; i < ics->nr_irqs; i++) { - /* FIXME: filter by server#? */ - if (ics->islsi[i]) { - resend_lsi(ics, i); - } else { - resend_msi(ics, i); - } - } -} - -static void ics_eoi(struct ics_state *ics, int nr) -{ - int srcno = nr - ics->offset; - struct ics_irq_state *irq = ics->irqs + srcno; - - trace_xics_ics_eoi(nr); - - if (ics->islsi[srcno]) { - irq->status &= ~XICS_STATUS_SENT; - } -} - -/* - * Exported functions - */ - -qemu_irq xics_get_qirq(struct icp_state *icp, int irq) -{ - if (!ics_valid_irq(icp->ics, irq)) { - return NULL; - } - - return icp->ics->qirqs[irq - icp->ics->offset]; -} - -void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi) -{ - assert(ics_valid_irq(icp->ics, irq)); - - icp->ics->islsi[irq - icp->ics->offset] = lsi; -} - -static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - target_ulong cppr = args[0]; - - icp_set_cppr(spapr->icp, cs->cpu_index, cppr); - return H_SUCCESS; -} - -static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong server = args[0]; - target_ulong mfrr = args[1]; - - if (server >= spapr->icp->nr_servers) { - return H_PARAMETER; - } - - icp_set_mfrr(spapr->icp, server, mfrr); - return H_SUCCESS; -} - -static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); - - args[0] = xirr; - return H_SUCCESS; -} - -static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - target_ulong xirr = args[0]; - - icp_eoi(spapr->icp, cs->cpu_index, xirr); - return H_SUCCESS; -} - -static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct ics_state *ics = spapr->icp->ics; - uint32_t nr, server, priority; - - if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, -3); - return; - } - - nr = rtas_ld(args, 0); - server = rtas_ld(args, 1); - priority = rtas_ld(args, 2); - - if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) - || (priority > 0xff)) { - rtas_st(rets, 0, -3); - return; - } - - ics_write_xive(ics, nr, server, priority, priority); - - rtas_st(rets, 0, 0); /* Success */ -} - -static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct ics_state *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 3)) { - rtas_st(rets, 0, -3); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); - return; - } - - rtas_st(rets, 0, 0); /* Success */ - rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); - rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); -} - -static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct ics_state *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, -3); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); - return; - } - - ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, - ics->irqs[nr - ics->offset].priority); - - rtas_st(rets, 0, 0); /* Success */ -} - -static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct ics_state *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, -3); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); - return; - } - - ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, - ics->irqs[nr - ics->offset].saved_priority, - ics->irqs[nr - ics->offset].saved_priority); - - rtas_st(rets, 0, 0); /* Success */ -} - -static void xics_reset(void *opaque) -{ - struct icp_state *icp = (struct icp_state *)opaque; - struct ics_state *ics = icp->ics; - int i; - - for (i = 0; i < icp->nr_servers; i++) { - icp->ss[i].xirr = 0; - icp->ss[i].pending_priority = 0xff; - icp->ss[i].mfrr = 0xff; - /* Make all outputs are deasserted */ - qemu_set_irq(icp->ss[i].output, 0); - } - - memset(ics->irqs, 0, sizeof(struct ics_irq_state) * ics->nr_irqs); - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - } -} - -void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - struct icp_server_state *ss = &icp->ss[cs->cpu_index]; - - assert(cs->cpu_index < icp->nr_servers); - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; - - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; - - default: - fprintf(stderr, "XICS interrupt controller does not support this CPU " - "bus model\n"); - abort(); - } -} - -struct icp_state *xics_system_init(int nr_servers, int nr_irqs) -{ - struct icp_state *icp; - struct ics_state *ics; - - icp = g_malloc0(sizeof(*icp)); - icp->nr_servers = nr_servers; - icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); - - ics = g_malloc0(sizeof(*ics)); - ics->nr_irqs = nr_irqs; - ics->offset = XICS_IRQ_BASE; - ics->irqs = g_malloc0(nr_irqs * sizeof(struct ics_irq_state)); - ics->islsi = g_malloc0(nr_irqs * sizeof(bool)); - - icp->ics = ics; - ics->icp = icp; - - ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs); - - spapr_register_hypercall(H_CPPR, h_cppr); - spapr_register_hypercall(H_IPI, h_ipi); - spapr_register_hypercall(H_XIRR, h_xirr); - spapr_register_hypercall(H_EOI, h_eoi); - - spapr_rtas_register("ibm,set-xive", rtas_set_xive); - spapr_rtas_register("ibm,get-xive", rtas_get_xive); - spapr_rtas_register("ibm,int-off", rtas_int_off); - spapr_rtas_register("ibm,int-on", rtas_int_on); - - qemu_register_reset(xics_reset, icp); - - return icp; -} -- cgit v1.2.3 From 8d43ea1c97d0d611f9cf868fe78b17f2a1031298 Mon Sep 17 00:00:00 2001 From: Prerna Saxena Date: Thu, 4 Jul 2013 12:12:32 +0530 Subject: target-ppc: Add POWER8 v1.0 CPU model This patch adds CPU PVR definition for POWER8, and enables QEMU to launch guests on POWER8 hardware. Signed-off-by: Prerna Saxena Signed-off-by: Alexey Kardashevskiy Reviewed-by: Paul Mackerras Reviewed-by: Andreas Farber Signed-off-by: Alexander Graf --- target-ppc/cpu-models.c | 3 +++ target-ppc/cpu-models.h | 1 + target-ppc/translate_init.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 13dd670218..9578ed8891 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1143,6 +1143,8 @@ "POWER7 v2.1") POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, "POWER7 v2.3") + POWERPC_DEF("POWER8_v1.0", CPU_POWERPC_POWER8_v10, POWER8, + "POWER8 v1.0") POWERPC_DEF("970", CPU_POWERPC_970, 970, "PowerPC 970") POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970FX, @@ -1388,6 +1390,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "Dino", "POWER3" }, { "POWER3+", "631" }, { "POWER7", "POWER7_v2.3" }, + { "POWER8", "POWER8_v1.0" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, { "Apache", "RS64" }, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index fcd3a151d6..01e488f71c 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -556,6 +556,7 @@ enum { CPU_POWERPC_POWER7_v20 = 0x003F0200, CPU_POWERPC_POWER7_v21 = 0x003F0201, CPU_POWERPC_POWER7_v23 = 0x003F0203, + CPU_POWERPC_POWER8_v10 = 0x004B0100, CPU_POWERPC_970 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 76483d9aa2..79bfcd8df9 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7136,6 +7136,40 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; } + +POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "POWER8"; + pcc->init_proc = init_proc_POWER7; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX; + pcc->msr_mask = 0x800000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_2_06; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif + pcc->excp_model = POWERPC_EXCP_POWER7; + pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; +} #endif /* defined (TARGET_PPC64) */ -- cgit v1.2.3 From 536d8cda4adef3d202a800688ed5f63dae1f0f88 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 29 Jun 2013 17:34:58 +0200 Subject: PPC: Mac: Fix guest exported tbfreq values We can tell the guest the frequency of its time base through fwcfg. However, we tell it a different value from the speed tb actually runs at. Let's fix it and make the tbfreq initialization and the fwcfg exposure use the same values. Signed-off-by: Alexander Graf --- hw/ppc/mac_newworld.c | 5 +++-- hw/ppc/mac_oldworld.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 77a8c2e28b..fe803480a7 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -71,6 +71,7 @@ #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 +#define TBFREQ (100UL * 1000UL * 1000UL) /* debug UniNorth */ //#define DEBUG_UNIN @@ -191,7 +192,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) env = &cpu->env; /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + cpu_ppc_tb_init(env, TBFREQ); qemu_register_reset(ppc_core99_reset, cpu); } @@ -460,7 +461,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); #endif } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ); } /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, 266000000); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 4663ed2730..b607471485 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -45,6 +45,7 @@ #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 +#define TBFREQ 16600000UL static int fw_cfg_boot_set(void *opaque, const char *boot_device) { @@ -114,7 +115,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) env = &cpu->env; /* Set time-base frequency to 16.6 Mhz */ - cpu_ppc_tb_init(env, 16600000UL); + cpu_ppc_tb_init(env, TBFREQ); qemu_register_reset(ppc_heathrow_reset, cpu); } @@ -331,7 +332,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); #endif } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ); } /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, 266000000); -- cgit v1.2.3 From 14eefd0ec3b4720faddf4cc7682d7ac8c82a3a45 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 24 Jun 2013 21:40:50 +0200 Subject: PPC: g3beige: Move secondary IDE bus to mac-io On a real G3 Beige the secondary IDE bus lives on the mac-io chip, not on some random PCI device. Move it there to become more compatible. While at it, also clean up the IDE channel connection logic. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 2 +- hw/misc/macio/macio.c | 93 ++++++++++++++++++++++++++++++--------------------- hw/ppc/mac_oldworld.c | 17 +++++----- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 479820239e..0b05a74e45 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -363,7 +363,7 @@ static void macio_ide_register_types(void) type_register_static(&macio_ide_type_info); } -/* hd_table must contain 4 block drivers */ +/* hd_table must contain 2 block drivers */ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) { int i; diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 76a1cfba32..6459bc1325 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -52,10 +52,10 @@ typedef struct OldWorldMacIOState { MacIOState parent_obj; /*< public >*/ - qemu_irq irqs[3]; + qemu_irq irqs[5]; MacIONVRAMState nvram; - MACIOIDEState ide; + MACIOIDEState ide[2]; } OldWorldMacIOState; #define NEWWORLD_MACIO(obj) \ @@ -147,18 +147,32 @@ static int macio_common_initfn(PCIDevice *d) return 0; } +static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0, + qemu_irq irq1, int dmaid) +{ + SysBusDevice *sysbus_dev; + + sysbus_dev = SYS_BUS_DEVICE(ide); + sysbus_connect_irq(sysbus_dev, 0, irq0); + sysbus_connect_irq(sysbus_dev, 1, irq1); + macio_ide_register_dma(ide, s->dbdma, dmaid); + return qdev_init(DEVICE(ide)); +} + static int macio_oldworld_initfn(PCIDevice *d) { MacIOState *s = MACIO(d); OldWorldMacIOState *os = OLDWORLD_MACIO(d); SysBusDevice *sysbus_dev; + int i; + int cur_irq = 0; int ret = macio_common_initfn(d); if (ret < 0) { return ret; } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); ret = qdev_init(DEVICE(&os->nvram)); if (ret < 0) { @@ -174,23 +188,39 @@ static int macio_oldworld_initfn(PCIDevice *d) memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); } - sysbus_dev = SYS_BUS_DEVICE(&os->ide); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]); - sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]); - macio_ide_register_dma(&os->ide, s->dbdma, 0x16); - ret = qdev_init(DEVICE(&os->ide)); - if (ret < 0) { - return ret; + /* IDE buses */ + for (i = 0; i < ARRAY_SIZE(os->ide); i++) { + qemu_irq irq0 = os->irqs[cur_irq++]; + qemu_irq irq1 = os->irqs[cur_irq++]; + + ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4)); + if (ret < 0) { + return ret; + } } return 0; } +static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, int index) +{ + gchar *name; + + object_initialize(ide, TYPE_MACIO_IDE); + qdev_set_parent_bus(DEVICE(ide), sysbus_get_default()); + memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), + &ide->mem); + name = g_strdup_printf("ide[%i]", index); + object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL); + g_free(name); +} + static void macio_oldworld_init(Object *obj) { MacIOState *s = MACIO(obj); OldWorldMacIOState *os = OLDWORLD_MACIO(obj); DeviceState *dev; + int i; qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); @@ -199,10 +229,9 @@ static void macio_oldworld_init(Object *obj) qdev_prop_set_uint32(dev, "size", 0x2000); qdev_prop_set_uint32(dev, "it_shift", 4); - object_initialize(&os->ide, TYPE_MACIO_IDE); - qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default()); - memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem); - object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL); + for (i = 0; i < 2; i++) { + macio_init_ide(s, &os->ide[i], i); + } } static int macio_newworld_initfn(PCIDevice *d) @@ -210,35 +239,30 @@ static int macio_newworld_initfn(PCIDevice *d) MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); SysBusDevice *sysbus_dev; + int i; + int cur_irq = 0; int ret = macio_common_initfn(d); if (ret < 0) { return ret; } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); if (s->pic_mem) { /* OpenPIC */ memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); } - sysbus_dev = SYS_BUS_DEVICE(&ns->ide[0]); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]); - sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]); - macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16); - ret = qdev_init(DEVICE(&ns->ide[0])); - if (ret < 0) { - return ret; - } + /* IDE buses */ + for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { + qemu_irq irq0 = ns->irqs[cur_irq++]; + qemu_irq irq1 = ns->irqs[cur_irq++]; - sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]); - sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]); - macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a); - ret = qdev_init(DEVICE(&ns->ide[1])); - if (ret < 0) { - return ret; + ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4)); + if (ret < 0) { + return ret; + } } return 0; @@ -249,18 +273,11 @@ static void macio_newworld_init(Object *obj) MacIOState *s = MACIO(obj); NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); int i; - gchar *name; qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); for (i = 0; i < 2; i++) { - object_initialize(&ns->ide[i], TYPE_MACIO_IDE); - qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default()); - memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000), - &ns->ide[i].mem); - name = g_strdup_printf("ide[%i]", i); - object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL); - g_free(name); + macio_init_ide(s, &ns->ide[i], i); } } diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index b607471485..8b8c6b93a5 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -268,20 +268,19 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); dev = DEVICE(macio); qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE-0 */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */ + qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */ + qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */ macio_init(macio, pic_mem, escc_bar); - /* First IDE channel is a MAC IDE on the MacIO bus */ macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide")); + "ide[0]")); macio_ide_init_drives(macio_ide, hd); - /* Second IDE channel is a CMD646 on the PCI bus */ - hd[0] = hd[MAX_IDE_DEVS]; - hd[1] = hd[MAX_IDE_DEVS + 1]; - hd[3] = hd[2] = NULL; - pci_cmd646_ide_init(pci_bus, hd, 0); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[1]")); + macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); adb_bus = qdev_get_child_bus(dev, "adb.0"); -- cgit v1.2.3 From 8aef291fb876670b264297ab333f5072cddbf625 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 01:43:17 +0200 Subject: PPC: Macio: Replace tabs with spaces s/^I/ /g on the file. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 0b05a74e45..60b64ac22b 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -55,7 +55,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) s->packet_transfer_size -= s->io_buffer_size; s->io_buffer_index += s->io_buffer_size; - s->lba += s->io_buffer_index >> 11; + s->lba += s->io_buffer_index >> 11; s->io_buffer_index &= 0x7ff; } @@ -97,7 +97,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (ret < 0) { m->aiocb = NULL; qemu_sglist_destroy(&s->sg); - ide_dma_error(s); + ide_dma_error(s); goto done; } @@ -136,11 +136,11 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) switch (s->dma_cmd) { case IDE_DMA_READ: m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, - pmac_ide_transfer_cb, io); + pmac_ide_transfer_cb, io); break; case IDE_DMA_WRITE: m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, - pmac_ide_transfer_cb, io); + pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: m->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num, -- cgit v1.2.3 From 9e232428784641b8e48a237a4cb2ffe84450d15c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 01:45:22 +0200 Subject: PPC: dbdma: Replace tabs with spaces s/^I/ /g on the file with a few manual tweaks to align things. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 102 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index a1d7862a66..fc46727762 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -85,75 +85,75 @@ /* Bits in control and status registers */ -#define RUN 0x8000 -#define PAUSE 0x4000 -#define FLUSH 0x2000 -#define WAKE 0x1000 -#define DEAD 0x0800 -#define ACTIVE 0x0400 -#define BT 0x0100 -#define DEVSTAT 0x00ff +#define RUN 0x8000 +#define PAUSE 0x4000 +#define FLUSH 0x2000 +#define WAKE 0x1000 +#define DEAD 0x0800 +#define ACTIVE 0x0400 +#define BT 0x0100 +#define DEVSTAT 0x00ff /* * DBDMA command structure. These fields are all little-endian! */ typedef struct dbdma_cmd { - uint16_t req_count; /* requested byte transfer count */ - uint16_t command; /* command word (has bit-fields) */ - uint32_t phy_addr; /* physical data address */ - uint32_t cmd_dep; /* command-dependent field */ - uint16_t res_count; /* residual count after completion */ - uint16_t xfer_status; /* transfer status */ + uint16_t req_count; /* requested byte transfer count */ + uint16_t command; /* command word (has bit-fields) */ + uint32_t phy_addr; /* physical data address */ + uint32_t cmd_dep; /* command-dependent field */ + uint16_t res_count; /* residual count after completion */ + uint16_t xfer_status; /* transfer status */ } dbdma_cmd; /* DBDMA command values in command field */ #define COMMAND_MASK 0xf000 -#define OUTPUT_MORE 0x0000 /* transfer memory data to stream */ -#define OUTPUT_LAST 0x1000 /* ditto followed by end marker */ -#define INPUT_MORE 0x2000 /* transfer stream data to memory */ -#define INPUT_LAST 0x3000 /* ditto, expect end marker */ -#define STORE_WORD 0x4000 /* write word (4 bytes) to device reg */ -#define LOAD_WORD 0x5000 /* read word (4 bytes) from device reg */ -#define DBDMA_NOP 0x6000 /* do nothing */ -#define DBDMA_STOP 0x7000 /* suspend processing */ +#define OUTPUT_MORE 0x0000 /* transfer memory data to stream */ +#define OUTPUT_LAST 0x1000 /* ditto followed by end marker */ +#define INPUT_MORE 0x2000 /* transfer stream data to memory */ +#define INPUT_LAST 0x3000 /* ditto, expect end marker */ +#define STORE_WORD 0x4000 /* write word (4 bytes) to device reg */ +#define LOAD_WORD 0x5000 /* read word (4 bytes) from device reg */ +#define DBDMA_NOP 0x6000 /* do nothing */ +#define DBDMA_STOP 0x7000 /* suspend processing */ /* Key values in command field */ #define KEY_MASK 0x0700 -#define KEY_STREAM0 0x0000 /* usual data stream */ -#define KEY_STREAM1 0x0100 /* control/status stream */ -#define KEY_STREAM2 0x0200 /* device-dependent stream */ -#define KEY_STREAM3 0x0300 /* device-dependent stream */ -#define KEY_STREAM4 0x0400 /* reserved */ -#define KEY_REGS 0x0500 /* device register space */ -#define KEY_SYSTEM 0x0600 /* system memory-mapped space */ -#define KEY_DEVICE 0x0700 /* device memory-mapped space */ +#define KEY_STREAM0 0x0000 /* usual data stream */ +#define KEY_STREAM1 0x0100 /* control/status stream */ +#define KEY_STREAM2 0x0200 /* device-dependent stream */ +#define KEY_STREAM3 0x0300 /* device-dependent stream */ +#define KEY_STREAM4 0x0400 /* reserved */ +#define KEY_REGS 0x0500 /* device register space */ +#define KEY_SYSTEM 0x0600 /* system memory-mapped space */ +#define KEY_DEVICE 0x0700 /* device memory-mapped space */ /* Interrupt control values in command field */ #define INTR_MASK 0x0030 -#define INTR_NEVER 0x0000 /* don't interrupt */ -#define INTR_IFSET 0x0010 /* intr if condition bit is 1 */ -#define INTR_IFCLR 0x0020 /* intr if condition bit is 0 */ -#define INTR_ALWAYS 0x0030 /* always interrupt */ +#define INTR_NEVER 0x0000 /* don't interrupt */ +#define INTR_IFSET 0x0010 /* intr if condition bit is 1 */ +#define INTR_IFCLR 0x0020 /* intr if condition bit is 0 */ +#define INTR_ALWAYS 0x0030 /* always interrupt */ /* Branch control values in command field */ #define BR_MASK 0x000c -#define BR_NEVER 0x0000 /* don't branch */ -#define BR_IFSET 0x0004 /* branch if condition bit is 1 */ -#define BR_IFCLR 0x0008 /* branch if condition bit is 0 */ -#define BR_ALWAYS 0x000c /* always branch */ +#define BR_NEVER 0x0000 /* don't branch */ +#define BR_IFSET 0x0004 /* branch if condition bit is 1 */ +#define BR_IFCLR 0x0008 /* branch if condition bit is 0 */ +#define BR_ALWAYS 0x000c /* always branch */ /* Wait control values in command field */ #define WAIT_MASK 0x0003 -#define WAIT_NEVER 0x0000 /* don't wait */ -#define WAIT_IFSET 0x0001 /* wait if condition bit is 1 */ -#define WAIT_IFCLR 0x0002 /* wait if condition bit is 0 */ -#define WAIT_ALWAYS 0x0003 /* always wait */ +#define WAIT_NEVER 0x0000 /* don't wait */ +#define WAIT_IFSET 0x0001 /* wait if condition bit is 1 */ +#define WAIT_IFCLR 0x0002 /* wait if condition bit is 0 */ +#define WAIT_ALWAYS 0x0003 /* always wait */ typedef struct DBDMA_channel { int channel; @@ -558,11 +558,11 @@ static void channel_run(DBDMA_channel *ch) switch (cmd) { case DBDMA_NOP: nop(ch); - return; + return; case DBDMA_STOP: stop(ch); - return; + return; } key = le16_to_cpu(current->command) & 0x0700; @@ -578,19 +578,19 @@ static void channel_run(DBDMA_channel *ch) switch (cmd) { case OUTPUT_MORE: start_output(ch, key, phy_addr, req_count, 0); - return; + return; case OUTPUT_LAST: start_output(ch, key, phy_addr, req_count, 1); - return; + return; case INPUT_MORE: start_input(ch, key, phy_addr, req_count, 0); - return; + return; case INPUT_LAST: start_input(ch, key, phy_addr, req_count, 1); - return; + return; } if (key < KEY_REGS) { @@ -615,11 +615,11 @@ static void channel_run(DBDMA_channel *ch) switch (cmd) { case LOAD_WORD: load_word(ch, key, phy_addr, req_count); - return; + return; case STORE_WORD: store_word(ch, key, phy_addr, req_count); - return; + return; } } @@ -720,7 +720,7 @@ static void dbdma_write(void *opaque, hwaddr addr, if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE))) - return; + return; ch->regs[reg] = value; -- cgit v1.2.3 From 33ce36bb33cc00ac3070d49e17b4afed62b412a8 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 01:23:45 +0200 Subject: PPC: Mac: Add debug prints in macio and dbdma code The macio code is basically undebuggable as it stands today, with no debug prints anywhere whatsoever. DBDMA was better, but I needed a few more to create reasonable logs that tell me where breakage is. Add a DPRINTF macro in the macio source file and add a bunch of debug prints that are all disabled by default of course. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 43 ++++++++++++++++++++++++++++++++++++++++--- hw/misc/macio/mac_dbdma.c | 14 +++++++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 60b64ac22b..8d42b5c2bb 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -30,6 +30,22 @@ #include +/* debug MACIO */ +// #define DEBUG_MACIO + +#ifdef DEBUG_MACIO +static const int debug_macio = 1; +#else +static const int debug_macio = 0; +#endif + +#define MACIO_DPRINTF(fmt, ...) do { \ + if (debug_macio) { \ + printf(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + + /***********************************************************/ /* MacIO based PowerPC IDE */ @@ -48,6 +64,8 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) goto done; } + MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size); + if (s->io_buffer_size > 0) { m->aiocb = NULL; qemu_sglist_destroy(&s->sg); @@ -59,15 +77,20 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) s->io_buffer_index &= 0x7ff; } - if (s->packet_transfer_size <= 0) + if (s->packet_transfer_size <= 0) { + MACIO_DPRINTF("end of transfer\n"); ide_atapi_cmd_ok(s); + } if (io->len == 0) { + MACIO_DPRINTF("end of DMA\n"); goto done; } /* launch next transfer */ + MACIO_DPRINTF("io->len = %#x\n", io->len); + s->io_buffer_size = io->len; qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, @@ -76,12 +99,17 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) io->addr += io->len; io->len = 0; + MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n", + (s->lba << 2) + (s->io_buffer_index >> 9), + s->packet_transfer_size, s->dma_cmd); + m->aiocb = dma_bdrv_read(s->bs, &s->sg, (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9), pmac_ide_atapi_transfer_cb, io); return; done: + MACIO_DPRINTF("done DMA\n"); bdrv_acct_done(s->bs, &s->acct); io->dma_end(opaque); } @@ -95,6 +123,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) int64_t sector_num; if (ret < 0) { + MACIO_DPRINTF("DMA error\n"); m->aiocb = NULL; qemu_sglist_destroy(&s->sg); ide_dma_error(s); @@ -102,6 +131,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) } sector_num = ide_get_sector(s); + MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size); if (s->io_buffer_size > 0) { m->aiocb = NULL; qemu_sglist_destroy(&s->sg); @@ -111,14 +141,14 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->nsector -= n; } - /* end of transfer ? */ if (s->nsector == 0) { + MACIO_DPRINTF("end of transfer\n"); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); } - /* end of DMA ? */ if (io->len == 0) { + MACIO_DPRINTF("end of DMA\n"); goto done; } @@ -127,12 +157,17 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->io_buffer_index = 0; s->io_buffer_size = io->len; + MACIO_DPRINTF("io->len = %#x\n", io->len); + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, &address_space_memory); qemu_sglist_add(&s->sg, io->addr, io->len); io->addr += io->len; io->len = 0; + MACIO_DPRINTF("sector_num=%" PRId64 " n=%d, nsector=%d, cmd_cmd=%d\n", + sector_num, n, s->nsector, s->dma_cmd); + switch (s->dma_cmd) { case IDE_DMA_READ: m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, @@ -162,6 +197,8 @@ static void pmac_ide_transfer(DBDMA_io *io) MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); + MACIO_DPRINTF("\n"); + s->io_buffer_size = 0; if (s->drive_kind == IDE_CD) { bdrv_acct_start(s->bs, &s->acct, io->len, BDRV_ACCT_READ); diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index fc46727762..0d7f40bf32 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -224,7 +224,7 @@ static void conditional_interrupt(DBDMA_channel *ch) uint32_t status; int cond; - DBDMA_DPRINTF("conditional_interrupt\n"); + DBDMA_DPRINTF("%s\n", __func__); intr = le16_to_cpu(current->command) & INTR_MASK; @@ -233,6 +233,7 @@ static void conditional_interrupt(DBDMA_channel *ch) return; case INTR_ALWAYS: /* always interrupt */ qemu_irq_raise(ch->irq); + DBDMA_DPRINTF("%s: raise\n", __func__); return; } @@ -245,12 +246,16 @@ static void conditional_interrupt(DBDMA_channel *ch) switch(intr) { case INTR_IFSET: /* intr if condition bit is 1 */ - if (cond) + if (cond) { qemu_irq_raise(ch->irq); + DBDMA_DPRINTF("%s: raise\n", __func__); + } return; case INTR_IFCLR: /* intr if condition bit is 0 */ - if (!cond) + if (!cond) { qemu_irq_raise(ch->irq); + DBDMA_DPRINTF("%s: raise\n", __func__); + } return; } } @@ -368,6 +373,8 @@ static void dbdma_end(DBDMA_io *io) DBDMA_channel *ch = io->channel; dbdma_cmd *current = &ch->current; + DBDMA_DPRINTF("%s\n", __func__); + if (conditional_wait(ch)) goto wait; @@ -422,6 +429,7 @@ static void start_input(DBDMA_channel *ch, int key, uint32_t addr, * are not implemented in the mac-io chip */ + DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); if (!addr || key > KEY_STREAM3) { kill_channel(ch); return; -- cgit v1.2.3 From 58c0c311838dd2c6622a156b4d261aa127c510dd Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 01:53:05 +0200 Subject: PPC: dbdma: Fix debug print There was a debug print that didn't compile for me because the format and the arguments weren't in sync. Fix it up. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 0d7f40bf32..3b1e97efbe 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -720,7 +720,8 @@ static void dbdma_write(void *opaque, hwaddr addr, DBDMA_channel *ch = &s->channels[channel]; int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; - DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value); + DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", + addr, value); DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); -- cgit v1.2.3 From 7eaba824b6bc14ee724f8593891f2165d2faa4ab Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 01:53:51 +0200 Subject: PPC: dbdma: Allow new commands in RUN state The DBDMA controller can not change its command stream while it's actively streaming data, true. But the fact that it's in RUN state doesn't actually indicate anything. It could just as well be in WAIT while in RUN. And then it's legal to change commands. This fixes a real world issue I've encountered with Mac OS X. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 3b1e97efbe..76567672b0 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -725,11 +725,11 @@ static void dbdma_write(void *opaque, hwaddr addr, DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); - /* cmdptr cannot be modified if channel is RUN or ACTIVE */ + /* cmdptr cannot be modified if channel is ACTIVE */ - if (reg == DBDMA_CMDPTR_LO && - (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE))) + if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & ACTIVE)) { return; + } ch->regs[reg] = value; -- cgit v1.2.3 From f2f963fd0775263f9c1cdd3cd92b171c8dae6c77 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:06:35 +0200 Subject: PPC: dbdma: Move defines into header file We usually keep struct and constant definitions in header files. Move them there to stay consistent and to make access to fields easier. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 117 -------------------------------------------- include/hw/ppc/mac_dbdma.h | 118 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 117 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 76567672b0..c55c75c83c 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -54,123 +54,6 @@ /* */ -/* - * DBDMA control/status registers. All little-endian. - */ - -#define DBDMA_CONTROL 0x00 -#define DBDMA_STATUS 0x01 -#define DBDMA_CMDPTR_HI 0x02 -#define DBDMA_CMDPTR_LO 0x03 -#define DBDMA_INTR_SEL 0x04 -#define DBDMA_BRANCH_SEL 0x05 -#define DBDMA_WAIT_SEL 0x06 -#define DBDMA_XFER_MODE 0x07 -#define DBDMA_DATA2PTR_HI 0x08 -#define DBDMA_DATA2PTR_LO 0x09 -#define DBDMA_RES1 0x0A -#define DBDMA_ADDRESS_HI 0x0B -#define DBDMA_BRANCH_ADDR_HI 0x0C -#define DBDMA_RES2 0x0D -#define DBDMA_RES3 0x0E -#define DBDMA_RES4 0x0F - -#define DBDMA_REGS 16 -#define DBDMA_SIZE (DBDMA_REGS * sizeof(uint32_t)) - -#define DBDMA_CHANNEL_SHIFT 7 -#define DBDMA_CHANNEL_SIZE (1 << DBDMA_CHANNEL_SHIFT) - -#define DBDMA_CHANNELS (0x1000 >> DBDMA_CHANNEL_SHIFT) - -/* Bits in control and status registers */ - -#define RUN 0x8000 -#define PAUSE 0x4000 -#define FLUSH 0x2000 -#define WAKE 0x1000 -#define DEAD 0x0800 -#define ACTIVE 0x0400 -#define BT 0x0100 -#define DEVSTAT 0x00ff - -/* - * DBDMA command structure. These fields are all little-endian! - */ - -typedef struct dbdma_cmd { - uint16_t req_count; /* requested byte transfer count */ - uint16_t command; /* command word (has bit-fields) */ - uint32_t phy_addr; /* physical data address */ - uint32_t cmd_dep; /* command-dependent field */ - uint16_t res_count; /* residual count after completion */ - uint16_t xfer_status; /* transfer status */ -} dbdma_cmd; - -/* DBDMA command values in command field */ - -#define COMMAND_MASK 0xf000 -#define OUTPUT_MORE 0x0000 /* transfer memory data to stream */ -#define OUTPUT_LAST 0x1000 /* ditto followed by end marker */ -#define INPUT_MORE 0x2000 /* transfer stream data to memory */ -#define INPUT_LAST 0x3000 /* ditto, expect end marker */ -#define STORE_WORD 0x4000 /* write word (4 bytes) to device reg */ -#define LOAD_WORD 0x5000 /* read word (4 bytes) from device reg */ -#define DBDMA_NOP 0x6000 /* do nothing */ -#define DBDMA_STOP 0x7000 /* suspend processing */ - -/* Key values in command field */ - -#define KEY_MASK 0x0700 -#define KEY_STREAM0 0x0000 /* usual data stream */ -#define KEY_STREAM1 0x0100 /* control/status stream */ -#define KEY_STREAM2 0x0200 /* device-dependent stream */ -#define KEY_STREAM3 0x0300 /* device-dependent stream */ -#define KEY_STREAM4 0x0400 /* reserved */ -#define KEY_REGS 0x0500 /* device register space */ -#define KEY_SYSTEM 0x0600 /* system memory-mapped space */ -#define KEY_DEVICE 0x0700 /* device memory-mapped space */ - -/* Interrupt control values in command field */ - -#define INTR_MASK 0x0030 -#define INTR_NEVER 0x0000 /* don't interrupt */ -#define INTR_IFSET 0x0010 /* intr if condition bit is 1 */ -#define INTR_IFCLR 0x0020 /* intr if condition bit is 0 */ -#define INTR_ALWAYS 0x0030 /* always interrupt */ - -/* Branch control values in command field */ - -#define BR_MASK 0x000c -#define BR_NEVER 0x0000 /* don't branch */ -#define BR_IFSET 0x0004 /* branch if condition bit is 1 */ -#define BR_IFCLR 0x0008 /* branch if condition bit is 0 */ -#define BR_ALWAYS 0x000c /* always branch */ - -/* Wait control values in command field */ - -#define WAIT_MASK 0x0003 -#define WAIT_NEVER 0x0000 /* don't wait */ -#define WAIT_IFSET 0x0001 /* wait if condition bit is 1 */ -#define WAIT_IFCLR 0x0002 /* wait if condition bit is 0 */ -#define WAIT_ALWAYS 0x0003 /* always wait */ - -typedef struct DBDMA_channel { - int channel; - uint32_t regs[DBDMA_REGS]; - qemu_irq irq; - DBDMA_io io; - DBDMA_rw rw; - DBDMA_flush flush; - dbdma_cmd current; - int processing; -} DBDMA_channel; - -typedef struct { - MemoryRegion mem; - DBDMA_channel channels[DBDMA_CHANNELS]; -} DBDMAState; - #ifdef DEBUG_DBDMA static void dump_dbdma_cmd(dbdma_cmd *cmd) { diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 691263eede..90be5d9c9b 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -39,6 +39,124 @@ struct DBDMA_io { DBDMA_end dma_end; }; +/* + * DBDMA control/status registers. All little-endian. + */ + +#define DBDMA_CONTROL 0x00 +#define DBDMA_STATUS 0x01 +#define DBDMA_CMDPTR_HI 0x02 +#define DBDMA_CMDPTR_LO 0x03 +#define DBDMA_INTR_SEL 0x04 +#define DBDMA_BRANCH_SEL 0x05 +#define DBDMA_WAIT_SEL 0x06 +#define DBDMA_XFER_MODE 0x07 +#define DBDMA_DATA2PTR_HI 0x08 +#define DBDMA_DATA2PTR_LO 0x09 +#define DBDMA_RES1 0x0A +#define DBDMA_ADDRESS_HI 0x0B +#define DBDMA_BRANCH_ADDR_HI 0x0C +#define DBDMA_RES2 0x0D +#define DBDMA_RES3 0x0E +#define DBDMA_RES4 0x0F + +#define DBDMA_REGS 16 +#define DBDMA_SIZE (DBDMA_REGS * sizeof(uint32_t)) + +#define DBDMA_CHANNEL_SHIFT 7 +#define DBDMA_CHANNEL_SIZE (1 << DBDMA_CHANNEL_SHIFT) + +#define DBDMA_CHANNELS (0x1000 >> DBDMA_CHANNEL_SHIFT) + +/* Bits in control and status registers */ + +#define RUN 0x8000 +#define PAUSE 0x4000 +#define FLUSH 0x2000 +#define WAKE 0x1000 +#define DEAD 0x0800 +#define ACTIVE 0x0400 +#define BT 0x0100 +#define DEVSTAT 0x00ff + +/* + * DBDMA command structure. These fields are all little-endian! + */ + +typedef struct dbdma_cmd { + uint16_t req_count; /* requested byte transfer count */ + uint16_t command; /* command word (has bit-fields) */ + uint32_t phy_addr; /* physical data address */ + uint32_t cmd_dep; /* command-dependent field */ + uint16_t res_count; /* residual count after completion */ + uint16_t xfer_status; /* transfer status */ +} dbdma_cmd; + +/* DBDMA command values in command field */ + +#define COMMAND_MASK 0xf000 +#define OUTPUT_MORE 0x0000 /* transfer memory data to stream */ +#define OUTPUT_LAST 0x1000 /* ditto followed by end marker */ +#define INPUT_MORE 0x2000 /* transfer stream data to memory */ +#define INPUT_LAST 0x3000 /* ditto, expect end marker */ +#define STORE_WORD 0x4000 /* write word (4 bytes) to device reg */ +#define LOAD_WORD 0x5000 /* read word (4 bytes) from device reg */ +#define DBDMA_NOP 0x6000 /* do nothing */ +#define DBDMA_STOP 0x7000 /* suspend processing */ + +/* Key values in command field */ + +#define KEY_MASK 0x0700 +#define KEY_STREAM0 0x0000 /* usual data stream */ +#define KEY_STREAM1 0x0100 /* control/status stream */ +#define KEY_STREAM2 0x0200 /* device-dependent stream */ +#define KEY_STREAM3 0x0300 /* device-dependent stream */ +#define KEY_STREAM4 0x0400 /* reserved */ +#define KEY_REGS 0x0500 /* device register space */ +#define KEY_SYSTEM 0x0600 /* system memory-mapped space */ +#define KEY_DEVICE 0x0700 /* device memory-mapped space */ + +/* Interrupt control values in command field */ + +#define INTR_MASK 0x0030 +#define INTR_NEVER 0x0000 /* don't interrupt */ +#define INTR_IFSET 0x0010 /* intr if condition bit is 1 */ +#define INTR_IFCLR 0x0020 /* intr if condition bit is 0 */ +#define INTR_ALWAYS 0x0030 /* always interrupt */ + +/* Branch control values in command field */ + +#define BR_MASK 0x000c +#define BR_NEVER 0x0000 /* don't branch */ +#define BR_IFSET 0x0004 /* branch if condition bit is 1 */ +#define BR_IFCLR 0x0008 /* branch if condition bit is 0 */ +#define BR_ALWAYS 0x000c /* always branch */ + +/* Wait control values in command field */ + +#define WAIT_MASK 0x0003 +#define WAIT_NEVER 0x0000 /* don't wait */ +#define WAIT_IFSET 0x0001 /* wait if condition bit is 1 */ +#define WAIT_IFCLR 0x0002 /* wait if condition bit is 0 */ +#define WAIT_ALWAYS 0x0003 /* always wait */ + +typedef struct DBDMA_channel { + int channel; + uint32_t regs[DBDMA_REGS]; + qemu_irq irq; + DBDMA_io io; + DBDMA_rw rw; + DBDMA_flush flush; + dbdma_cmd current; + int processing; +} DBDMA_channel; + +typedef struct { + MemoryRegion mem; + DBDMA_channel channels[DBDMA_CHANNELS]; +} DBDMAState; + +/* Externally callable functions */ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_rw rw, DBDMA_flush flush, -- cgit v1.2.3 From d1e562deb2de5c2ced639b18dee59a9ab08236b6 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:18:54 +0200 Subject: PPC: dbdma: Introduce kick function The DBDMA engine really is running all the time, waiting for input. However we don't want to waste cycles constantly polling. So introduce a kick function that data providers can call to notify the DBDMA controller of new input. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 5 +++++ include/hw/ppc/mac_dbdma.h | 1 + 2 files changed, 6 insertions(+) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index c55c75c83c..d1506ce228 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -536,6 +536,11 @@ static void DBDMA_run_bh(void *opaque) DBDMA_run(s); } +void DBDMA_kick(DBDMAState *dbdma) +{ + qemu_bh_schedule(dbdma_bh); +} + void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_rw rw, DBDMA_flush flush, void *opaque) diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 90be5d9c9b..aaeab10b6c 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -161,6 +161,7 @@ typedef struct { void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_rw rw, DBDMA_flush flush, void *opaque); +void DBDMA_kick(DBDMAState *dbdma); void* DBDMA_init (MemoryRegion **dbdma_mem); #endif -- cgit v1.2.3 From d2f0ce2189feef94516d81fa5834fa4d65435053 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:22:41 +0200 Subject: PPC: dbdma: Move static bh variable to device struct The DBDMA controller has a bottom half to asynchronously process DMA request queues. This bh was stored as a gross static variable. Move it into the device struct instead. While at it, move all users of it to the new generic kick function. Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 24 +++++++++++++++--------- include/hw/ppc/mac_dbdma.h | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index d1506ce228..4ca0491fab 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -54,6 +54,11 @@ /* */ +static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) +{ + return container_of(ch, DBDMAState, channels[ch->channel]); +} + #ifdef DEBUG_DBDMA static void dump_dbdma_cmd(dbdma_cmd *cmd) { @@ -248,7 +253,6 @@ static void conditional_branch(DBDMA_channel *ch) } } -static QEMUBH *dbdma_bh; static void channel_run(DBDMA_channel *ch); static void dbdma_end(DBDMA_io *io) @@ -365,7 +369,7 @@ static void load_word(DBDMA_channel *ch, int key, uint32_t addr, next(ch); wait: - qemu_bh_schedule(dbdma_bh); + DBDMA_kick(dbdma_from_ch(ch)); } static void store_word(DBDMA_channel *ch, int key, uint32_t addr, @@ -403,7 +407,7 @@ static void store_word(DBDMA_channel *ch, int key, uint32_t addr, next(ch); wait: - qemu_bh_schedule(dbdma_bh); + DBDMA_kick(dbdma_from_ch(ch)); } static void nop(DBDMA_channel *ch) @@ -420,7 +424,7 @@ static void nop(DBDMA_channel *ch) conditional_branch(ch); wait: - qemu_bh_schedule(dbdma_bh); + DBDMA_kick(dbdma_from_ch(ch)); } static void stop(DBDMA_channel *ch) @@ -538,7 +542,7 @@ static void DBDMA_run_bh(void *opaque) void DBDMA_kick(DBDMAState *dbdma) { - qemu_bh_schedule(dbdma_bh); + qemu_bh_schedule(dbdma->bh); } void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, @@ -594,10 +598,12 @@ dbdma_control_write(DBDMA_channel *ch) ch->regs[DBDMA_STATUS] = status; - if (status & ACTIVE) - qemu_bh_schedule(dbdma_bh); - if ((status & FLUSH) && ch->flush) + if (status & ACTIVE) { + DBDMA_kick(dbdma_from_ch(ch)); + } + if ((status & FLUSH) && ch->flush) { ch->flush(&ch->io); + } } static void dbdma_write(void *opaque, hwaddr addr, @@ -750,7 +756,7 @@ void* DBDMA_init (MemoryRegion **dbdma_mem) vmstate_register(NULL, -1, &vmstate_dbdma, s); qemu_register_reset(dbdma_reset, s); - dbdma_bh = qemu_bh_new(DBDMA_run_bh, s); + s->bh = qemu_bh_new(DBDMA_run_bh, s); return s; } diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index aaeab10b6c..eb8e0f0075 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -154,6 +154,7 @@ typedef struct DBDMA_channel { typedef struct { MemoryRegion mem; DBDMA_channel channels[DBDMA_CHANNELS]; + QEMUBH *bh; } DBDMAState; /* Externally callable functions */ -- cgit v1.2.3 From 4aa3510f6f36c5ea35219acf641788222d977437 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:36:14 +0200 Subject: PPC: dbdma: macio: Add DMA callback We need to know when the IDE core starts a DMA transfer. Add a notifier function so we have the chance to start transmitting data. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 40 ++++++++++++++++++++++++++++++++++++++++ hw/ppc/mac.h | 2 ++ 2 files changed, 42 insertions(+) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 8d42b5c2bb..126549dcdc 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -359,11 +359,50 @@ static void macio_ide_reset(DeviceState *dev) ide_bus_reset(&d->bus); } +static int ide_nop(IDEDMA *dma) +{ + return 0; +} + +static int ide_nop_int(IDEDMA *dma, int x) +{ + return 0; +} + +static void ide_nop_restart(void *opaque, int x, RunState y) +{ +} + +static void ide_dbdma_start(IDEDMA *dma, IDEState *s, + BlockDriverCompletionFunc *cb) +{ + MACIOIDEState *m = container_of(dma, MACIOIDEState, dma); + + MACIO_DPRINTF("\n"); + DBDMA_kick(m->dbdma); +} + +static const IDEDMAOps dbdma_ops = { + .start_dma = ide_dbdma_start, + .start_transfer = ide_nop, + .prepare_buf = ide_nop_int, + .rw_buf = ide_nop_int, + .set_unit = ide_nop_int, + .add_status = ide_nop_int, + .set_inactive = ide_nop, + .restart_cb = ide_nop_restart, + .reset = ide_nop, +}; + static void macio_ide_realizefn(DeviceState *dev, Error **errp) { MACIOIDEState *s = MACIO_IDE(dev); ide_init2(&s->bus, s->irq); + + /* Register DMA callbacks */ + s->dma.ops = &dbdma_ops; + s->bus.dma = &s->dma; } static void macio_ide_initfn(Object *obj) @@ -414,6 +453,7 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) void macio_ide_register_dma(MACIOIDEState *s, void *dbdma, int channel) { + s->dbdma = dbdma; DBDMA_register_channel(dbdma, channel, s->dma_irq, pmac_ide_transfer, pmac_ide_flush, s); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 54efaed627..27c4ca39be 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -131,6 +131,8 @@ typedef struct MACIOIDEState { MemoryRegion mem; IDEBus bus; BlockDriverAIOCB *aiocb; + IDEDMA dma; + void *dbdma; } MACIOIDEState; void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); -- cgit v1.2.3 From 03ee3b1e58266c7e5b155d58f443d94b23d2bd05 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:47:20 +0200 Subject: PPC: dbdma: Move processing to io Soon we will introduce intermediate processing pauses which will allow the bottom half to restart a DMA request that couldn't be fulfilled yet. For that to work, move the processing variable into the io struct which is what DMA providers work with. While touching it, also change it into a bool Signed-off-by: Alexander Graf --- hw/misc/macio/mac_dbdma.c | 10 ++++++---- include/hw/ppc/mac_dbdma.h | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 4ca0491fab..f47a736182 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -275,7 +275,9 @@ static void dbdma_end(DBDMA_io *io) conditional_branch(ch); wait: - ch->processing = 0; + /* Indicate that we're ready for a new DMA round */ + ch->io.processing = false; + if ((ch->regs[DBDMA_STATUS] & RUN) && (ch->regs[DBDMA_STATUS] & ACTIVE)) channel_run(ch); @@ -301,7 +303,7 @@ static void start_output(DBDMA_channel *ch, int key, uint32_t addr, ch->io.is_last = is_last; ch->io.dma_end = dbdma_end; ch->io.is_dma_out = 1; - ch->processing = 1; + ch->io.processing = true; if (ch->rw) { ch->rw(&ch->io); } @@ -327,7 +329,7 @@ static void start_input(DBDMA_channel *ch, int key, uint32_t addr, ch->io.is_last = is_last; ch->io.dma_end = dbdma_end; ch->io.is_dma_out = 0; - ch->processing = 1; + ch->io.processing = true; if (ch->rw) { ch->rw(&ch->io); } @@ -525,7 +527,7 @@ static void DBDMA_run(DBDMAState *s) for (channel = 0; channel < DBDMA_CHANNELS; channel++) { DBDMA_channel *ch = &s->channels[channel]; uint32_t status = ch->regs[DBDMA_STATUS]; - if (!ch->processing && (status & RUN) && (status & ACTIVE)) { + if (!ch->io.processing && (status & RUN) && (status & ACTIVE)) { channel_run(ch); } } diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index eb8e0f0075..4d7318df84 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -37,6 +37,8 @@ struct DBDMA_io { int is_last; int is_dma_out; DBDMA_end dma_end; + /* DMA is in progress, don't start another one */ + bool processing; }; /* @@ -148,7 +150,6 @@ typedef struct DBDMA_channel { DBDMA_rw rw; DBDMA_flush flush; dbdma_cmd current; - int processing; } DBDMA_channel; typedef struct { -- cgit v1.2.3 From cae323572eddc1a45e2f6ef98c006d98fed23b1e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 02:54:35 +0200 Subject: PPC: dbdma: Wait for DMA until we have data We should only start processing DMA requests when we have data to process. Hold off working through the DMA shuffling until the IDE core told us that it's ready. This is required because the guest can program the DMA engine or the IDE transfer first. Both are legal. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 19 +++++++++++++++++++ hw/ppc/mac.h | 1 + 2 files changed, 20 insertions(+) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 126549dcdc..2b1e51d49e 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -64,6 +64,14 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) goto done; } + if (!m->dma_active) { + MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n", + s->nsector, io->len, s->status); + /* data not ready yet, wait for the channel to get restarted */ + io->processing = false; + return; + } + MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size); if (s->io_buffer_size > 0) { @@ -80,6 +88,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (s->packet_transfer_size <= 0) { MACIO_DPRINTF("end of transfer\n"); ide_atapi_cmd_ok(s); + m->dma_active = false; } if (io->len == 0) { @@ -130,6 +139,14 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) goto done; } + if (!m->dma_active) { + MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n", + s->nsector, io->len, s->status); + /* data not ready yet, wait for the channel to get restarted */ + io->processing = false; + return; + } + sector_num = ide_get_sector(s); MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size); if (s->io_buffer_size > 0) { @@ -145,6 +162,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) MACIO_DPRINTF("end of transfer\n"); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); + m->dma_active = false; } if (io->len == 0) { @@ -379,6 +397,7 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s, MACIOIDEState *m = container_of(dma, MACIOIDEState, dma); MACIO_DPRINTF("\n"); + m->dma_active = true; DBDMA_kick(m->dbdma); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 27c4ca39be..1e578dd59d 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -133,6 +133,7 @@ typedef struct MACIOIDEState { BlockDriverAIOCB *aiocb; IDEDMA dma; void *dbdma; + bool dma_active; } MACIOIDEState; void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); -- cgit v1.2.3 From 80fc95d8bdaf3392106b131a97ca701fd374489a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 28 Jun 2013 13:30:01 +0200 Subject: PPC: dbdma: Support unaligned DMA access The DBDMA engine really just reads bytes from a producing device (IDE in our case) and shoves these bytes into memory. It doesn't care whether any alignment takes place or not. Our code today however assumes that block accesses always happen on sector (512 byte) boundaries. This is a fair assumption for most cases. However, Mac OS X really likes to do unaligned, incomplete accesses that it finishes with the next DMA request. So we need to read / write the unaligned bits independent of the actual asynchronous request, because that one can only handle 512-byte-aligned data. We also need to cache these unaligned sectors until the next DMA request, at which point the data might be successfully flushed from the pipe. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 131 ++++++++++++++++++++++++++++++++++++++++++--- include/hw/ppc/mac_dbdma.h | 3 ++ 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 2b1e51d49e..18bc1d1273 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -56,11 +56,13 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); + int unaligned; if (ret < 0) { m->aiocb = NULL; qemu_sglist_destroy(&s->sg); ide_atapi_io_error(s, ret); + io->remainder_len = 0; goto done; } @@ -85,7 +87,31 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) s->io_buffer_index &= 0x7ff; } - if (s->packet_transfer_size <= 0) { + s->io_buffer_size = io->len; + + MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len, + io->len, s->packet_transfer_size); + if (io->remainder_len && io->len) { + /* guest wants the rest of its previous transfer */ + int remainder_len = MIN(io->remainder_len, io->len); + + MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len); + + cpu_physical_memory_write(io->addr, io->remainder + 0x200 - + remainder_len, remainder_len); + + io->addr += remainder_len; + io->len -= remainder_len; + s->io_buffer_size = remainder_len; + io->remainder_len -= remainder_len; + /* treat remainder as individual transfer, start again */ + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, + &address_space_memory); + pmac_ide_atapi_transfer_cb(opaque, 0); + return; + } + + if (!s->packet_transfer_size) { MACIO_DPRINTF("end of transfer\n"); ide_atapi_cmd_ok(s); m->dma_active = false; @@ -98,14 +124,40 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) /* launch next transfer */ - MACIO_DPRINTF("io->len = %#x\n", io->len); + /* handle unaligned accesses first, get them over with and only do the + remaining bulk transfer using our async DMA helpers */ + unaligned = io->len & 0x1ff; + if (unaligned) { + int sector_num = (s->lba << 2) + (s->io_buffer_index >> 9); + int nsector = io->len >> 9; - s->io_buffer_size = io->len; + MACIO_DPRINTF("precopying unaligned %d bytes to %#lx\n", + unaligned, io->addr + io->len - unaligned); + + bdrv_read(s->bs, sector_num + nsector, io->remainder, 1); + cpu_physical_memory_write(io->addr + io->len - unaligned, + io->remainder, unaligned); + + io->len -= unaligned; + } + + MACIO_DPRINTF("io->len = %#x\n", io->len); qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, &address_space_memory); qemu_sglist_add(&s->sg, io->addr, io->len); - io->addr += io->len; + io->addr += s->io_buffer_size; + io->remainder_len = MIN(s->packet_transfer_size - s->io_buffer_size, + (0x200 - unaligned) & 0x1ff); + MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len); + + /* We would read no data from the block layer, thus not get a callback. + Just fake completion manually. */ + if (!io->len) { + pmac_ide_atapi_transfer_cb(opaque, 0); + return; + } + io->len = 0; MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n", @@ -128,14 +180,16 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - int n; + int n = 0; int64_t sector_num; + int unaligned; if (ret < 0) { MACIO_DPRINTF("DMA error\n"); m->aiocb = NULL; qemu_sglist_destroy(&s->sg); ide_dma_error(s); + io->remainder_len = 0; goto done; } @@ -158,7 +212,33 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->nsector -= n; } - if (s->nsector == 0) { + MACIO_DPRINTF("remainder: %d io->len: %d nsector: %d sector_num: %ld\n", + io->remainder_len, io->len, s->nsector, sector_num); + if (io->remainder_len && io->len) { + /* guest wants the rest of its previous transfer */ + int remainder_len = MIN(io->remainder_len, io->len); + uint8_t *p = &io->remainder[0x200 - remainder_len]; + + MACIO_DPRINTF("copying remainder %d bytes at %#lx\n", + remainder_len, io->addr); + + switch (s->dma_cmd) { + case IDE_DMA_READ: + cpu_physical_memory_write(io->addr, p, remainder_len); + break; + case IDE_DMA_WRITE: + cpu_physical_memory_read(io->addr, p, remainder_len); + bdrv_write(s->bs, sector_num - 1, io->remainder, 1); + break; + case IDE_DMA_TRIM: + break; + } + io->addr += remainder_len; + io->len -= remainder_len; + io->remainder_len -= remainder_len; + } + + if (s->nsector == 0 && !io->remainder_len) { MACIO_DPRINTF("end of transfer\n"); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); @@ -175,12 +255,49 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->io_buffer_index = 0; s->io_buffer_size = io->len; + /* handle unaligned accesses first, get them over with and only do the + remaining bulk transfer using our async DMA helpers */ + unaligned = io->len & 0x1ff; + if (unaligned) { + int nsector = io->len >> 9; + + MACIO_DPRINTF("precopying unaligned %d bytes to %#lx\n", + unaligned, io->addr + io->len - unaligned); + + switch (s->dma_cmd) { + case IDE_DMA_READ: + bdrv_read(s->bs, sector_num + nsector, io->remainder, 1); + cpu_physical_memory_write(io->addr + io->len - unaligned, + io->remainder, unaligned); + break; + case IDE_DMA_WRITE: + /* cache the contents in our io struct */ + cpu_physical_memory_read(io->addr + io->len - unaligned, + io->remainder, unaligned); + break; + case IDE_DMA_TRIM: + break; + } + + io->len -= unaligned; + } + MACIO_DPRINTF("io->len = %#x\n", io->len); qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, &address_space_memory); qemu_sglist_add(&s->sg, io->addr, io->len); - io->addr += io->len; + io->addr += io->len + unaligned; + io->remainder_len = (0x200 - unaligned) & 0x1ff; + MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len); + + /* We would read no data from the block layer, thus not get a callback. + Just fake completion manually. */ + if (!io->len) { + pmac_ide_transfer_cb(opaque, 0); + return; + } + io->len = 0; MACIO_DPRINTF("sector_num=%" PRId64 " n=%d, nsector=%d, cmd_cmd=%d\n", diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 4d7318df84..90efd277e4 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -39,6 +39,9 @@ struct DBDMA_io { DBDMA_end dma_end; /* DMA is in progress, don't start another one */ bool processing; + /* unaligned last sector of a request */ + uint8_t remainder[0x200]; + int remainder_len; }; /* -- cgit v1.2.3 From a0f9fdfd98cc0571f9921a7eadd7316532e3e289 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 05:15:14 +0200 Subject: PPC: Add timer handler for newworld mac-io Mac OS X accesses fancy timer registers inside of the mac-io on bootup. These really should be ticking at the mac-io bus frequency, but I don't see anyone upset when we just make them as fast as we want to. With this patch on top of my previous patch queue and latest OpenBIOS I am able to boot Mac OS X 10.4 with -M mac99. Signed-off-by: Alexander Graf --- hw/misc/macio/macio.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 6459bc1325..c0d0bf7287 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -234,11 +234,39 @@ static void macio_oldworld_init(Object *obj) } } +static void timer_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ +} + +static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t value = 0; + + switch (addr) { + case 0x38: + value = qemu_get_clock_ns(vm_clock); + break; + case 0x3c: + value = qemu_get_clock_ns(vm_clock) >> 32; + break; + } + + return value; +} + +static const MemoryRegionOps timer_ops = { + .read = timer_read, + .write = timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static int macio_newworld_initfn(PCIDevice *d) { MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); SysBusDevice *sysbus_dev; + MemoryRegion *timer_memory = g_new(MemoryRegion, 1); int i; int cur_irq = 0; int ret = macio_common_initfn(d); @@ -265,6 +293,11 @@ static int macio_newworld_initfn(PCIDevice *d) } } + /* Timer */ + memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", + 0x1000); + memory_region_add_subregion(&s->bar, 0x15000, timer_memory); + return 0; } -- cgit v1.2.3 From f35ea98cd9f75db9286f05bf3dc4b532f4cb5eaa Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 30 Jun 2013 15:29:13 +0200 Subject: PPC: dbdma: Support more multi-issue DMA requests A DMA request can happen for data that hasn't been completely been provided by the IDE core yet. For example - DBDMA request for 0x1000 bytes - IDE request for 1 sector - DBDMA wants to read 0x1000 bytes (8 sectors) from bdrv - breakage Instead, we should truncate our bdrv request to the maximum number of sectors we're allowed to read at that given time. Once that transfer is through, we will fall into our recently introduced waiting logic. - DBDMA requests for 0x1000 bytes - IDE request for 1 sector - DBDMA wants to read MIN(0x1000, 1 * 512) bytes - DBDMA finishes reading, indicates to IDE core that transfer is complete - IDE request for 7 sectors - DBDMA finishes the DMA Reported-by: Mark Cave-Ayland Signed-off-by: Alexander Graf --- hw/ide/macio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 18bc1d1273..38ad92423d 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -87,7 +87,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) s->io_buffer_index &= 0x7ff; } - s->io_buffer_size = io->len; + s->io_buffer_size = MIN(io->len, s->packet_transfer_size); MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len, io->len, s->packet_transfer_size); @@ -253,7 +253,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) /* launch next transfer */ s->io_buffer_index = 0; - s->io_buffer_size = io->len; + s->io_buffer_size = MIN(io->len, s->nsector * 512); /* handle unaligned accesses first, get them over with and only do the remaining bulk transfer using our async DMA helpers */ -- cgit v1.2.3