diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index b7fb04406c34b5c4cbeda2dc78ee41cf1d63f7c9..245004530c87c15c39128c71bd4f68f969a38c54 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -26,9 +26,9 @@ typedef struct TyphoonCchip { } TyphoonCchip; typedef struct TyphoonWindow { - uint32_t base_addr; - uint32_t mask; - uint32_t translated_base_pfn; + uint64_t wba; + uint64_t wsm; + uint64_t tba; } TyphoonWindow; typedef struct TyphoonPchip { @@ -37,6 +37,10 @@ typedef struct TyphoonPchip { MemoryRegion reg_mem; MemoryRegion reg_io; MemoryRegion reg_conf; + + AddressSpace iommu_as; + MemoryRegion iommu; + uint64_t ctl; TyphoonWindow win[4]; } TyphoonPchip; @@ -209,53 +213,53 @@ static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case 0x0000: /* WSBA0: Window Space Base Address Register. */ - ret = s->pchip.win[0].base_addr; + ret = s->pchip.win[0].wba; break; case 0x0040: /* WSBA1 */ - ret = s->pchip.win[1].base_addr; + ret = s->pchip.win[1].wba; break; case 0x0080: /* WSBA2 */ - ret = s->pchip.win[2].base_addr; + ret = s->pchip.win[2].wba; break; case 0x00c0: /* WSBA3 */ - ret = s->pchip.win[3].base_addr; + ret = s->pchip.win[3].wba; break; case 0x0100: /* WSM0: Window Space Mask Register. */ - ret = s->pchip.win[0].mask; + ret = s->pchip.win[0].wsm; break; case 0x0140: /* WSM1 */ - ret = s->pchip.win[1].mask; + ret = s->pchip.win[1].wsm; break; case 0x0180: /* WSM2 */ - ret = s->pchip.win[2].mask; + ret = s->pchip.win[2].wsm; break; case 0x01c0: /* WSM3 */ - ret = s->pchip.win[3].mask; + ret = s->pchip.win[3].wsm; break; case 0x0200: /* TBA0: Translated Base Address Register. */ - ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; + ret = s->pchip.win[0].tba; break; case 0x0240: /* TBA1 */ - ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; + ret = s->pchip.win[1].tba; break; case 0x0280: /* TBA2 */ - ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; + ret = s->pchip.win[2].tba; break; case 0x02c0: /* TBA3 */ - ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; + ret = s->pchip.win[3].tba; break; case 0x0300: @@ -458,53 +462,53 @@ static void pchip_write(void *opaque, hwaddr addr, switch (addr) { case 0x0000: /* WSBA0: Window Space Base Address Register. */ - s->pchip.win[0].base_addr = val; + s->pchip.win[0].wba = val & 0xfff00003u; break; case 0x0040: /* WSBA1 */ - s->pchip.win[1].base_addr = val; + s->pchip.win[1].wba = val & 0xfff00003u; break; case 0x0080: /* WSBA2 */ - s->pchip.win[2].base_addr = val; + s->pchip.win[2].wba = val & 0xfff00003u; break; case 0x00c0: /* WSBA3 */ - s->pchip.win[3].base_addr = val; + s->pchip.win[3].wba = (val & 0x80fff00001ull) | 2; break; case 0x0100: /* WSM0: Window Space Mask Register. */ - s->pchip.win[0].mask = val; + s->pchip.win[0].wsm = val & 0xfff00000u; break; case 0x0140: /* WSM1 */ - s->pchip.win[1].mask = val; + s->pchip.win[1].wsm = val & 0xfff00000u; break; case 0x0180: /* WSM2 */ - s->pchip.win[2].mask = val; + s->pchip.win[2].wsm = val & 0xfff00000u; break; case 0x01c0: /* WSM3 */ - s->pchip.win[3].mask = val; + s->pchip.win[3].wsm = val & 0xfff00000u; break; case 0x0200: /* TBA0: Translated Base Address Register. */ - s->pchip.win[0].translated_base_pfn = val >> 10; + s->pchip.win[0].tba = val & 0x7fffffc00ull; break; case 0x0240: /* TBA1 */ - s->pchip.win[1].translated_base_pfn = val >> 10; + s->pchip.win[1].tba = val & 0x7fffffc00ull; break; case 0x0280: /* TBA2 */ - s->pchip.win[2].translated_base_pfn = val >> 10; + s->pchip.win[2].tba = val & 0x7fffffc00ull; break; case 0x02c0: /* TBA3 */ - s->pchip.win[3].translated_base_pfn = val >> 10; + s->pchip.win[3].tba = val & 0x7fffffc00ull; break; case 0x0300: @@ -512,7 +516,6 @@ static void pchip_write(void *opaque, hwaddr addr, oldval = s->pchip.ctl; oldval &= ~0x00001cff0fc7ffull; /* RW fields */ oldval |= val & 0x00001cff0fc7ffull; - s->pchip.ctl = oldval; break; @@ -593,6 +596,140 @@ static const MemoryRegionOps pchip_ops = { }, }; +/* A subroutine of typhoon_translate_iommu that builds an IOMMUTLBEntry + using the given translated address and mask. */ +static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret) +{ + *ret = (IOMMUTLBEntry) { + .target_as = &address_space_memory, + .translated_addr = taddr, + .addr_mask = mask, + .perm = IOMMU_RW, + }; + return true; +} + +/* A subroutine of typhoon_translate_iommu that handles scatter-gather + translation, given the address of the PTE. */ +static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret) +{ + uint64_t pte = ldq_phys(pte_addr); + + /* Check valid bit. */ + if ((pte & 1) == 0) { + return false; + } + + return make_iommu_tlbe((pte & 0x3ffffe) << 12, 0x1fff, ret); +} + +/* A subroutine of typhoon_translate_iommu that handles one of the + four single-address-cycle translation windows. */ +static bool window_translate(TyphoonWindow *win, hwaddr addr, + IOMMUTLBEntry *ret) +{ + uint32_t wba = win->wba; + uint64_t wsm = win->wsm; + uint64_t tba = win->tba; + uint64_t wsm_ext = wsm | 0xfffff; + + /* Check for window disabled. */ + if ((wba & 1) == 0) { + return false; + } + + /* Check for window hit. */ + if ((addr & ~wsm_ext) != (wba & 0xfff00000u)) { + return false; + } + + if (wba & 2) { + /* Scatter-gather translation. */ + hwaddr pte_addr; + + /* See table 10-6, Generating PTE address for PCI DMA Address. */ + pte_addr = tba & ~(wsm >> 10); + pte_addr |= (addr & (wsm | 0xfe000)) >> 10; + return pte_translate(pte_addr, ret); + } else { + /* Direct-mapped translation. */ + return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret); + } +} + +/* Handle PCI-to-system address translation. */ +/* TODO: A translation failure here ought to set PCI error codes on the + Pchip and generate a machine check interrupt. */ +static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr) +{ + TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); + IOMMUTLBEntry ret; + int i; + + if (addr <= 0xffffffffu) { + /* Single-address cycle. */ + + /* Check for the Window Hole, inhibiting matching. */ + if ((pchip->ctl & 0x20) + && addr >= 0x80000 + && addr <= 0xfffff) { + goto failure; + } + + /* Check the first three windows. */ + for (i = 0; i < 3; ++i) { + if (window_translate(&pchip->win[i], addr, &ret)) { + goto success; + } + } + + /* Check the fourth window for DAC disable. */ + if ((pchip->win[3].wba & 0x80000000000ull) == 0 + && window_translate(&pchip->win[3], addr, &ret)) { + goto success; + } + } else { + /* Double-address cycle. */ + + if (addr >= 0x10000000000ull && addr < 0x20000000000ull) { + /* Check for the DMA monster window. */ + if (pchip->ctl & 0x40) { + /* See 10.1.4.4; in particular <39:35> is ignored. */ + make_iommu_tlbe(0, 0x007ffffffffull, &ret); + goto success; + } + } + + if (addr >= 0x80000000000 && addr <= 0xfffffffffff) { + /* Check the fourth window for DAC enable and window enable. */ + if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) { + uint64_t pte_addr; + + pte_addr = pchip->win[3].tba & 0x7ffc00000ull; + pte_addr |= (addr & 0xffffe000u) >> 10; + if (pte_translate(pte_addr, &ret)) { + goto success; + } + } + } + } + + failure: + ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE }; + success: + return ret; +} + +static const MemoryRegionIOMMUOps typhoon_iommu_ops = { + .translate = typhoon_translate_iommu, +}; + +static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + TyphoonState *s = opaque; + return &s->pchip.iommu_as; +} + static void typhoon_set_irq(void *opaque, int irq, int level) { TyphoonState *s = opaque; @@ -688,6 +825,9 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, s = TYPHOON_PCI_HOST_BRIDGE(dev); phb = PCI_HOST_BRIDGE(dev); + s->cchip.misc = 0x800000000ull; /* Revision: Typhoon. */ + s->pchip.win[3].wba = 2; /* Window 3 SG always enabled. */ + /* Remember the CPUs so that we can deliver interrupts to them. */ for (i = 0; i < 4; i++) { AlphaCPU *cpu = cpus[i]; @@ -746,6 +886,12 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, 0, 64, TYPE_PCI_BUS); phb->bus = b; + /* Host memory as seen from the PCI side, via the IOMMU. */ + memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, + "iommu-typhoon", UINT64_MAX); + address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); + pci_setup_iommu(b, typhoon_pci_dma_iommu, s); + /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, b, "pci0-iack", 64*MB); diff --git a/target-alpha/helper.h b/target-alpha/helper.h index 0e425cfc0840a08ac4c44b507ee5b92f227e4725..732b701d53960e7d9fd997f5a56f9578d58d5e3a 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -99,6 +99,7 @@ DEF_HELPER_FLAGS_2(ieee_input_cmp, TCG_CALL_NO_WG, void, env, i64) #if !defined (CONFIG_USER_ONLY) DEF_HELPER_2(hw_ret, void, env, i64) +DEF_HELPER_3(call_pal, void, env, i64, i64) DEF_HELPER_1(ldl_phys, i64, i64) DEF_HELPER_1(ldq_phys, i64, i64) @@ -111,6 +112,7 @@ DEF_HELPER_3(stq_c_phys, i64, env, i64, i64) DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_1(tb_flush, TCG_CALL_NO_RWG, void, env) DEF_HELPER_1(halt, void, i64); diff --git a/target-alpha/sys_helper.c b/target-alpha/sys_helper.c index bd94597d363dfffce314c71fd27fd90a5fc49f04..97cf9ebfc9033596b6746c15b68e2a4476686459 100644 --- a/target-alpha/sys_helper.c +++ b/target-alpha/sys_helper.c @@ -51,6 +51,17 @@ void helper_hw_ret(CPUAlphaState *env, uint64_t a) } } +void helper_call_pal(CPUAlphaState *env, uint64_t pc, uint64_t entry_ofs) +{ + int pal_mode = env->pal_mode; + env->exc_addr = pc | pal_mode; + env->pc = env->palbr + entry_ofs; + if (!pal_mode) { + env->pal_mode = 1; + swap_shadow_regs(env); + } +} + void helper_tbia(CPUAlphaState *env) { tlb_flush(env, 1); @@ -61,6 +72,11 @@ void helper_tbis(CPUAlphaState *env, uint64_t p) tlb_flush_page(env, p); } +void helper_tb_flush(CPUAlphaState *env) +{ + tb_flush(env); +} + void helper_halt(uint64_t restart) { if (restart) { @@ -91,4 +107,5 @@ void helper_set_alarm(CPUAlphaState *env, uint64_t expire) qemu_del_timer(cpu->alarm_timer); } } + #endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 0efd5595e66c94939e71ff155c989fa085c5712b..309dea6ff0dd5c09288513e75eab6e21ecafbca4 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -379,13 +379,26 @@ static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, #endif } -static int use_goto_tb(DisasContext *ctx, uint64_t dest) +static bool in_superpage(DisasContext *ctx, int64_t addr) { - /* Check for the dest on the same page as the start of the TB. We - also want to suppress goto_tb in the case of single-steping and IO. */ - return (((ctx->tb->pc ^ dest) & TARGET_PAGE_MASK) == 0 - && !ctx->singlestep_enabled - && !(ctx->tb->cflags & CF_LAST_IO)); + return ((ctx->tb->flags & TB_FLAGS_USER_MODE) == 0 + && addr < 0 + && ((addr >> 41) & 3) == 2 + && addr >> TARGET_VIRT_ADDR_SPACE_BITS == addr >> 63); +} + +static bool use_goto_tb(DisasContext *ctx, uint64_t dest) +{ + /* Suppress goto_tb in the case of single-steping and IO. */ + if (ctx->singlestep_enabled || (ctx->tb->cflags & CF_LAST_IO)) { + return false; + } + /* If the destination is in the superpage, the page perms can't change. */ + if (in_superpage(ctx, dest)) { + return true; + } + /* Check for the dest on the same page as the start of the TB. */ + return ((ctx->tb->pc ^ dest) & TARGET_PAGE_MASK) == 0; } static ExitStatus gen_bdirect(DisasContext *ctx, int ra, int32_t disp) @@ -1521,7 +1534,8 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) tcg_gen_mov_i64(cpu_unique, cpu_ir[IR_A0]); break; default: - return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0xbf); + palcode &= 0xbf; + goto do_call_pal; } return NO_EXIT; } @@ -1586,13 +1600,42 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) break; default: - return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0x3f); + palcode &= 0x3f; + goto do_call_pal; } return NO_EXIT; } #endif - return gen_invalid(ctx); + + do_call_pal: +#ifdef CONFIG_USER_ONLY + return gen_excp(ctx, EXCP_CALL_PAL, palcode); +#else + { + TCGv pc = tcg_const_i64(ctx->pc); + TCGv entry = tcg_const_i64(palcode & 0x80 + ? 0x2000 + (palcode - 0x80) * 64 + : 0x1000 + palcode * 64); + + gen_helper_call_pal(cpu_env, pc, entry); + + tcg_temp_free(entry); + tcg_temp_free(pc); + + /* Since the destination is running in PALmode, we don't really + need the page permissions check. We'll see the existance of + the page when we create the TB, and we'll flush all TBs if + we change the PAL base register. */ + if (!ctx->singlestep_enabled && !(ctx->tb->cflags & CF_LAST_IO)) { + tcg_gen_goto_tb(0); + tcg_gen_exit_tb((tcg_target_long)ctx->tb); + return EXIT_GOTO_TB; + } + + return EXIT_PC_UPDATED; + } +#endif } #ifndef CONFIG_USER_ONLY @@ -1708,6 +1751,15 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) gen_helper_set_alarm(cpu_env, tmp); break; + case 7: + /* PALBR */ + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUAlphaState, palbr)); + /* Changing the PAL base register implies un-chaining all of the TBs + that ended with a CALL_PAL. Since the base register usually only + changes during boot, flushing everything works well. */ + gen_helper_tb_flush(cpu_env); + return EXIT_PC_STALE; + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ @@ -3392,6 +3444,7 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, CPUAlphaState *env = &cpu->env; DisasContext ctx, *ctxp = &ctx; target_ulong pc_start; + target_ulong pc_mask; uint32_t insn; uint16_t *gen_opc_end; CPUBreakpoint *bp; @@ -3421,8 +3474,15 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) + if (max_insns == 0) { max_insns = CF_COUNT_MASK; + } + + if (in_superpage(&ctx, pc_start)) { + pc_mask = (1ULL << 41) - 1; + } else { + pc_mask = ~TARGET_PAGE_MASK; + } gen_tb_start(); do { @@ -3460,7 +3520,7 @@ static inline void gen_intermediate_code_internal(AlphaCPU *cpu, /* If we reach a page boundary, are single stepping, or exhaust instruction count, stop generation. */ if (ret == NO_EXIT - && ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0 + && ((ctx.pc & pc_mask) == 0 || tcg_ctx.gen_opc_ptr >= gen_opc_end || num_insns >= max_insns || singlestep