@@ -52,6 +52,9 @@ int chsc_sei_nt2_get_event(void *res)
eccdf = (PciCcdfErr *)nt2_res->ccdf;
eccdf->fid = cpu_to_be32(sei_cont->fid);
eccdf->fh = cpu_to_be32(sei_cont->fh);
+ eccdf->e = cpu_to_be32(sei_cont->e);
+ eccdf->faddr = cpu_to_be64(sei_cont->faddr);
+ eccdf->pec = cpu_to_be16(sei_cont->pec);
break;
case 2: /* availability event */
accdf = (PciCcdfAvail *)nt2_res->ccdf;
@@ -184,8 +187,8 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
return NULL;
}
-static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
- uint32_t fid)
+static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
+ uint32_t fid, uint64_t faddr, uint32_t e)
{
SeiContainer *sei_cont = g_malloc0(sizeof(SeiContainer));
S390pciState *s = S390_PCI_HOST_BRIDGE(
@@ -197,13 +200,28 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
sei_cont->fh = fh;
sei_cont->fid = fid;
- sei_cont->cc = 2;
+ sei_cont->cc = cc;
sei_cont->pec = pec;
+ sei_cont->faddr = faddr;
+ sei_cont->e = e;
QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link);
css_generate_css_crws(0);
}
+static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
+ uint32_t fid)
+{
+ s390_pci_generate_event(2, pec, fh, fid, 0, 0);
+}
+
+static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh,
+ uint32_t fid, uint64_t faddr,
+ uint32_t e)
+{
+ s390_pci_generate_event(1, pec, fh, fid, faddr, e);
+}
+
static void s390_pci_set_irq(void *opaque, int irq, int level)
{
/* nothing to do */
@@ -313,10 +331,30 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
return ret;
}
+ if (!pbdev->g_iota) {
+ pbdev->error_state = true;
+ pbdev->lgstg_blocked = true;
+ s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
+ addr, 0);
+ return ret;
+ }
+
+ if (addr < pbdev->pba || addr > pbdev->pal) {
+ pbdev->error_state = true;
+ pbdev->lgstg_blocked = true;
+ s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
+ addr, 0);
+ return ret;
+ }
+
pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
addr);
if (!pte) {
+ pbdev->error_state = true;
+ pbdev->lgstg_blocked = true;
+ s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
+ addr, ERR_EVENT_Q_BIT);
return ret;
}
@@ -353,7 +391,7 @@ static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
ind_addr = cpu_physical_memory_map(ind_loc, &len, 1);
if (!ind_addr) {
- error_report("%s: unable to access indicator", __func__);
+ s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0);
return -1;
}
do {
@@ -374,12 +412,14 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
uint32_t vec = data & ZPCI_MSI_VEC_MASK;
uint64_t ind_bit;
uint32_t sum_bit;
+ uint32_t e = 0;
DPRINTF("write_msix data 0x%lx fid %d vec 0x%x\n", data, fid, vec);
pbdev = s390_pci_find_dev_by_fid(fid);
if (!pbdev) {
- DPRINTF("msix_notify no dev\n");
+ e |= (vec << ERR_EVENT_MVN_OFFSET);
+ s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e);
return;
}
@@ -33,6 +33,31 @@
#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
#define HP_EVENT_STANDBY_TO_RESERVED 0x0308
+#define ERR_EVENT_INVALAS 0x1
+#define ERR_EVENT_OORANGE 0x2
+#define ERR_EVENT_INVALTF 0x3
+#define ERR_EVENT_TPROTE 0x4
+#define ERR_EVENT_APROTE 0x5
+#define ERR_EVENT_KEYE 0x6
+#define ERR_EVENT_INVALTE 0x7
+#define ERR_EVENT_INVALTL 0x8
+#define ERR_EVENT_TT 0x9
+#define ERR_EVENT_INVALMS 0xa
+#define ERR_EVENT_SERR 0xb
+#define ERR_EVENT_NOMSI 0x10
+#define ERR_EVENT_INVALBV 0x11
+#define ERR_EVENT_AIBV 0x12
+#define ERR_EVENT_AIRERR 0x13
+#define ERR_EVENT_FMBA 0x2a
+#define ERR_EVENT_FMBUP 0x2b
+#define ERR_EVENT_FMBPRO 0x2c
+#define ERR_EVENT_CCONF 0x30
+#define ERR_EVENT_SERVAC 0x3a
+#define ERR_EVENT_PERMERR 0x3b
+
+#define ERR_EVENT_Q_BIT 0x2
+#define ERR_EVENT_MVN_OFFSET 16
+
#define ZPCI_MSI_VEC_BITS 11
#define ZPCI_MSI_VEC_MASK 0x7ff
@@ -130,13 +155,15 @@ typedef struct SeiContainer {
uint32_t fh;
uint8_t cc;
uint16_t pec;
+ uint64_t faddr;
+ uint32_t e;
} SeiContainer;
typedef struct PciCcdfErr {
uint32_t reserved1;
uint32_t fh;
uint32_t fid;
- uint32_t reserved2;
+ uint32_t e;
uint64_t faddr;
uint32_t reserved3;
uint16_t reserved4;
@@ -189,10 +216,16 @@ typedef struct S390MsixInfo {
typedef struct S390PCIBusDevice {
PCIDevice *pdev;
bool configured;
+ bool error_state;
+ bool lgstg_blocked;
uint32_t fh;
uint32_t fid;
uint64_t g_iota;
+ uint64_t pba;
+ uint64_t pal;
uint8_t isc;
+ uint16_t noi;
+ uint8_t sum;
S390MsixInfo msix;
AdapterRoutes routes;
AddressSpace as;
@@ -230,6 +230,8 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
break;
case CLP_SET_DISABLE_PCI_FN:
pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET);
+ pbdev->error_state = false;
+ pbdev->lgstg_blocked = false;
stl_p(&ressetpci->fh, pbdev->fh);
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
break;
@@ -330,6 +332,12 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
return 0;
}
+ if (pbdev->lgstg_blocked) {
+ setcc(cpu, ZPCI_PCI_LS_ERR);
+ s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
+ return 0;
+ }
+
if (pcias < 6) {
if ((8 - (offset & 0x7)) < len) {
program_interrupt(env, PGM_OPERAND, 4);
@@ -440,6 +448,12 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
return 0;
}
+ if (pbdev->lgstg_blocked) {
+ setcc(cpu, ZPCI_PCI_LS_ERR);
+ s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
+ return 0;
+ }
+
data = env->regs[r1];
if (pcias < 6) {
if ((8 - (offset & 0x7)) < len) {
@@ -586,6 +600,12 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
return 0;
}
+ if (pbdev->lgstg_blocked) {
+ setcc(cpu, ZPCI_PCI_LS_ERR);
+ s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
+ return 0;
+ }
+
mr = pbdev->pdev->io_regions[pcias].memory;
if (!memory_region_access_valid(mr, env->regs[r3], len, true)) {
program_interrupt(env, PGM_ADDRESSING, 6);
@@ -621,6 +641,9 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv);
pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data));
+ pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data));
+ pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data));
+ pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data));
DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
return 0;
@@ -638,11 +661,47 @@ static int dereg_irqs(S390PCIBusDevice *pbdev)
pbdev->routes.adapter.summary_offset = 0;
pbdev->routes.adapter.ind_addr = 0;
pbdev->routes.adapter.ind_offset = 0;
+ pbdev->isc = 0;
+ pbdev->noi = 0;
+ pbdev->sum = 0;
DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
return 0;
}
+static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
+{
+ uint64_t pba = ldq_p(&fib.pba);
+ uint64_t pal = ldq_p(&fib.pal);
+ uint64_t g_iota = ldq_p(&fib.iota);
+ uint8_t dt = (g_iota >> 2) & 0x7;
+ uint8_t t = (g_iota >> 11) & 0x1;
+
+ if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
+ program_interrupt(env, PGM_OPERAND, 6);
+ return -EINVAL;
+ }
+
+ /* currently we only support designation type 1 with translation */
+ if (!(dt == ZPCI_IOTA_RTTO && t)) {
+ error_report("unsupported ioat dt %d t %d", dt, t);
+ program_interrupt(env, PGM_OPERAND, 6);
+ return -EINVAL;
+ }
+
+ pbdev->pba = pba;
+ pbdev->pal = pal;
+ pbdev->g_iota = g_iota;
+ return 0;
+}
+
+static void dereg_ioat(S390PCIBusDevice *pbdev)
+{
+ pbdev->pba = 0;
+ pbdev->pal = 0;
+ pbdev->g_iota = 0;
+}
+
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
{
CPUS390XState *env = &cpu->env;
@@ -650,6 +709,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
uint32_t fh;
ZpciFib fib;
S390PCIBusDevice *pbdev;
+ uint64_t cc = ZPCI_PCI_LS_OK;
cpu_synchronize_state(CPU(cpu));
@@ -676,36 +736,42 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
cpu_physical_memory_read(fiba, (uint8_t *)&fib, sizeof(fib));
switch (oc) {
- case ZPCI_MOD_FC_REG_INT: {
- pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data));
- reg_irqs(env, pbdev, fib);
+ case ZPCI_MOD_FC_REG_INT:
+ if (reg_irqs(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
+ }
break;
- }
case ZPCI_MOD_FC_DEREG_INT:
dereg_irqs(pbdev);
break;
case ZPCI_MOD_FC_REG_IOAT:
- if (ldq_p(&fib.pba) > ldq_p(&fib.pal)) {
- program_interrupt(&cpu->env, PGM_OPERAND, 6);
- return 0;
+ if (reg_ioat(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
}
- pbdev->g_iota = ldq_p(&fib.iota);
break;
case ZPCI_MOD_FC_DEREG_IOAT:
+ dereg_ioat(pbdev);
break;
case ZPCI_MOD_FC_REREG_IOAT:
+ dereg_ioat(pbdev);
+ if (reg_ioat(env, pbdev, fib)) {
+ cc = ZPCI_PCI_LS_ERR;
+ }
break;
case ZPCI_MOD_FC_RESET_ERROR:
+ pbdev->error_state = false;
+ pbdev->lgstg_blocked = false;
break;
case ZPCI_MOD_FC_RESET_BLOCK:
+ pbdev->lgstg_blocked = false;
break;
case ZPCI_MOD_FC_SET_MEASURE:
break;
default:
program_interrupt(&cpu->env, PGM_OPERAND, 6);
- return 0;
+ cc = ZPCI_PCI_LS_ERR;
}
- setcc(cpu, ZPCI_PCI_LS_OK);
+ setcc(cpu, cc);
return 0;
}