Merge remote-tracking branch 'rth/axp-next' into staging

# By Richard Henderson
# Via Richard Henderson
* rth/axp-next:
  target-alpha: Implement the typhoon iommu
  target-alpha: Consider the superpage when threading and ending TBs
  target-alpha: Use goto_tb in call_pal
  target-alpha: Implement call_pal without an exception

Message-id: 1376720412-2165-1-git-send-email-rth@twiddle.net
Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
This commit is contained in:
Anthony Liguori 2013-08-20 09:52:07 -05:00
commit 72420ce9f0
4 changed files with 264 additions and 39 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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