mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 16:23:55 -06:00
Merge remote-tracking branch 'remotes/mcayland/qemu-sparc' into staging
* remotes/mcayland/qemu-sparc: apb: implement IOMMU translation for PCI host bridge apb: handle reading/writing of IOMMU control registers apb: fix IOMMU register sizes apb: Move IOMMU registers into a separate IOMMUState struct tcx: move initialisation from realizefn to initfn tcx: move initialisation from SysBusDevice class to TCX class realizefn cg3: add extra check to prevent CG3 register array overflow cg3: move initialisation from realizefn to initfn Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
50809c8b92
4 changed files with 336 additions and 53 deletions
|
@ -46,6 +46,16 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
|
|||
#define APB_DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* debug IOMMU */
|
||||
//#define DEBUG_IOMMU
|
||||
|
||||
#ifdef DEBUG_IOMMU
|
||||
#define IOMMU_DPRINTF(fmt, ...) \
|
||||
do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define IOMMU_DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Chipset docs:
|
||||
* PBM: "UltraSPARC IIi User's Manual",
|
||||
|
@ -70,6 +80,51 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
|
|||
#define MAX_IVEC 0x40
|
||||
#define NO_IRQ_REQUEST (MAX_IVEC + 1)
|
||||
|
||||
#define IOMMU_PAGE_SIZE_8K (1ULL << 13)
|
||||
#define IOMMU_PAGE_MASK_8K (~(IOMMU_PAGE_SIZE_8K - 1))
|
||||
#define IOMMU_PAGE_SIZE_64K (1ULL << 16)
|
||||
#define IOMMU_PAGE_MASK_64K (~(IOMMU_PAGE_SIZE_64K - 1))
|
||||
|
||||
#define IOMMU_NREGS 3
|
||||
|
||||
#define IOMMU_CTRL 0x0
|
||||
#define IOMMU_CTRL_TBW_SIZE (1ULL << 2)
|
||||
#define IOMMU_CTRL_MMU_EN (1ULL)
|
||||
|
||||
#define IOMMU_CTRL_TSB_SHIFT 16
|
||||
|
||||
#define IOMMU_BASE 0x8
|
||||
|
||||
#define IOMMU_TTE_DATA_V (1ULL << 63)
|
||||
#define IOMMU_TTE_DATA_SIZE (1ULL << 61)
|
||||
#define IOMMU_TTE_DATA_W (1ULL << 1)
|
||||
|
||||
#define IOMMU_TTE_PHYS_MASK_8K 0x1ffffffe000
|
||||
#define IOMMU_TTE_PHYS_MASK_64K 0x1ffffff8000
|
||||
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_8M 0x00000000007fe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_16M 0x0000000000ffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_32M 0x0000000001ffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_64M 0x0000000003ffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_128M 0x0000000007ffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_256M 0x000000000fffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_512M 0x000000001fffe000ULL
|
||||
#define IOMMU_TSB_8K_OFFSET_MASK_1G 0x000000003fffe000ULL
|
||||
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_64M 0x0000000003ff0000ULL
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_128M 0x0000000007ff0000ULL
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_256M 0x000000000fff0000ULL
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_512M 0x000000001fff0000ULL
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_1G 0x000000003fff0000ULL
|
||||
#define IOMMU_TSB_64K_OFFSET_MASK_2G 0x000000007fff0000ULL
|
||||
|
||||
typedef struct IOMMUState {
|
||||
AddressSpace iommu_as;
|
||||
MemoryRegion iommu;
|
||||
|
||||
uint64_t regs[IOMMU_NREGS];
|
||||
} IOMMUState;
|
||||
|
||||
#define TYPE_APB "pbm"
|
||||
|
||||
#define APB_DEVICE(obj) \
|
||||
|
@ -83,7 +138,7 @@ typedef struct APBState {
|
|||
MemoryRegion pci_mmio;
|
||||
MemoryRegion pci_ioport;
|
||||
uint64_t pci_irq_in;
|
||||
uint32_t iommu[4];
|
||||
IOMMUState iommu;
|
||||
uint32_t pci_control[16];
|
||||
uint32_t pci_irq_map[8];
|
||||
uint32_t obio_irq_map[32];
|
||||
|
@ -141,10 +196,217 @@ static inline void pbm_clear_request(APBState *s, unsigned int irq_num)
|
|||
s->irq_request = NO_IRQ_REQUEST;
|
||||
}
|
||||
|
||||
static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||
{
|
||||
IOMMUState *is = opaque;
|
||||
|
||||
return &is->iommu_as;
|
||||
}
|
||||
|
||||
static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr)
|
||||
{
|
||||
IOMMUState *is = container_of(iommu, IOMMUState, iommu);
|
||||
hwaddr baseaddr, offset;
|
||||
uint64_t tte;
|
||||
uint32_t tsbsize;
|
||||
IOMMUTLBEntry ret = {
|
||||
.target_as = &address_space_memory,
|
||||
.iova = 0,
|
||||
.translated_addr = 0,
|
||||
.addr_mask = ~(hwaddr)0,
|
||||
.perm = IOMMU_NONE,
|
||||
};
|
||||
|
||||
if (!(is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_MMU_EN)) {
|
||||
/* IOMMU disabled, passthrough using standard 8K page */
|
||||
ret.iova = addr & IOMMU_PAGE_MASK_8K;
|
||||
ret.translated_addr = addr;
|
||||
ret.addr_mask = IOMMU_PAGE_MASK_8K;
|
||||
ret.perm = IOMMU_RW;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
baseaddr = is->regs[IOMMU_BASE >> 3];
|
||||
tsbsize = (is->regs[IOMMU_CTRL >> 3] >> IOMMU_CTRL_TSB_SHIFT) & 0x7;
|
||||
|
||||
if (is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_TBW_SIZE) {
|
||||
/* 64K */
|
||||
switch (tsbsize) {
|
||||
case 0:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_64M) >> 13;
|
||||
break;
|
||||
case 1:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_128M) >> 13;
|
||||
break;
|
||||
case 2:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_256M) >> 13;
|
||||
break;
|
||||
case 3:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_512M) >> 13;
|
||||
break;
|
||||
case 4:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_1G) >> 13;
|
||||
break;
|
||||
case 5:
|
||||
offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_2G) >> 13;
|
||||
break;
|
||||
default:
|
||||
/* Not implemented, error */
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* 8K */
|
||||
switch (tsbsize) {
|
||||
case 0:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_8M) >> 10;
|
||||
break;
|
||||
case 1:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_16M) >> 10;
|
||||
break;
|
||||
case 2:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_32M) >> 10;
|
||||
break;
|
||||
case 3:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_64M) >> 10;
|
||||
break;
|
||||
case 4:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_128M) >> 10;
|
||||
break;
|
||||
case 5:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_256M) >> 10;
|
||||
break;
|
||||
case 6:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_512M) >> 10;
|
||||
break;
|
||||
case 7:
|
||||
offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_1G) >> 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tte = ldq_be_phys(&address_space_memory, baseaddr + offset);
|
||||
|
||||
if (!(tte & IOMMU_TTE_DATA_V)) {
|
||||
/* Invalid mapping */
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tte & IOMMU_TTE_DATA_W) {
|
||||
/* Writeable */
|
||||
ret.perm = IOMMU_RW;
|
||||
} else {
|
||||
ret.perm = IOMMU_RO;
|
||||
}
|
||||
|
||||
/* Extract phys */
|
||||
if (tte & IOMMU_TTE_DATA_SIZE) {
|
||||
/* 64K */
|
||||
ret.iova = addr & IOMMU_PAGE_MASK_64K;
|
||||
ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_64K;
|
||||
ret.addr_mask = (IOMMU_PAGE_SIZE_64K - 1);
|
||||
} else {
|
||||
/* 8K */
|
||||
ret.iova = addr & IOMMU_PAGE_MASK_8K;
|
||||
ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_8K;
|
||||
ret.addr_mask = (IOMMU_PAGE_SIZE_8K - 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static MemoryRegionIOMMUOps pbm_iommu_ops = {
|
||||
.translate = pbm_translate_iommu,
|
||||
};
|
||||
|
||||
static void iommu_config_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
IOMMUState *is = opaque;
|
||||
|
||||
IOMMU_DPRINTF("IOMMU config write: 0x%" HWADDR_PRIx " val: %" PRIx64
|
||||
" size: %d\n", addr, val, size);
|
||||
|
||||
switch (addr) {
|
||||
case IOMMU_CTRL:
|
||||
if (size == 4) {
|
||||
is->regs[IOMMU_CTRL >> 3] &= 0xffffffffULL;
|
||||
is->regs[IOMMU_CTRL >> 3] |= val << 32;
|
||||
} else {
|
||||
is->regs[IOMMU_CTRL] = val;
|
||||
}
|
||||
break;
|
||||
case IOMMU_CTRL + 0x4:
|
||||
is->regs[IOMMU_CTRL >> 3] &= 0xffffffff00000000ULL;
|
||||
is->regs[IOMMU_CTRL >> 3] |= val & 0xffffffffULL;
|
||||
break;
|
||||
case IOMMU_BASE:
|
||||
if (size == 4) {
|
||||
is->regs[IOMMU_BASE >> 3] &= 0xffffffffULL;
|
||||
is->regs[IOMMU_BASE >> 3] |= val << 32;
|
||||
} else {
|
||||
is->regs[IOMMU_BASE] = val;
|
||||
}
|
||||
break;
|
||||
case IOMMU_BASE + 0x4:
|
||||
is->regs[IOMMU_BASE >> 3] &= 0xffffffff00000000ULL;
|
||||
is->regs[IOMMU_BASE >> 3] |= val & 0xffffffffULL;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"apb iommu: Unimplemented register write "
|
||||
"reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n",
|
||||
addr, size, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t iommu_config_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IOMMUState *is = opaque;
|
||||
uint64_t val;
|
||||
|
||||
switch (addr) {
|
||||
case IOMMU_CTRL:
|
||||
if (size == 4) {
|
||||
val = is->regs[IOMMU_CTRL >> 3] >> 32;
|
||||
} else {
|
||||
val = is->regs[IOMMU_CTRL >> 3];
|
||||
}
|
||||
break;
|
||||
case IOMMU_CTRL + 0x4:
|
||||
val = is->regs[IOMMU_CTRL >> 3] & 0xffffffffULL;
|
||||
break;
|
||||
case IOMMU_BASE:
|
||||
if (size == 4) {
|
||||
val = is->regs[IOMMU_BASE >> 3] >> 32;
|
||||
} else {
|
||||
val = is->regs[IOMMU_BASE >> 3];
|
||||
}
|
||||
break;
|
||||
case IOMMU_BASE + 0x4:
|
||||
val = is->regs[IOMMU_BASE >> 3] & 0xffffffffULL;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"apb iommu: Unimplemented register read "
|
||||
"reg 0x%" HWADDR_PRIx " size 0x%x\n",
|
||||
addr, size);
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
IOMMU_DPRINTF("IOMMU config read: 0x%" HWADDR_PRIx " val: %" PRIx64
|
||||
" size: %d\n", addr, val, size);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void apb_config_writel (void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
APBState *s = opaque;
|
||||
IOMMUState *is = &s->iommu;
|
||||
|
||||
APB_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val);
|
||||
|
||||
|
@ -152,10 +414,8 @@ static void apb_config_writel (void *opaque, hwaddr addr,
|
|||
case 0x30 ... 0x4f: /* DMA error registers */
|
||||
/* XXX: not implemented yet */
|
||||
break;
|
||||
case 0x200 ... 0x20b: /* IOMMU */
|
||||
s->iommu[(addr & 0xf) >> 2] = val;
|
||||
break;
|
||||
case 0x20c ... 0x3ff: /* IOMMU flush */
|
||||
case 0x200 ... 0x217: /* IOMMU */
|
||||
iommu_config_write(is, (addr & 0xf), val, size);
|
||||
break;
|
||||
case 0xc00 ... 0xc3f: /* PCI interrupt control */
|
||||
if (addr & 4) {
|
||||
|
@ -228,6 +488,7 @@ static uint64_t apb_config_readl (void *opaque,
|
|||
hwaddr addr, unsigned size)
|
||||
{
|
||||
APBState *s = opaque;
|
||||
IOMMUState *is = &s->iommu;
|
||||
uint32_t val;
|
||||
|
||||
switch (addr & 0xffff) {
|
||||
|
@ -235,11 +496,8 @@ static uint64_t apb_config_readl (void *opaque,
|
|||
val = 0;
|
||||
/* XXX: not implemented yet */
|
||||
break;
|
||||
case 0x200 ... 0x20b: /* IOMMU */
|
||||
val = s->iommu[(addr & 0xf) >> 2];
|
||||
break;
|
||||
case 0x20c ... 0x3ff: /* IOMMU flush */
|
||||
val = 0;
|
||||
case 0x200 ... 0x217: /* IOMMU */
|
||||
val = iommu_config_read(is, (addr & 0xf), size);
|
||||
break;
|
||||
case 0xc00 ... 0xc3f: /* PCI interrupt control */
|
||||
if (addr & 4) {
|
||||
|
@ -390,6 +648,7 @@ PCIBus *pci_apb_init(hwaddr special_base,
|
|||
SysBusDevice *s;
|
||||
PCIHostState *phb;
|
||||
APBState *d;
|
||||
IOMMUState *is;
|
||||
PCIDevice *pci_dev;
|
||||
PCIBridge *br;
|
||||
|
||||
|
@ -420,6 +679,15 @@ PCIBus *pci_apb_init(hwaddr special_base,
|
|||
|
||||
pci_create_simple(phb->bus, 0, "pbm-pci");
|
||||
|
||||
/* APB IOMMU */
|
||||
is = &d->iommu;
|
||||
memset(is, 0, sizeof(IOMMUState));
|
||||
|
||||
memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops,
|
||||
"iommu-apb", UINT64_MAX);
|
||||
address_space_init(&is->iommu_as, &is->iommu, "pbm-as");
|
||||
pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is);
|
||||
|
||||
/* APB secondary busses */
|
||||
pci_dev = pci_create_multifunction(phb->bus, PCI_DEVFN(1, 0), true,
|
||||
"pbm-bridge");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue