diff options
40 files changed, 1366 insertions, 766 deletions
diff --git a/block/nvme.c b/block/nvme.c index e8dbbc2317..abfe305baf 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -176,12 +176,11 @@ static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, return false; } memset(q->queue, 0, bytes); - r = qemu_vfio_dma_map(s->vfio, q->queue, bytes, false, &q->iova); + r = qemu_vfio_dma_map(s->vfio, q->queue, bytes, false, &q->iova, errp); if (r) { - error_setg(errp, "Cannot map queue"); - return false; + error_prepend(errp, "Cannot map queue: "); } - return true; + return r == 0; } static void nvme_free_queue_pair(NVMeQueuePair *q) @@ -220,6 +219,7 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, q = g_try_new0(NVMeQueuePair, 1); if (!q) { + error_setg(errp, "Cannot allocate queue pair"); return NULL; } trace_nvme_create_queue_pair(idx, q, size, aio_context, @@ -228,6 +228,7 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, qemu_real_host_page_size); q->prp_list_pages = qemu_try_memalign(qemu_real_host_page_size, bytes); if (!q->prp_list_pages) { + error_setg(errp, "Cannot allocate PRP page list"); goto fail; } memset(q->prp_list_pages, 0, bytes); @@ -237,8 +238,9 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, qemu_co_queue_init(&q->free_req_queue); q->completion_bh = aio_bh_new(aio_context, nvme_process_completion_bh, q); r = qemu_vfio_dma_map(s->vfio, q->prp_list_pages, bytes, - false, &prp_list_iova); + false, &prp_list_iova, errp); if (r) { + error_prepend(errp, "Cannot map buffer for DMA: "); goto fail; } q->free_req_head = -1; @@ -531,9 +533,9 @@ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) error_setg(errp, "Cannot allocate buffer for identify response"); goto out; } - r = qemu_vfio_dma_map(s->vfio, id, id_size, true, &iova); + r = qemu_vfio_dma_map(s->vfio, id, id_size, true, &iova, errp); if (r) { - error_setg(errp, "Cannot map buffer for DMA"); + error_prepend(errp, "Cannot map buffer for DMA: "); goto out; } @@ -1017,6 +1019,7 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd, uint64_t *pagelist = req->prp_list_page; int i, j, r; int entries = 0; + Error *local_err = NULL, **errp = NULL; assert(qiov->size); assert(QEMU_IS_ALIGNED(qiov->size, s->page_size)); @@ -1029,7 +1032,7 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd, try_map: r = qemu_vfio_dma_map(s->vfio, qiov->iov[i].iov_base, - len, true, &iova); + len, true, &iova, errp); if (r == -ENOSPC) { /* * In addition to the -ENOMEM error, the VFIO_IOMMU_MAP_DMA @@ -1064,6 +1067,8 @@ try_map: goto fail; } } + errp = &local_err; + goto try_map; } if (r) { @@ -1107,6 +1112,9 @@ fail: * because they are already mapped before calling this function; for * temporary mappings, a later nvme_cmd_(un)map_qiov will reclaim by * calling qemu_vfio_dma_reset_temporary when necessary. */ + if (local_err) { + error_reportf_err(local_err, "Cannot map buffer for DMA: "); + } return r; } @@ -1521,14 +1529,15 @@ static void nvme_aio_unplug(BlockDriverState *bs) static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size) { int ret; + Error *local_err = NULL; BDRVNVMeState *s = bs->opaque; - ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL); + ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, &local_err); if (ret) { /* FIXME: we may run out of IOVA addresses after repeated * bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap * doesn't reclaim addresses for fixed mappings. */ - error_report("nvme_register_buf failed: %s", strerror(-ret)); + error_reportf_err(local_err, "nvme_register_buf failed: "); } } diff --git a/block/trace-events b/block/trace-events index b3d2b1e62c..f4f1267c8c 100644 --- a/block/trace-events +++ b/block/trace-events @@ -156,7 +156,7 @@ nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset 0x%"PRIx64" byte nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset 0x%"PRIx64" bytes %"PRId64" ret %d" nvme_dma_map_flush(void *s) "s %p" nvme_free_req_queue_wait(void *s, unsigned q_index) "s %p q #%u" -nvme_create_queue_pair(unsigned q_index, void *q, unsigned size, void *aio_context, int fd) "index %u q %p size %u aioctx %p fd %d" +nvme_create_queue_pair(unsigned q_index, void *q, size_t size, void *aio_context, int fd) "index %u q %p size %zu aioctx %p fd %d" nvme_free_queue_pair(unsigned q_index, void *q) "index %u q %p" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 8d67b6c076..50c8e8dabc 100644 --- a/docs/devel/tcg-icount.rst +++ b/docs/devel/tcg-icount.rst @@ -92,6 +92,3 @@ When the translator is handling an instruction of this kind: } * it must end the TB immediately after this instruction - -Note that some older front-ends call a "gen_io_end()" function: -this is obsolete and should not be used. diff --git a/hw/char/escc.c b/hw/char/escc.c index 52e7978287..0fce4f6324 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -86,12 +86,15 @@ #define W_INTR 1 #define INTR_INTALL 0x01 #define INTR_TXINT 0x02 +#define INTR_PAR_SPEC 0x04 #define INTR_RXMODEMSK 0x18 #define INTR_RXINT1ST 0x08 #define INTR_RXINTALL 0x10 +#define INTR_WTRQ_TXRX 0x20 #define W_IVEC 2 #define W_RXCTRL 3 #define RXCTRL_RXEN 0x01 +#define RXCTRL_HUNT 0x10 #define W_TXCTRL1 4 #define TXCTRL1_PAREN 0x01 #define TXCTRL1_PAREV 0x02 @@ -105,6 +108,7 @@ #define TXCTRL1_CLK64X 0xc0 #define TXCTRL1_CLKMSK 0xc0 #define W_TXCTRL2 5 +#define TXCTRL2_TXCRC 0x01 #define TXCTRL2_TXEN 0x08 #define TXCTRL2_BITMSK 0x60 #define TXCTRL2_5BITS 0x00 @@ -115,18 +119,27 @@ #define W_SYNC2 7 #define W_TXBUF 8 #define W_MINTR 9 +#define MINTR_VIS 0x01 +#define MINTR_NV 0x02 #define MINTR_STATUSHI 0x10 +#define MINTR_SOFTIACK 0x20 #define MINTR_RST_MASK 0xc0 #define MINTR_RST_B 0x40 #define MINTR_RST_A 0x80 #define MINTR_RST_ALL 0xc0 #define W_MISC1 10 +#define MISC1_ENC_MASK 0x60 #define W_CLOCK 11 #define CLOCK_TRXC 0x08 #define W_BRGLO 12 #define W_BRGHI 13 #define W_MISC2 14 -#define MISC2_PLLDIS 0x30 +#define MISC2_BRG_EN 0x01 +#define MISC2_BRG_SRC 0x02 +#define MISC2_LCL_LOOP 0x10 +#define MISC2_PLLCMD0 0x20 +#define MISC2_PLLCMD1 0x40 +#define MISC2_PLLCMD2 0x80 #define W_EXTINT 15 #define EXTINT_DCD 0x08 #define EXTINT_SYNCINT 0x10 @@ -170,6 +183,7 @@ #define R_RXBUF 8 #define R_RXCTRL 9 #define R_MISC 10 +#define MISC_2CLKMISS 0x40 #define R_MISC1 11 #define R_BRGLO 12 #define R_BRGHI 13 @@ -230,20 +244,23 @@ static uint32_t get_queue(void *opaque) q->count--; } trace_escc_get_queue(CHN_C(s), val); - if (q->count > 0) + if (q->count > 0) { serial_receive_byte(s, 0); + } return val; } static int escc_update_irq_chn(ESCCChannelState *s) { if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || - // tx ints enabled, pending - ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || - ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && - s->rxint == 1) || // rx ints enabled, pending - ((s->wregs[W_EXTINT] & EXTINT_BRKINT) && - (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p + /* tx ints enabled, pending */ + ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || + ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && + s->rxint == 1) || + /* rx ints enabled, pending */ + ((s->wregs[W_EXTINT] & EXTINT_BRKINT) && + (s->rregs[R_STATUS] & STATUS_BRK)))) { + /* break int e&p */ return 1; } return 0; @@ -262,26 +279,7 @@ static void escc_update_irq(ESCCChannelState *s) static void escc_reset_chn(ESCCChannelState *s) { - int i; - s->reg = 0; - for (i = 0; i < ESCC_SERIAL_REGS; i++) { - s->rregs[i] = 0; - s->wregs[i] = 0; - } - s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity - s->wregs[W_MINTR] = MINTR_RST_ALL; - s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC - s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled - s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT | - EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts - if (s->disabled) - s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC | - STATUS_CTS | STATUS_TXUNDRN; - else - s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN; - s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT; - s->rx = s->tx = 0; s->rxint = s->txint = 0; s->rxint_under_svc = s->txint_under_svc = 0; @@ -289,32 +287,99 @@ static void escc_reset_chn(ESCCChannelState *s) clear_queue(s); } +static void escc_soft_reset_chn(ESCCChannelState *s) +{ + escc_reset_chn(s); + + s->wregs[W_CMD] = 0; + s->wregs[W_INTR] &= INTR_PAR_SPEC | INTR_WTRQ_TXRX; + s->wregs[W_RXCTRL] &= ~RXCTRL_RXEN; + /* 1 stop bit */ + s->wregs[W_TXCTRL1] |= TXCTRL1_1STOP; + s->wregs[W_TXCTRL2] &= TXCTRL2_TXCRC | TXCTRL2_8BITS; + s->wregs[W_MINTR] &= ~MINTR_SOFTIACK; + s->wregs[W_MISC1] &= MISC1_ENC_MASK; + /* PLL disabled */ + s->wregs[W_MISC2] &= MISC2_BRG_EN | MISC2_BRG_SRC | + MISC2_PLLCMD1 | MISC2_PLLCMD2; + s->wregs[W_MISC2] |= MISC2_PLLCMD0; + /* Enable most interrupts */ + s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT | + EXTINT_TXUNDRN | EXTINT_BRKINT; + + s->rregs[R_STATUS] &= STATUS_DCD | STATUS_SYNC | STATUS_CTS | STATUS_BRK; + s->rregs[R_STATUS] |= STATUS_TXEMPTY | STATUS_TXUNDRN; + if (s->disabled) { + s->rregs[R_STATUS] |= STATUS_DCD | STATUS_SYNC | STATUS_CTS; + } + s->rregs[R_SPEC] &= SPEC_ALLSENT; + s->rregs[R_SPEC] |= SPEC_BITS8; + s->rregs[R_INTR] = 0; + s->rregs[R_MISC] &= MISC_2CLKMISS; +} + +static void escc_hard_reset_chn(ESCCChannelState *s) +{ + escc_soft_reset_chn(s); + + /* + * Hard reset is almost identical to soft reset above, except that the + * values of WR9 (W_MINTR), WR10 (W_MISC1), WR11 (W_CLOCK) and WR14 + * (W_MISC2) have extra bits forced to 0/1 + */ + s->wregs[W_MINTR] &= MINTR_VIS | MINTR_NV; + s->wregs[W_MINTR] |= MINTR_RST_B | MINTR_RST_A; + s->wregs[W_MISC1] = 0; + s->wregs[W_CLOCK] = CLOCK_TRXC; + s->wregs[W_MISC2] &= MISC2_PLLCMD1 | MISC2_PLLCMD2; + s->wregs[W_MISC2] |= MISC2_LCL_LOOP | MISC2_PLLCMD0; +} + static void escc_reset(DeviceState *d) { ESCCState *s = ESCC(d); + int i, j; - escc_reset_chn(&s->chn[0]); - escc_reset_chn(&s->chn[1]); + for (i = 0; i < 2; i++) { + ESCCChannelState *cs = &s->chn[i]; + + /* + * According to the ESCC datasheet "Miscellaneous Questions" section + * on page 384, the values of the ESCC registers are not guaranteed on + * power-on until an explicit hardware or software reset has been + * issued. For now we zero the registers so that a device reset always + * returns the emulated device to a fixed state. + */ + for (j = 0; j < ESCC_SERIAL_REGS; j++) { + cs->rregs[j] = 0; + cs->wregs[j] = 0; + } + escc_reset_chn(cs); + } } static inline void set_rxint(ESCCChannelState *s) { s->rxint = 1; - /* XXX: missing daisy chainnig: escc_chn_b rx should have a lower priority - than chn_a rx/tx/special_condition service*/ + /* + * XXX: missing daisy chaining: escc_chn_b rx should have a lower priority + * than chn_a rx/tx/special_condition service + */ s->rxint_under_svc = 1; if (s->chn == escc_chn_a) { s->rregs[R_INTR] |= INTR_RXINTA; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; - else + } else { s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; + } } else { s->otherchn->rregs[R_INTR] |= INTR_RXINTB; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->rregs[R_IVEC] = IVEC_HIRXINTB; - else + } else { s->rregs[R_IVEC] = IVEC_LORXINTB; + } } escc_update_irq(s); } @@ -328,17 +393,18 @@ static inline void set_txint(ESCCChannelState *s) if (s->wregs[W_INTR] & INTR_TXINT) { s->rregs[R_INTR] |= INTR_TXINTA; } - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; - else + } else { s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; + } } else { s->rregs[R_IVEC] = IVEC_TXINTB; if (s->wregs[W_INTR] & INTR_TXINT) { s->otherchn->rregs[R_INTR] |= INTR_TXINTB; } } - escc_update_irq(s); + escc_update_irq(s); } } @@ -347,20 +413,23 @@ static inline void clr_rxint(ESCCChannelState *s) s->rxint = 0; s->rxint_under_svc = 0; if (s->chn == escc_chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; - else + } else { s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; + } s->rregs[R_INTR] &= ~INTR_RXINTA; } else { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->rregs[R_IVEC] = IVEC_HINOINT; - else + } else { s->rregs[R_IVEC] = IVEC_LONOINT; + } s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB; } - if (s->txint) + if (s->txint) { set_txint(s); + } escc_update_irq(s); } @@ -369,21 +438,24 @@ static inline void clr_txint(ESCCChannelState *s) s->txint = 0; s->txint_under_svc = 0; if (s->chn == escc_chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; - else + } else { s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; + } s->rregs[R_INTR] &= ~INTR_TXINTA; } else { s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) + if (s->wregs[W_MINTR] & MINTR_STATUSHI) { s->rregs[R_IVEC] = IVEC_HINOINT; - else + } else { s->rregs[R_IVEC] = IVEC_LONOINT; + } s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; } - if (s->rxint) + if (s->rxint) { set_rxint(s); + } escc_update_irq(s); } @@ -392,21 +464,24 @@ static void escc_update_parameters(ESCCChannelState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != escc_serial) + if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != escc_serial) { return; + } if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { - if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV) + if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV) { parity = 'E'; - else + } else { parity = 'O'; + } } else { parity = 'N'; } - if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP) + if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP) { stop_bits = 2; - else + } else { stop_bits = 1; + } switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) { case TXCTRL2_5BITS: data_bits = 5; @@ -487,7 +562,13 @@ static void escc_mem_write(void *opaque, hwaddr addr, break; } break; - case W_INTR ... W_RXCTRL: + case W_RXCTRL: + s->wregs[s->reg] = val; + if (val & RXCTRL_HUNT) { + s->rregs[R_STATUS] |= STATUS_SYNC; + } + break; + case W_INTR ... W_IVEC: case W_SYNC1 ... W_TXBUF: case W_MISC1 ... W_CLOCK: case W_MISC2 ... W_EXTINT: @@ -510,23 +591,28 @@ static void escc_mem_write(void *opaque, hwaddr addr, default: break; case MINTR_RST_B: - escc_reset_chn(&serial->chn[0]); + trace_escc_soft_reset_chn(CHN_C(&serial->chn[0])); + escc_soft_reset_chn(&serial->chn[0]); return; case MINTR_RST_A: - escc_reset_chn(&serial->chn[1]); + trace_escc_soft_reset_chn(CHN_C(&serial->chn[1])); + escc_soft_reset_chn(&serial->chn[1]); return; case MINTR_RST_ALL: - escc_reset(DEVICE(serial)); + trace_escc_hard_reset(); + escc_hard_reset_chn(&serial->chn[0]); + escc_hard_reset_chn(&serial->chn[1]); return; } break; default: break; } - if (s->reg == 0) + if (s->reg == 0) { s->reg = newreg; - else + } else { s->reg = 0; + } break; case SERIAL_DATA: trace_escc_mem_writeb_data(CHN_C(s), val); @@ -538,17 +624,19 @@ static void escc_mem_write(void *opaque, hwaddr addr, s->txint = 0; escc_update_irq(s); s->tx = val; - if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled + if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { /* tx enabled */ if (qemu_chr_fe_backend_connected(&s->chr)) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ qemu_chr_fe_write_all(&s->chr, &s->tx, 1); } else if (s->type == escc_kbd && !s->disabled) { handle_kbd_command(s, val); } } - s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty - s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent + s->rregs[R_STATUS] |= STATUS_TXEMPTY; /* Tx buffer empty */ + s->rregs[R_SPEC] |= SPEC_ALLSENT; /* All sent */ set_txint(s); break; default: @@ -606,12 +694,13 @@ static int serial_can_receive(void *opaque) ESCCChannelState *s = opaque; int ret; - if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled - || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV)) - // char already available + if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) /* Rx not enabled */ + || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV)) { + /* char already available */ ret = 0; - else + } else { ret = 1; + } return ret; } @@ -638,12 +727,13 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size) static void serial_event(void *opaque, QEMUChrEvent event) { ESCCChannelState *s = opaque; - if (event == CHR_EVENT_BREAK) + if (event == CHR_EVENT_BREAK) { serial_receive_break(s); + } } static const VMStateDescription vmstate_escc_chn = { - .name ="escc_chn", + .name = "escc_chn", .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { @@ -662,7 +752,7 @@ static const VMStateDescription vmstate_escc_chn = { }; static const VMStateDescription vmstate_escc = { - .name ="escc", + .name = "escc", .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { @@ -734,21 +824,21 @@ static QemuInputHandler sunkbd_handler = { static void handle_kbd_command(ESCCChannelState *s, int val) { trace_escc_kbd_command(val); - if (s->led_mode) { // Ignore led byte + if (s->led_mode) { /* Ignore led byte */ s->led_mode = 0; return; } switch (val) { - case 1: // Reset, return type code + case 1: /* Reset, return type code */ clear_queue(s); put_queue(s, 0xff); - put_queue(s, 4); // Type 4 + put_queue(s, 4); /* Type 4 */ put_queue(s, 0x7f); break; - case 0xe: // Set leds + case 0xe: /* Set leds */ s->led_mode = 1; break; - case 7: // Query layout + case 7: /* Query layout */ case 0xf: clear_queue(s); put_queue(s, 0xfe); @@ -768,34 +858,39 @@ static void sunmouse_event(void *opaque, trace_escc_sunmouse_event(dx, dy, buttons_state); ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */ - if (buttons_state & MOUSE_EVENT_LBUTTON) + if (buttons_state & MOUSE_EVENT_LBUTTON) { ch ^= 0x4; - if (buttons_state & MOUSE_EVENT_MBUTTON) + } + if (buttons_state & MOUSE_EVENT_MBUTTON) { ch ^= 0x2; - if (buttons_state & MOUSE_EVENT_RBUTTON) + } + if (buttons_state & MOUSE_EVENT_RBUTTON) { ch ^= 0x1; + } put_queue(s, ch); ch = dx; - if (ch > 127) + if (ch > 127) { ch = 127; - else if (ch < -127) + } else if (ch < -127) { ch = -127; + } put_queue(s, ch & 0xff); ch = -dy; - if (ch > 127) + if (ch > 127) { ch = 127; - else if (ch < -127) + } else if (ch < -127) { ch = -127; + } put_queue(s, ch & 0xff); - // MSC protocol specify two extra motion bytes + /* MSC protocol specifies two extra motion bytes */ put_queue(s, 0); put_queue(s, 0); diff --git a/hw/char/trace-events b/hw/char/trace-events index 1436fb462d..b774832af4 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -36,6 +36,8 @@ grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" va grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64 # escc.c +escc_hard_reset(void) "hard reset" +escc_soft_reset_chn(char channel) "soft reset channel %c" escc_put_queue(char channel, int b) "channel %c put: 0x%02x" escc_get_queue(char channel, int val) "channel %c get 0x%02x" escc_update_irq(int irq) "IRQ = %d" diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 8dd482c1f6..9376a8f4ce 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -74,7 +74,13 @@ #define MOUSE_STATUS_ENABLED 0x20 #define MOUSE_STATUS_SCALE21 0x10 -#define PS2_QUEUE_SIZE 16 /* Buffer size required by PS/2 protocol */ +/* + * PS/2 buffer size. Keep 256 bytes for compatibility with + * older QEMU versions. + */ +#define PS2_BUFFER_SIZE 256 +#define PS2_QUEUE_SIZE 16 /* Queue size required by PS/2 protocol */ +#define PS2_QUEUE_HEADROOM 8 /* Queue size for keyboard command replies */ /* Bits for 'modifiers' field in PS2KbdState */ #define MOD_CTRL_L (1 << 0) @@ -85,10 +91,8 @@ #define MOD_ALT_R (1 << 5) typedef struct { - /* Keep the data array 256 bytes long, which compatibility - with older qemu versions. */ - uint8_t data[256]; - int rptr, wptr, count; + uint8_t data[PS2_BUFFER_SIZE]; + int rptr, wptr, cwptr, count; } PS2Queue; struct PS2State { @@ -183,6 +187,7 @@ static void ps2_reset_queue(PS2State *s) q->rptr = 0; q->wptr = 0; + q->cwptr = -1; q->count = 0; } @@ -195,13 +200,14 @@ void ps2_queue_noirq(PS2State *s, int b) { PS2Queue *q = &s->queue; - if (q->count == PS2_QUEUE_SIZE) { + if (q->count >= PS2_QUEUE_SIZE) { return; } q->data[q->wptr] = b; - if (++q->wptr == PS2_QUEUE_SIZE) + if (++q->wptr == PS2_BUFFER_SIZE) { q->wptr = 0; + } q->count++; } @@ -256,6 +262,63 @@ void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4) ps2_raise_irq(s); } +static void ps2_cqueue_data(PS2Queue *q, int b) +{ + q->data[q->cwptr] = b; + if (++q->cwptr >= PS2_BUFFER_SIZE) { + q->cwptr = 0; + } + q->count++; +} + +static void ps2_cqueue_1(PS2State *s, int b1) +{ + PS2Queue *q = &s->queue; + + q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1); + q->cwptr = q->rptr; + ps2_cqueue_data(q, b1); + ps2_raise_irq(s); +} + +static void ps2_cqueue_2(PS2State *s, int b1, int b2) +{ + PS2Queue *q = &s->queue; + + q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1); + q->cwptr = q->rptr; + ps2_cqueue_data(q, b1); + ps2_cqueue_data(q, b2); + ps2_raise_irq(s); +} + +static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3) +{ + PS2Queue *q = &s->queue; + + q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1); + q->cwptr = q->rptr; + ps2_cqueue_data(q, b1); + ps2_cqueue_data(q, b2); + ps2_cqueue_data(q, b3); + ps2_raise_irq(s); +} + +static void ps2_cqueue_reset(PS2State *s) +{ + PS2Queue *q = &s->queue; + int ccount; + + if (q->cwptr == -1) { + return; + } + + ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1); + q->count -= ccount; + q->rptr = q->cwptr; + q->cwptr = -1; +} + /* keycode is the untranslated scancode in the current scancode set. */ static void ps2_put_keycode(void *opaque, int keycode) { @@ -509,14 +572,20 @@ uint32_t ps2_read_data(PS2State *s) (needed for EMM386) */ /* XXX: need a timer to do things correctly */ index = q->rptr - 1; - if (index < 0) - index = PS2_QUEUE_SIZE - 1; + if (index < 0) { + index = PS2_BUFFER_SIZE - 1; + } val = q->data[index]; } else { val = q->data[q->rptr]; - if (++q->rptr == PS2_QUEUE_SIZE) + if (++q->rptr == PS2_BUFFER_SIZE) { q->rptr = 0; + } q->count--; + if (q->rptr == q->cwptr) { + /* command reply queue is empty */ + q->cwptr = -1; + } /* reading deasserts IRQ */ s->update_irq(s->update_arg, 0); /* reassert IRQs if data left */ @@ -548,92 +617,83 @@ void ps2_write_keyboard(void *opaque, int val) PS2KbdState *s = (PS2KbdState *)opaque; trace_ps2_write_keyboard(opaque, val); + ps2_cqueue_reset(&s->common); switch(s->common.write_cmd) { default: case -1: switch(val) { case 0x00: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; case 0x05: - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: /* We emulate a MF2 AT keyboard here */ - if (s->translate) - ps2_queue_3(&s->common, - KBD_REPLY_ACK, - KBD_REPLY_ID, - 0x41); - else - ps2_queue_3(&s->common, - KBD_REPLY_ACK, - KBD_REPLY_ID, - 0x83); + ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID, + s->translate ? 0x41 : 0x83); break; case KBD_CMD_ECHO: - ps2_queue(&s->common, KBD_CMD_ECHO); + ps2_cqueue_1(&s->common, KBD_CMD_ECHO); break; case KBD_CMD_ENABLE: s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_SCANCODE: case KBD_CMD_SET_LEDS: case KBD_CMD_SET_RATE: case KBD_CMD_SET_MAKE_BREAK: s->common.write_cmd = val; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET_DISABLE: ps2_reset_keyboard(s); s->scan_enabled = 0; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET_ENABLE: ps2_reset_keyboard(s); s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_queue_2(&s->common, + ps2_cqueue_2(&s->common, KBD_REPLY_ACK, KBD_REPLY_POR); break; case KBD_CMD_SET_TYPEMATIC: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); break; default: - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); break; } break; case KBD_CMD_SET_MAKE_BREAK: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; case KBD_CMD_SCANCODE: if (val == 0) { - if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) { - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_put_keycode(s, s->scancode_set); - } + ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ? + translate_table[s->scancode_set] : s->scancode_set); } else if (val >= 1 && val <= 3) { s->scancode_set = val; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); } else { - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); } s->common.write_cmd = -1; break; case KBD_CMD_SET_LEDS: ps2_set_ledstate(s, val); - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; case KBD_CMD_SET_RATE: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; } @@ -926,30 +986,27 @@ static void ps2_common_reset(PS2State *s) static void ps2_common_post_load(PS2State *s) { PS2Queue *q = &s->queue; - uint8_t i, size; - uint8_t tmp_data[PS2_QUEUE_SIZE]; - - /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */ - size = q->count; - if (q->count < 0) { - size = 0; - } else if (q->count > PS2_QUEUE_SIZE) { - size = PS2_QUEUE_SIZE; - } + int ccount = 0; - /* move the queue elements to the start of data array */ - for (i = 0; i < size; i++) { - if (q->rptr < 0 || q->rptr >= sizeof(q->data)) { - q->rptr = 0; + /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */ + if (q->cwptr != -1) { + ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1); + if (ccount > PS2_QUEUE_HEADROOM) { + ccount = PS2_QUEUE_HEADROOM; } - tmp_data[i] = q->data[q->rptr++]; } - memcpy(q->data, tmp_data, size); - /* reset rptr/wptr/count */ - q->rptr = 0; - q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size; - q->count = size; + /* limit the scancode queue size to PS2_QUEUE_SIZE */ + if (q->count < ccount) { + q->count = ccount; + } else if (q->count > ccount + PS2_QUEUE_SIZE) { + q->count = ccount + PS2_QUEUE_SIZE; + } + + /* sanitize rptr and recalculate wptr and cwptr */ + q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1); + q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1); + q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } static void ps2_kbd_reset(void *opaque) @@ -1040,6 +1097,22 @@ static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = { } }; +static bool ps2_keyboard_cqueue_needed(void *opaque) +{ + PS2KbdState *s = opaque; + + return s->common.queue.cwptr != -1; /* the queue is mostly empty */ +} + +static const VMStateDescription vmstate_ps2_keyboard_cqueue = { + .name = "ps2kbd/command_reply_queue", + .needed = ps2_keyboard_cqueue_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32(common.queue.cwptr, PS2KbdState), + VMSTATE_END_OF_LIST() + } +}; + static int ps2_kbd_post_load(void* opaque, int version_id) { PS2KbdState *s = (PS2KbdState*)opaque; @@ -1053,22 +1126,11 @@ static int ps2_kbd_post_load(void* opaque, int version_id) return 0; } -static int ps2_kbd_pre_save(void *opaque) -{ - PS2KbdState *s = (PS2KbdState *)opaque; - PS2State *ps2 = &s->common; - - ps2_common_post_load(ps2); - - return 0; -} - static const VMStateDescription vmstate_ps2_keyboard = { .name = "ps2kbd", .version_id = 3, .minimum_version_id = 2, .post_load = ps2_kbd_post_load, - .pre_save = ps2_kbd_pre_save, .fields = (VMStateField[]) { VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State), VMSTATE_INT32(scan_enabled, PS2KbdState), @@ -1079,6 +1141,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { .subsections = (const VMStateDescription*[]) { &vmstate_ps2_keyboard_ledstate, &vmstate_ps2_keyboard_need_high_bit, + &vmstate_ps2_keyboard_cqueue, NULL } }; @@ -1093,22 +1156,11 @@ static int ps2_mouse_post_load(void *opaque, int version_id) return 0; } -static int ps2_mouse_pre_save(void *opaque) -{ - PS2MouseState *s = (PS2MouseState *)opaque; - PS2State *ps2 = &s->common; - - ps2_common_post_load(ps2); - - return 0; -} - static const VMStateDescription vmstate_ps2_mouse = { .name = "ps2mouse", .version_id = 2, .minimum_version_id = 2, .post_load = ps2_mouse_post_load, - .pre_save = ps2_mouse_pre_save, .fields = (VMStateField[]) { VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State), VMSTATE_UINT8(mouse_status, PS2MouseState), diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index ac0a13060b..5ba87f789c 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -225,7 +225,7 @@ static void q800_init(MachineState *machine) hwaddr parameters_base; CPUState *cs; DeviceState *dev; - DeviceState *via_dev; + DeviceState *via1_dev, *via2_dev; DeviceState *escc_orgate; SysBusESPState *sysbus_esp; ESPState *esp; @@ -270,28 +270,30 @@ static void q800_init(MachineState *machine) object_property_set_link(OBJECT(glue), "cpu", OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(glue), &error_fatal); - /* VIA */ - - via_dev = qdev_new(TYPE_MAC_VIA); + /* VIA 1 */ + via1_dev = qdev_new(TYPE_MOS6522_Q800_VIA1); dinfo = drive_get(IF_MTD, 0, 0); if (dinfo) { - qdev_prop_set_drive(via_dev, "drive", blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(via1_dev, "drive", blk_by_legacy_dinfo(dinfo)); } - sysbus = SYS_BUS_DEVICE(via_dev); + sysbus = SYS_BUS_DEVICE(via1_dev); sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, VIA_BASE); - qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 0, - qdev_get_gpio_in(glue, 0)); - qdev_connect_gpio_out_named(DEVICE(sysbus), "irq", 1, - qdev_get_gpio_in(glue, 1)); - + sysbus_mmio_map(sysbus, 1, VIA_BASE); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 0)); - adb_bus = qdev_get_child_bus(via_dev, "adb.0"); + adb_bus = qdev_get_child_bus(via1_dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); qdev_realize_and_unref(dev, adb_bus, &error_fatal); dev = qdev_new(TYPE_ADB_MOUSE); qdev_realize_and_unref(dev, adb_bus, &error_fatal); + /* VIA 2 */ + via2_dev = qdev_new(TYPE_MOS6522_Q800_VIA2); + sysbus = SYS_BUS_DEVICE(via2_dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 1, VIA_BASE + VIA_SIZE); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 1)); + /* MACSONIC */ if (nb_nics > 1) { @@ -375,12 +377,10 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in_named(via_dev, - "via2-irq", - VIA2_IRQ_SCSI_BIT)); - sysbus_connect_irq(sysbus, 1, - qdev_get_gpio_in_named(via_dev, "via2-irq", - VIA2_IRQ_SCSI_DATA_BIT)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_BIT)); + sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_DATA_BIT)); sysbus_mmio_map(sysbus, 0, ESP_BASE); sysbus_mmio_map(sysbus, 1, ESP_PDMA); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index ff0156db76..d1abcd97b5 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -34,11 +34,9 @@ #include "qemu/log.h" /* - * VIAs: There are two in every machine, + * VIAs: There are two in every machine */ -#define VIA_SIZE (0x2000) - /* * Not all of these are true post MacII I think. * CSA: probably the ones CHRP marks as 'unused' change purposes @@ -374,11 +372,10 @@ static void via2_irq_request(void *opaque, int irq, int level) } -static void pram_update(MacVIAState *m) +static void pram_update(MOS6522Q800VIA1State *v1s) { - if (m->blk) { - if (blk_pwrite(m->blk, 0, m->mos6522_via1.PRAM, - sizeof(m->mos6522_via1.PRAM), 0) < 0) { + if (v1s->blk) { + if (blk_pwrite(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM), 0) < 0) { qemu_log("pram_update: cannot write to file\n"); } } @@ -434,9 +431,8 @@ static int via1_rtc_compact_cmd(uint8_t value) return REG_INVALID; } -static void via1_rtc_update(MacVIAState *m) +static void via1_rtc_update(MOS6522Q800VIA1State *v1s) { - MOS6522Q800VIA1State *v1s = &m->mos6522_via1; MOS6522State *s = MOS6522(v1s); int cmd, sector, addr; uint32_t time; @@ -448,40 +444,40 @@ static void via1_rtc_update(MacVIAState *m) if (s->dirb & VIA1B_vRTCData) { /* send bits to the RTC */ if (!(v1s->last_b & VIA1B_vRTCClk) && (s->b & VIA1B_vRTCClk)) { - m->data_out <<= 1; - m->data_out |= s->b & VIA1B_vRTCData; - m->data_out_cnt++; + v1s->data_out <<= 1; + v1s->data_out |= s->b & VIA1B_vRTCData; + v1s->data_out_cnt++; } - trace_via1_rtc_update_data_out(m->data_out_cnt, m->data_out); + trace_via1_rtc_update_data_out(v1s->data_out_cnt, v1s->data_out); } else { - trace_via1_rtc_update_data_in(m->data_in_cnt, m->data_in); + trace_via1_rtc_update_data_in(v1s->data_in_cnt, v1s->data_in); /* receive bits from the RTC */ if ((v1s->last_b & VIA1B_vRTCClk) && !(s->b & VIA1B_vRTCClk) && - m->data_in_cnt) { + v1s->data_in_cnt) { s->b = (s->b & ~VIA1B_vRTCData) | - ((m->data_in >> 7) & VIA1B_vRTCData); - m->data_in <<= 1; - m->data_in_cnt--; + ((v1s->data_in >> 7) & VIA1B_vRTCData); + v1s->data_in <<= 1; + v1s->data_in_cnt--; } return; } - if (m->data_out_cnt != 8) { + if (v1s->data_out_cnt != 8) { return; } - m->data_out_cnt = 0; + v1s->data_out_cnt = 0; - trace_via1_rtc_internal_status(m->cmd, m->alt, m->data_out); + trace_via1_rtc_internal_status(v1s->cmd, v1s->alt, v1s->data_out); /* first byte: it's a command */ - if (m->cmd == REG_EMPTY) { + if (v1s->cmd == REG_EMPTY) { - cmd = via1_rtc_compact_cmd(m->data_out); + cmd = via1_rtc_compact_cmd(v1s->data_out); trace_via1_rtc_internal_cmd(cmd); if (cmd == REG_INVALID) { - trace_via1_rtc_cmd_invalid(m->data_out); + trace_via1_rtc_cmd_invalid(v1s->data_out); return; } @@ -493,20 +489,20 @@ static void via1_rtc_update(MacVIAState *m) * register 3 is highest-order byte */ - time = m->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time = v1s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / NANOSECONDS_PER_SECOND); trace_via1_rtc_internal_time(time); - m->data_in = (time >> ((cmd & 0x03) << 3)) & 0xff; - m->data_in_cnt = 8; + v1s->data_in = (time >> ((cmd & 0x03) << 3)) & 0xff; + v1s->data_in_cnt = 8; trace_via1_rtc_cmd_seconds_read((cmd & 0x7f) - REG_0, - m->data_in); + v1s->data_in); break; case REG_PRAM_ADDR...REG_PRAM_ADDR_LAST: /* PRAM address 0x00 -> 0x13 */ - m->data_in = v1s->PRAM[(cmd & 0x7f) - REG_PRAM_ADDR]; - m->data_in_cnt = 8; + v1s->data_in = v1s->PRAM[(cmd & 0x7f) - REG_PRAM_ADDR]; + v1s->data_in_cnt = 8; trace_via1_rtc_cmd_pram_read((cmd & 0x7f) - REG_PRAM_ADDR, - m->data_in); + v1s->data_in); break; case REG_PRAM_SECT...REG_PRAM_SECT_LAST: /* @@ -514,7 +510,7 @@ static void via1_rtc_update(MacVIAState *m) * the only two-byte read command */ trace_via1_rtc_internal_set_cmd(cmd); - m->cmd = cmd; + v1s->cmd = cmd; break; default: g_assert_not_reached(); @@ -524,9 +520,9 @@ static void via1_rtc_update(MacVIAState *m) } /* this is a write command, needs a parameter */ - if (cmd == REG_WPROTECT || !m->wprotect) { + if (cmd == REG_WPROTECT || !v1s->wprotect) { trace_via1_rtc_internal_set_cmd(cmd); - m->cmd = cmd; + v1s->cmd = cmd; } else { trace_via1_rtc_internal_ignore_cmd(cmd); } @@ -534,46 +530,47 @@ static void via1_rtc_update(MacVIAState *m) } /* second byte: it's a parameter */ - if (m->alt == REG_EMPTY) { - switch (m->cmd & 0x7f) { + if (v1s->alt == REG_EMPTY) { + switch (v1s->cmd & 0x7f) { case REG_0...REG_3: /* seconds register */ /* FIXME */ - trace_via1_rtc_cmd_seconds_write(m->cmd - REG_0, m->data_out); - m->cmd = REG_EMPTY; + trace_via1_rtc_cmd_seconds_write(v1s->cmd - REG_0, v1s->data_out); + v1s->cmd = REG_EMPTY; break; case REG_TEST: /* device control: nothing to do */ - trace_via1_rtc_cmd_test_write(m->data_out); - m->cmd = REG_EMPTY; + trace_via1_rtc_cmd_test_write(v1s->data_out); + v1s->cmd = REG_EMPTY; break; case REG_WPROTECT: /* Write Protect register */ - trace_via1_rtc_cmd_wprotect_write(m->data_out); - m->wprotect = !!(m->data_out & 0x80); - m->cmd = REG_EMPTY; + trace_via1_rtc_cmd_wprotect_write(v1s->data_out); + v1s->wprotect = !!(v1s->data_out & 0x80); + v1s->cmd = REG_EMPTY; break; case REG_PRAM_ADDR...REG_PRAM_ADDR_LAST: /* PRAM address 0x00 -> 0x13 */ - trace_via1_rtc_cmd_pram_write(m->cmd - REG_PRAM_ADDR, m->data_out); - v1s->PRAM[m->cmd - REG_PRAM_ADDR] = m->data_out; - pram_update(m); - m->cmd = REG_EMPTY; + trace_via1_rtc_cmd_pram_write(v1s->cmd - REG_PRAM_ADDR, + v1s->data_out); + v1s->PRAM[v1s->cmd - REG_PRAM_ADDR] = v1s->data_out; + pram_update(v1s); + v1s->cmd = REG_EMPTY; break; case REG_PRAM_SECT...REG_PRAM_SECT_LAST: - addr = (m->data_out >> 2) & 0x1f; - sector = (m->cmd & 0x7f) - REG_PRAM_SECT; - if (m->cmd & 0x80) { + addr = (v1s->data_out >> 2) & 0x1f; + sector = (v1s->cmd & 0x7f) - REG_PRAM_SECT; + if (v1s->cmd & 0x80) { /* it's a read */ - m->data_in = v1s->PRAM[sector * 32 + addr]; - m->data_in_cnt = 8; + v1s->data_in = v1s->PRAM[sector * 32 + addr]; + v1s->data_in_cnt = 8; trace_via1_rtc_cmd_pram_sect_read(sector, addr, sector * 32 + addr, - m->data_in); - m->cmd = REG_EMPTY; + v1s->data_in); + v1s->cmd = REG_EMPTY; } else { /* it's a write, we need one more parameter */ trace_via1_rtc_internal_set_alt(addr, sector, addr); - m->alt = addr; + v1s->alt = addr; } break; default: @@ -584,22 +581,21 @@ static void via1_rtc_update(MacVIAState *m) } /* third byte: it's the data of a REG_PRAM_SECT write */ - g_assert(REG_PRAM_SECT <= m->cmd && m->cmd <= REG_PRAM_SECT_LAST); - sector = m->cmd - REG_PRAM_SECT; - v1s->PRAM[sector * 32 + m->alt] = m->data_out; - pram_update(m); - trace_via1_rtc_cmd_pram_sect_write(sector, m->alt, sector * 32 + m->alt, - m->data_out); - m->alt = REG_EMPTY; - m->cmd = REG_EMPTY; + g_assert(REG_PRAM_SECT <= v1s->cmd && v1s->cmd <= REG_PRAM_SECT_LAST); + sector = v1s->cmd - REG_PRAM_SECT; + v1s->PRAM[sector * 32 + v1s->alt] = v1s->data_out; + pram_update(v1s); + trace_via1_rtc_cmd_pram_sect_write(sector, v1s->alt, sector * 32 + v1s->alt, + v1s->data_out); + v1s->alt = REG_EMPTY; + v1s->cmd = REG_EMPTY; } static void adb_via_poll(void *opaque) { - MacVIAState *m = opaque; - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); MOS6522State *s = MOS6522(v1s); - ADBBusState *adb_bus = &m->adb_bus; + ADBBusState *adb_bus = &v1s->adb_bus; uint8_t obuf[9]; uint8_t *data = &s->sr; int olen; @@ -611,50 +607,50 @@ static void adb_via_poll(void *opaque) */ adb_autopoll_block(adb_bus); - if (m->adb_data_in_size > 0 && m->adb_data_in_index == 0) { + if (v1s->adb_data_in_size > 0 && v1s->adb_data_in_index == 0) { /* * For older Linux kernels that switch to IDLE mode after sending the * ADB command, detect if there is an existing response and return that * as a a "fake" autopoll reply or bus timeout accordingly */ - *data = m->adb_data_out[0]; - olen = m->adb_data_in_size; + *data = v1s->adb_data_out[0]; + olen = v1s->adb_data_in_size; s->b &= ~VIA1B_vADBInt; - qemu_irq_raise(m->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); } else { /* * Otherwise poll as normal */ - m->adb_data_in_index = 0; - m->adb_data_out_index = 0; + v1s->adb_data_in_index = 0; + v1s->adb_data_out_index = 0; olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); if (olen > 0) { /* Autopoll response */ *data = obuf[0]; olen--; - memcpy(m->adb_data_in, &obuf[1], olen); - m->adb_data_in_size = olen; + memcpy(v1s->adb_data_in, &obuf[1], olen); + v1s->adb_data_in_size = olen; s->b &= ~VIA1B_vADBInt; - qemu_irq_raise(m->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); } else { - *data = m->adb_autopoll_cmd; + *data = v1s->adb_autopoll_cmd; obuf[0] = 0xff; obuf[1] = 0xff; olen = 2; - memcpy(m->adb_data_in, obuf, olen); - m->adb_data_in_size = olen; + memcpy(v1s->adb_data_in, obuf, olen); + v1s->adb_data_in_size = olen; s->b &= ~VIA1B_vADBInt; - qemu_irq_raise(m->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); } } trace_via1_adb_poll(*data, (s->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, m->adb_data_in_index, olen); + adb_bus->status, v1s->adb_data_in_index, olen); } static int adb_via_send_len(uint8_t data) @@ -687,11 +683,10 @@ static int adb_via_send_len(uint8_t data) } } -static void adb_via_send(MacVIAState *s, int state, uint8_t data) +static void adb_via_send(MOS6522Q800VIA1State *v1s, int state, uint8_t data) { - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); MOS6522State *ms = MOS6522(v1s); - ADBBusState *adb_bus = &s->adb_bus; + ADBBusState *adb_bus = &v1s->adb_bus; uint16_t autopoll_mask; switch (state) { @@ -707,22 +702,22 @@ static void adb_via_send(MacVIAState *s, int state, uint8_t data) ms->b &= ~VIA1B_vADBInt; } else { ms->b |= VIA1B_vADBInt; - s->adb_data_out_index = 0; - s->adb_data_out[s->adb_data_out_index++] = data; + v1s->adb_data_out_index = 0; + v1s->adb_data_out[v1s->adb_data_out_index++] = data; } trace_via1_adb_send(" NEW", data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); - qemu_irq_raise(s->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); break; case ADB_STATE_EVEN: case ADB_STATE_ODD: ms->b |= VIA1B_vADBInt; - s->adb_data_out[s->adb_data_out_index++] = data; + v1s->adb_data_out[v1s->adb_data_out_index++] = data; trace_via1_adb_send(state == ADB_STATE_EVEN ? "EVEN" : " ODD", data, (ms->b & VIA1B_vADBInt) ? "+" : "-"); - qemu_irq_raise(s->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); break; case ADB_STATE_IDLE: @@ -730,40 +725,39 @@ static void adb_via_send(MacVIAState *s, int state, uint8_t data) } /* If the command is complete, execute it */ - if (s->adb_data_out_index == adb_via_send_len(s->adb_data_out[0])) { - s->adb_data_in_size = adb_request(adb_bus, s->adb_data_in, - s->adb_data_out, - s->adb_data_out_index); - s->adb_data_in_index = 0; + if (v1s->adb_data_out_index == adb_via_send_len(v1s->adb_data_out[0])) { + v1s->adb_data_in_size = adb_request(adb_bus, v1s->adb_data_in, + v1s->adb_data_out, + v1s->adb_data_out_index); + v1s->adb_data_in_index = 0; if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { /* * Bus timeout (but allow first EVEN and ODD byte to indicate * timeout via vADBInt and SRQ status) */ - s->adb_data_in[0] = 0xff; - s->adb_data_in[1] = 0xff; - s->adb_data_in_size = 2; + v1s->adb_data_in[0] = 0xff; + v1s->adb_data_in[1] = 0xff; + v1s->adb_data_in_size = 2; } /* * If last command is TALK, store it for use by autopoll and adjust * the autopoll mask accordingly */ - if ((s->adb_data_out[0] & 0xc) == 0xc) { - s->adb_autopoll_cmd = s->adb_data_out[0]; + if ((v1s->adb_data_out[0] & 0xc) == 0xc) { + v1s->adb_autopoll_cmd = v1s->adb_data_out[0]; - autopoll_mask = 1 << (s->adb_autopoll_cmd >> 4); + autopoll_mask = 1 << (v1s->adb_autopoll_cmd >> 4); adb_set_autopoll_mask(adb_bus, autopoll_mask); } } } -static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) +static void adb_via_receive(MOS6522Q800VIA1State *v1s, int state, uint8_t *data) { - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1); MOS6522State *ms = MOS6522(v1s); - ADBBusState *adb_bus = &s->adb_bus; + ADBBusState *adb_bus = &v1s->adb_bus; uint16_t pending; switch (state) { @@ -777,16 +771,16 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) trace_via1_adb_receive("IDLE", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status, - s->adb_data_in_index, s->adb_data_in_size); + v1s->adb_data_in_index, v1s->adb_data_in_size); break; case ADB_STATE_EVEN: case ADB_STATE_ODD: - switch (s->adb_data_in_index) { + switch (v1s->adb_data_in_index) { case 0: /* First EVEN byte: vADBInt indicates bus timeout */ - *data = s->adb_data_in[s->adb_data_in_index]; + *data = v1s->adb_data_in[v1s->adb_data_in_index]; if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { ms->b &= ~VIA1B_vADBInt; } else { @@ -795,16 +789,16 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, s->adb_data_in_index, - s->adb_data_in_size); + adb_bus->status, v1s->adb_data_in_index, + v1s->adb_data_in_size); - s->adb_data_in_index++; + v1s->adb_data_in_index++; break; case 1: /* First ODD byte: vADBInt indicates SRQ */ - *data = s->adb_data_in[s->adb_data_in_index]; - pending = adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> 4)); + *data = v1s->adb_data_in[v1s->adb_data_in_index]; + pending = adb_bus->pending & ~(1 << (v1s->adb_autopoll_cmd >> 4)); if (pending) { ms->b &= ~VIA1B_vADBInt; } else { @@ -813,10 +807,10 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, s->adb_data_in_index, - s->adb_data_in_size); + adb_bus->status, v1s->adb_data_in_index, + v1s->adb_data_in_size); - s->adb_data_in_index++; + v1s->adb_data_in_index++; break; default: @@ -826,11 +820,11 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) * end of the poll reply, so provide these extra bytes below to * keep it happy */ - if (s->adb_data_in_index < s->adb_data_in_size) { + if (v1s->adb_data_in_index < v1s->adb_data_in_size) { /* Next data byte */ - *data = s->adb_data_in[s->adb_data_in_index]; + *data = v1s->adb_data_in[v1s->adb_data_in_index]; ms->b |= VIA1B_vADBInt; - } else if (s->adb_data_in_index == s->adb_data_in_size) { + } else if (v1s->adb_data_in_index == v1s->adb_data_in_size) { if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { /* Bus timeout (no more data) */ *data = 0xff; @@ -849,23 +843,22 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, s->adb_data_in_index, - s->adb_data_in_size); + adb_bus->status, v1s->adb_data_in_index, + v1s->adb_data_in_size); - if (s->adb_data_in_index <= s->adb_data_in_size) { - s->adb_data_in_index++; + if (v1s->adb_data_in_index <= v1s->adb_data_in_size) { + v1s->adb_data_in_index++; } break; } - qemu_irq_raise(s->adb_data_ready); + qemu_irq_raise(v1s->adb_data_ready); break; } } -static void via1_adb_update(MacVIAState *m) +static void via1_adb_update(MOS6522Q800VIA1State *v1s) { - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1); MOS6522State *s = MOS6522(v1s); int oldstate, state; @@ -875,10 +868,10 @@ static void via1_adb_update(MacVIAState *m) if (state != oldstate) { if (s->acr & VIA1ACR_vShiftOut) { /* output mode */ - adb_via_send(m, state, s->sr); + adb_via_send(v1s, state, s->sr); } else { /* input mode */ - adb_via_receive(m, state, &s->sr); + adb_via_receive(v1s, state, &s->sr); } } } @@ -896,7 +889,6 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); - MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1); MOS6522State *ms = MOS6522(v1s); addr = (addr >> 9) & 0xf; @@ -904,8 +896,8 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, switch (addr) { case VIA_REG_B: - via1_rtc_update(m); - via1_adb_update(m); + via1_rtc_update(v1s); + via1_adb_update(v1s); v1s->last_b = ms->b; break; @@ -951,216 +943,155 @@ static const MemoryRegionOps mos6522_q800_via2_ops = { }, }; -static void mac_via_reset(DeviceState *dev) +static void via1_postload_update_cb(void *opaque, bool running, RunState state) { - MacVIAState *m = MAC_VIA(dev); - ADBBusState *adb_bus = &m->adb_bus; + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); - adb_set_autopoll_enabled(adb_bus, true); + qemu_del_vm_change_state_handler(v1s->vmstate); + v1s->vmstate = NULL; + + pram_update(v1s); +} + +static int via1_post_load(void *opaque, int version_id) +{ + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); + + if (v1s->blk) { + v1s->vmstate = qemu_add_vm_change_state_handler( + via1_postload_update_cb, v1s); + } + + return 0; +} + +/* VIA 1 */ +static void mos6522_q800_via1_reset(DeviceState *dev) +{ + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(dev); + MOS6522State *ms = MOS6522(v1s); + MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); + ADBBusState *adb_bus = &v1s->adb_bus; + + mdc->parent_reset(dev); + + ms->timers[0].frequency = VIA_TIMER_FREQ; + ms->timers[1].frequency = VIA_TIMER_FREQ; + + ms->b = VIA1B_vADB_StateMask | VIA1B_vADBInt | VIA1B_vRTCEnb; - m->cmd = REG_EMPTY; - m->alt = REG_EMPTY; + /* ADB/RTC */ + adb_set_autopoll_enabled(adb_bus, true); + v1s->cmd = REG_EMPTY; + v1s->alt = REG_EMPTY; } -static void mac_via_realize(DeviceState *dev, Error **errp) +static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp) { - MacVIAState *m = MAC_VIA(dev); - MOS6522State *ms; - ADBBusState *adb_bus = &m->adb_bus; + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(dev); + ADBBusState *adb_bus = &v1s->adb_bus; struct tm tm; int ret; - /* Init VIAs 1 and 2 */ - object_initialize_child(OBJECT(dev), "via1", &m->mos6522_via1, - TYPE_MOS6522_Q800_VIA1); - - object_initialize_child(OBJECT(dev), "via2", &m->mos6522_via2, - TYPE_MOS6522_Q800_VIA2); - - /* Pass through mos6522 output IRQs */ - ms = MOS6522(&m->mos6522_via1); - object_property_add_alias(OBJECT(dev), "irq[0]", OBJECT(ms), - SYSBUS_DEVICE_GPIO_IRQ "[0]"); - ms = MOS6522(&m->mos6522_via2); - object_property_add_alias(OBJECT(dev), "irq[1]", OBJECT(ms), - SYSBUS_DEVICE_GPIO_IRQ "[0]"); - - sysbus_realize(SYS_BUS_DEVICE(&m->mos6522_via1), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&m->mos6522_via2), &error_abort); - - /* Pass through mos6522 input IRQs */ - qdev_pass_gpios(DEVICE(&m->mos6522_via1), dev, "via1-irq"); - qdev_pass_gpios(DEVICE(&m->mos6522_via2), dev, "via2-irq"); - - /* VIA 1 */ - m->mos6522_via1.one_second_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - via1_one_second, - &m->mos6522_via1); - via1_one_second_update(&m->mos6522_via1); - m->mos6522_via1.sixty_hz_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - via1_sixty_hz, - &m->mos6522_via1); - via1_sixty_hz_update(&m->mos6522_via1); + v1s->one_second_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, via1_one_second, + v1s); + via1_one_second_update(v1s); + v1s->sixty_hz_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via1_sixty_hz, + v1s); + via1_sixty_hz_update(v1s); qemu_get_timedate(&tm, 0); - m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; + v1s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; - adb_register_autopoll_callback(adb_bus, adb_via_poll, m); - m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq", - VIA1_IRQ_ADB_READY_BIT); + adb_register_autopoll_callback(adb_bus, adb_via_poll, v1s); + v1s->adb_data_ready = qdev_get_gpio_in(dev, VIA1_IRQ_ADB_READY_BIT); - if (m->blk) { - int64_t len = blk_getlength(m->blk); + if (v1s->blk) { + int64_t len = blk_getlength(v1s->blk); if (len < 0) { error_setg_errno(errp, -len, "could not get length of backing image"); return; } - ret = blk_set_perm(m->blk, + ret = blk_set_perm(v1s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, errp); if (ret < 0) { return; } - len = blk_pread(m->blk, 0, m->mos6522_via1.PRAM, - sizeof(m->mos6522_via1.PRAM)); - if (len != sizeof(m->mos6522_via1.PRAM)) { + len = blk_pread(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM)); + if (len != sizeof(v1s->PRAM)) { error_setg(errp, "can't read PRAM contents"); return; } } } -static void mac_via_init(Object *obj) +static void mos6522_q800_via1_init(Object *obj) { - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - MacVIAState *m = MAC_VIA(obj); - - /* MMIO */ - memory_region_init(&m->mmio, obj, "mac-via", 2 * VIA_SIZE); - sysbus_init_mmio(sbd, &m->mmio); + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(v1s); - memory_region_init_io(&m->via1mem, obj, &mos6522_q800_via1_ops, - &m->mos6522_via1, "via1", VIA_SIZE); - memory_region_add_subregion(&m->mmio, 0x0, &m->via1mem); - - memory_region_init_io(&m->via2mem, obj, &mos6522_q800_via2_ops, - &m->mos6522_via2, "via2", VIA_SIZE); - memory_region_add_subregion(&m->mmio, VIA_SIZE, &m->via2mem); + memory_region_init_io(&v1s->via_mem, obj, &mos6522_q800_via1_ops, v1s, + "via1", VIA_SIZE); + sysbus_init_mmio(sbd, &v1s->via_mem); /* ADB */ - qbus_create_inplace((BusState *)&m->adb_bus, sizeof(m->adb_bus), - TYPE_ADB_BUS, DEVICE(obj), "adb.0"); -} + qbus_create_inplace((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus), + TYPE_ADB_BUS, DEVICE(v1s), "adb.0"); -static void postload_update_cb(void *opaque, bool running, RunState state) -{ - MacVIAState *m = MAC_VIA(opaque); - - qemu_del_vm_change_state_handler(m->vmstate); - m->vmstate = NULL; - - pram_update(m); + qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB); } -static int mac_via_post_load(void *opaque, int version_id) -{ - MacVIAState *m = MAC_VIA(opaque); - - if (m->blk) { - m->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, - m); - } - - return 0; -} - -static const VMStateDescription vmstate_mac_via = { - .name = "mac-via", - .version_id = 2, - .minimum_version_id = 2, - .post_load = mac_via_post_load, +static const VMStateDescription vmstate_q800_via1 = { + .name = "q800-via1", + .version_id = 0, + .minimum_version_id = 0, + .post_load = via1_post_load, .fields = (VMStateField[]) { - /* VIAs */ - VMSTATE_STRUCT(mos6522_via1.parent_obj, MacVIAState, 0, vmstate_mos6522, - MOS6522State), - VMSTATE_UINT8(mos6522_via1.last_b, MacVIAState), - VMSTATE_BUFFER(mos6522_via1.PRAM, MacVIAState), - VMSTATE_TIMER_PTR(mos6522_via1.one_second_timer, MacVIAState), - VMSTATE_INT64(mos6522_via1.next_second, MacVIAState), - VMSTATE_TIMER_PTR(mos6522_via1.sixty_hz_timer, MacVIAState), - VMSTATE_INT64(mos6522_via1.next_sixty_hz, MacVIAState), - VMSTATE_STRUCT(mos6522_via2.parent_obj, MacVIAState, 0, vmstate_mos6522, + VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA1State, 0, vmstate_mos6522, MOS6522State), + VMSTATE_UINT8(last_b, MOS6522Q800VIA1State), /* RTC */ - VMSTATE_UINT32(tick_offset, MacVIAState), - VMSTATE_UINT8(data_out, MacVIAState), - VMSTATE_INT32(data_out_cnt, MacVIAState), - VMSTATE_UINT8(data_in, MacVIAState), - VMSTATE_UINT8(data_in_cnt, MacVIAState), - VMSTATE_UINT8(cmd, MacVIAState), - VMSTATE_INT32(wprotect, MacVIAState), - VMSTATE_INT32(alt, MacVIAState), + VMSTATE_BUFFER(PRAM, MOS6522Q800VIA1State), + VMSTATE_UINT32(tick_offset, MOS6522Q800VIA1State), + VMSTATE_UINT8(data_out, MOS6522Q800VIA1State), + VMSTATE_INT32(data_out_cnt, MOS6522Q800VIA1State), + VMSTATE_UINT8(data_in, MOS6522Q800VIA1State), + VMSTATE_UINT8(data_in_cnt, MOS6522Q800VIA1State), + VMSTATE_UINT8(cmd, MOS6522Q800VIA1State), + VMSTATE_INT32(wprotect, MOS6522Q800VIA1State), + VMSTATE_INT32(alt, MOS6522Q800VIA1State), /* ADB */ - VMSTATE_INT32(adb_data_in_size, MacVIAState), - VMSTATE_INT32(adb_data_in_index, MacVIAState), - VMSTATE_INT32(adb_data_out_index, MacVIAState), - VMSTATE_BUFFER(adb_data_in, MacVIAState), - VMSTATE_BUFFER(adb_data_out, MacVIAState), - VMSTATE_UINT8(adb_autopoll_cmd, MacVIAState), + VMSTATE_INT32(adb_data_in_size, MOS6522Q800VIA1State), + VMSTATE_INT32(adb_data_in_index, MOS6522Q800VIA1State), + VMSTATE_INT32(adb_data_out_index, MOS6522Q800VIA1State), + VMSTATE_BUFFER(adb_data_in, MOS6522Q800VIA1State), + VMSTATE_BUFFER(adb_data_out, MOS6522Q800VIA1State), + VMSTATE_UINT8(adb_autopoll_cmd, MOS6522Q800VIA1State), + /* Timers */ + VMSTATE_TIMER_PTR(one_second_timer, MOS6522Q800VIA1State), + VMSTATE_INT64(next_second, MOS6522Q800VIA1State), + VMSTATE_TIMER_PTR(sixty_hz_timer, MOS6522Q800VIA1State), + VMSTATE_INT64(next_sixty_hz, MOS6522Q800VIA1State), VMSTATE_END_OF_LIST() } }; -static Property mac_via_properties[] = { - DEFINE_PROP_DRIVE("drive", MacVIAState, blk), +static Property mos6522_q800_via1_properties[] = { + DEFINE_PROP_DRIVE("drive", MOS6522Q800VIA1State, blk), DEFINE_PROP_END_OF_LIST(), }; -static void mac_via_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = mac_via_realize; - dc->reset = mac_via_reset; - dc->vmsd = &vmstate_mac_via; - device_class_set_props(dc, mac_via_properties); -} - -static TypeInfo mac_via_info = { - .name = TYPE_MAC_VIA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MacVIAState), - .instance_init = mac_via_init, - .class_init = mac_via_class_init, -}; - -/* VIA 1 */ -static void mos6522_q800_via1_reset(DeviceState *dev) -{ - MOS6522State *ms = MOS6522(dev); - MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); - - mdc->parent_reset(dev); - - ms->timers[0].frequency = VIA_TIMER_FREQ; - ms->timers[1].frequency = VIA_TIMER_FREQ; - - ms->b = VIA1B_vADB_StateMask | VIA1B_vADBInt | VIA1B_vRTCEnb; -} - -static void mos6522_q800_via1_init(Object *obj) -{ - qdev_init_gpio_in_named(DEVICE(obj), via1_irq_request, "via1-irq", - VIA1_IRQ_NB); -} - static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + dc->realize = mos6522_q800_via1_realize; dc->reset = mos6522_q800_via1_reset; + dc->vmsd = &vmstate_q800_via1; + device_class_set_props(dc, mos6522_q800_via1_properties); } static const TypeInfo mos6522_q800_via1_type_info = { @@ -1192,20 +1123,61 @@ static void mos6522_q800_via2_reset(DeviceState *dev) ms->dirb = 0; ms->b = 0; + ms->dira = 0; + ms->a = 0x7f; +} + +static void via2_nubus_irq_request(void *opaque, int irq, int level) +{ + MOS6522Q800VIA2State *v2s = opaque; + MOS6522State *s = MOS6522(v2s); + MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + + if (level) { + /* Port A nubus IRQ inputs are active LOW */ + s->a &= ~(1 << irq); + s->ifr |= 1 << VIA2_IRQ_NUBUS_BIT; + } else { + s->a |= (1 << irq); + s->ifr &= ~(1 << VIA2_IRQ_NUBUS_BIT); + } + + mdc->update_irq(s); } static void mos6522_q800_via2_init(Object *obj) { - qdev_init_gpio_in_named(DEVICE(obj), via2_irq_request, "via2-irq", - VIA2_IRQ_NB); + MOS6522Q800VIA2State *v2s = MOS6522_Q800_VIA2(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(v2s); + + memory_region_init_io(&v2s->via_mem, obj, &mos6522_q800_via2_ops, v2s, + "via2", VIA_SIZE); + sysbus_init_mmio(sbd, &v2s->via_mem); + + qdev_init_gpio_in(DEVICE(obj), via2_irq_request, VIA2_IRQ_NB); + + qdev_init_gpio_in_named(DEVICE(obj), via2_nubus_irq_request, "nubus-irq", + VIA2_NUBUS_IRQ_NB); } +static const VMStateDescription vmstate_q800_via2 = { + .name = "q800-via2", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA2State, 0, vmstate_mos6522, + MOS6522State), + VMSTATE_END_OF_LIST() + } +}; + static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); dc->reset = mos6522_q800_via2_reset; + dc->vmsd = &vmstate_q800_via2; mdc->portB_write = mos6522_q800_via2_portB_write; } @@ -1221,7 +1193,6 @@ static void mac_via_register_types(void) { type_register_static(&mos6522_q800_via1_type_info); type_register_static(&mos6522_q800_via2_type_info); - type_register_static(&mac_via_info); } type_init(mac_via_register_types); diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 133ddea575..7d9523f811 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1206,23 +1206,53 @@ static void sch_handle_start_func_virtual(SubchDev *sch) } -static void sch_handle_halt_func_passthrough(SubchDev *sch) +static IOInstEnding sch_handle_halt_func_passthrough(SubchDev *sch) { int ret; ret = s390_ccw_halt(sch); if (ret == -ENOSYS) { sch_handle_halt_func(sch); + return IOINST_CC_EXPECTED; + } + /* + * Some conditions may have been detected prior to starting the halt + * function; map them to the correct cc. + * Note that we map both -ENODEV and -EACCES to cc 3 (there's not really + * anything else we can do.) + */ + switch (ret) { + case -EBUSY: + return IOINST_CC_BUSY; + case -ENODEV: + case -EACCES: + return IOINST_CC_NOT_OPERATIONAL; + default: + return IOINST_CC_EXPECTED; } } -static void sch_handle_clear_func_passthrough(SubchDev *sch) +static IOInstEnding sch_handle_clear_func_passthrough(SubchDev *sch) { int ret; ret = s390_ccw_clear(sch); if (ret == -ENOSYS) { sch_handle_clear_func(sch); + return IOINST_CC_EXPECTED; + } + /* + * Some conditions may have been detected prior to starting the clear + * function; map them to the correct cc. + * Note that we map both -ENODEV and -EACCES to cc 3 (there's not really + * anything else we can do.) + */ + switch (ret) { + case -ENODEV: + case -EACCES: + return IOINST_CC_NOT_OPERATIONAL; + default: + return IOINST_CC_EXPECTED; } } @@ -1265,9 +1295,9 @@ IOInstEnding do_subchannel_work_passthrough(SubchDev *sch) SCHIB *schib = &sch->curr_status; if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { - sch_handle_clear_func_passthrough(sch); + return sch_handle_clear_func_passthrough(sch); } else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) { - sch_handle_halt_func_passthrough(sch); + return sch_handle_halt_func_passthrough(sch); } else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) { return sch_handle_start_func_passthrough(sch); } diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 7db1c5943f..6c0225c3a0 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -330,7 +330,7 @@ static unsigned int calc_sx(dma_addr_t ptr) static unsigned int calc_px(dma_addr_t ptr) { - return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK; + return ((unsigned long) ptr >> TARGET_PAGE_BITS) & ZPCI_PT_MASK; } static uint64_t get_rt_sto(uint64_t entry) @@ -506,7 +506,7 @@ uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr, int8_t ett = 1; uint16_t error = 0; - entry->iova = addr & PAGE_MASK; + entry->iova = addr & TARGET_PAGE_MASK; entry->translated_addr = 0; entry->perm = IOMMU_RW; @@ -526,7 +526,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, { S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr); S390IOTLBEntry *entry; - uint64_t iova = addr & PAGE_MASK; + uint64_t iova = addr & TARGET_PAGE_MASK; uint16_t error = 0; IOMMUTLBEntry ret = { .target_as = &address_space_memory, @@ -562,7 +562,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, ret.perm = entry->perm; } else { ret.iova = iova; - ret.addr_mask = ~PAGE_MASK; + ret.addr_mask = ~TARGET_PAGE_MASK; ret.perm = IOMMU_NONE; } @@ -868,7 +868,7 @@ static int s390_pci_msix_init(S390PCIBusDevice *pbdev) name = g_strdup_printf("msix-s390-%04x", pbdev->uid); memory_region_init_io(&pbdev->msix_notify_mr, OBJECT(pbdev), - &s390_msi_ctrl_ops, pbdev, name, PAGE_SIZE); + &s390_msi_ctrl_ops, pbdev, name, TARGET_PAGE_SIZE); memory_region_add_subregion(&pbdev->iommu->mr, pbdev->pci_group->zpci_group.msia, &pbdev->msix_notify_mr); diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 9ec277d50e..1c8ad91175 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -613,7 +613,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, .iova = entry->iova, .translated_addr = entry->translated_addr, .perm = entry->perm, - .addr_mask = ~PAGE_MASK, + .addr_mask = ~TARGET_PAGE_MASK, }, }; @@ -640,7 +640,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, cache = g_new(S390IOTLBEntry, 1); cache->iova = entry->iova; cache->translated_addr = entry->translated_addr; - cache->len = PAGE_SIZE; + cache->len = TARGET_PAGE_SIZE; cache->perm = entry->perm; g_hash_table_replace(iommu->iotlb, &cache->iova, cache); dec_dma_avail(iommu); @@ -725,8 +725,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) while (entry.iova < start && entry.iova < end && (dma_avail > 0 || entry.perm == IOMMU_NONE)) { dma_avail = s390_pci_update_iotlb(iommu, &entry); - entry.iova += PAGE_SIZE; - entry.translated_addr += PAGE_SIZE; + entry.iova += TARGET_PAGE_SIZE; + entry.translated_addr += TARGET_PAGE_SIZE; } } err: diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c index 1c4d805ad8..3ff9d94b80 100644 --- a/hw/s390x/s390-skeys-kvm.c +++ b/hw/s390x/s390-skeys-kvm.c @@ -15,7 +15,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" -static int kvm_s390_skeys_enabled(S390SKeysState *ss) +static bool kvm_s390_skeys_are_enabled(S390SKeysState *ss) { S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); uint8_t single_key; @@ -57,7 +57,7 @@ static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data) S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - skeyclass->skeys_enabled = kvm_s390_skeys_enabled; + skeyclass->skeys_are_enabled = kvm_s390_skeys_are_enabled; skeyclass->get_skeys = kvm_s390_skeys_get; skeyclass->set_skeys = kvm_s390_skeys_set; diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 9a8d60d1d9..5024faf411 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -17,6 +17,8 @@ #include "qapi/qapi-commands-misc-target.h" #include "qapi/qmp/qdict.h" #include "qemu/error-report.h" +#include "sysemu/memory_mapping.h" +#include "exec/address-spaces.h" #include "sysemu/kvm.h" #include "migration/qemu-file-types.h" #include "migration/register.h" @@ -80,11 +82,18 @@ void hmp_info_skeys(Monitor *mon, const QDict *qdict) int r; /* Quick check to see if guest is using storage keys*/ - if (!skeyclass->skeys_enabled(ss)) { + if (!skeyclass->skeys_are_enabled(ss)) { monitor_printf(mon, "Error: This guest is not using storage keys\n"); return; } + if (!address_space_access_valid(&address_space_memory, + addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE, + false, MEMTXATTRS_UNSPECIFIED)) { + monitor_printf(mon, "Error: The given address is not valid\n"); + return; + } + r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (r < 0) { monitor_printf(mon, "Error: %s\n", strerror(-r)); @@ -109,18 +118,17 @@ void qmp_dump_skeys(const char *filename, Error **errp) { S390SKeysState *ss = s390_get_skeys_device(); S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); - MachineState *ms = MACHINE(qdev_get_machine()); - const uint64_t total_count = ms->ram_size / TARGET_PAGE_SIZE; - uint64_t handled_count = 0, cur_count; + GuestPhysBlockList guest_phys_blocks; + GuestPhysBlock *block; + uint64_t pages, gfn; Error *lerr = NULL; - vaddr cur_gfn = 0; uint8_t *buf; int ret; int fd; FILE *f; /* Quick check to see if guest is using storage keys*/ - if (!skeyclass->skeys_enabled(ss)) { + if (!skeyclass->skeys_are_enabled(ss)) { error_setg(errp, "This guest is not using storage keys - " "nothing to dump"); return; @@ -144,53 +152,86 @@ void qmp_dump_skeys(const char *filename, Error **errp) goto out; } - /* we'll only dump initial memory for now */ - while (handled_count < total_count) { - /* Calculate how many keys to ask for & handle overflow case */ - cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE); + assert(qemu_mutex_iothread_locked()); + guest_phys_blocks_init(&guest_phys_blocks); + guest_phys_blocks_append(&guest_phys_blocks); - ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf); - if (ret < 0) { - error_setg(errp, "get_keys error %d", ret); - goto out_free; - } + QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE)); + assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE)); - /* write keys to stream */ - write_keys(f, buf, cur_gfn, cur_count, &lerr); - if (lerr) { - goto out_free; - } + gfn = block->target_start / TARGET_PAGE_SIZE; + pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE; + + while (pages) { + const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE); - cur_gfn += cur_count; - handled_count += cur_count; + ret = skeyclass->get_skeys(ss, gfn, cur_pages, buf); + if (ret < 0) { + error_setg_errno(errp, -ret, "get_keys error"); + goto out_free; + } + + /* write keys to stream */ + write_keys(f, buf, gfn, cur_pages, &lerr); + if (lerr) { + goto out_free; + } + + gfn += cur_pages; + pages -= cur_pages; + } } out_free: + guest_phys_blocks_free(&guest_phys_blocks); error_propagate(errp, lerr); g_free(buf); out: fclose(f); } -static void qemu_s390_skeys_init(Object *obj) +static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss) { - QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj); - MachineState *machine = MACHINE(qdev_get_machine()); + QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss); - skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE; - skeys->keydata = g_malloc0(skeys->key_count); + /* Lockless check is sufficient. */ + return !!skeys->keydata; } -static int qemu_s390_skeys_enabled(S390SKeysState *ss) +static bool qemu_s390_enable_skeys(S390SKeysState *ss) { - return 1; + QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss); + static gsize initialized; + + if (likely(skeys->keydata)) { + return true; + } + + /* + * TODO: Modern Linux doesn't use storage keys unless running KVM guests + * that use storage keys. Therefore, we keep it simple for now. + * + * 1) We should initialize to "referenced+changed" for an initial + * over-indication. Let's avoid touching megabytes of data for now and + * assume that any sane user will issue a storage key instruction before + * actually relying on this data. + * 2) Relying on ram_size and allocating a big array is ugly. We should + * allocate and manage storage key data per RAMBlock or optimally using + * some sparse data structure. + * 3) We only ever have a single S390SKeysState, so relying on + * g_once_init_enter() is good enough. + */ + if (g_once_init_enter(&initialized)) { + MachineState *machine = MACHINE(qdev_get_machine()); + + skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE; + skeys->keydata = g_malloc0(skeys->key_count); + g_once_init_leave(&initialized, 1); + } + return false; } -/* - * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get - * will have to make sure that the given gfn belongs to a memory region and not - * a memory hole. - */ static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, uint64_t count, uint8_t *keys) { @@ -198,9 +239,10 @@ static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, int i; /* Check for uint64 overflow and access beyond end of key data */ - if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { - error_report("Error: Setting storage keys for page beyond the end " - "of memory: gfn=%" PRIx64 " count=%" PRId64, + if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count || + start_gfn + count < count)) { + error_report("Error: Setting storage keys for pages with unallocated " + "storage key memory: gfn=%" PRIx64 " count=%" PRId64, start_gfn, count); return -EINVAL; } @@ -218,9 +260,10 @@ static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, int i; /* Check for uint64 overflow and access beyond end of key data */ - if (start_gfn + count > skeydev->key_count || start_gfn + count < count) { - error_report("Error: Getting storage keys for page beyond the end " - "of memory: gfn=%" PRIx64 " count=%" PRId64, + if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count || + start_gfn + count < count)) { + error_report("Error: Getting storage keys for pages with unallocated " + "storage key memory: gfn=%" PRIx64 " count=%" PRId64, start_gfn, count); return -EINVAL; } @@ -236,7 +279,8 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - skeyclass->skeys_enabled = qemu_s390_skeys_enabled; + skeyclass->skeys_are_enabled = qemu_s390_skeys_are_enabled; + skeyclass->enable_skeys = qemu_s390_enable_skeys; skeyclass->get_skeys = qemu_s390_skeys_get; skeyclass->set_skeys = qemu_s390_skeys_set; @@ -247,7 +291,6 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) static const TypeInfo qemu_s390_skeys_info = { .name = TYPE_QEMU_S390_SKEYS, .parent = TYPE_S390_SKEYS, - .instance_init = qemu_s390_skeys_init, .instance_size = sizeof(QEMUS390SKeysState), .class_init = qemu_s390_skeys_class_init, .class_size = sizeof(S390SKeysClass), @@ -257,14 +300,13 @@ static void s390_storage_keys_save(QEMUFile *f, void *opaque) { S390SKeysState *ss = S390_SKEYS(opaque); S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); - MachineState *ms = MACHINE(qdev_get_machine()); - uint64_t pages_left = ms->ram_size / TARGET_PAGE_SIZE; - uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS; - vaddr cur_gfn = 0; + GuestPhysBlockList guest_phys_blocks; + GuestPhysBlock *block; + uint64_t pages, gfn; int error = 0; uint8_t *buf; - if (!skeyclass->skeys_enabled(ss)) { + if (!skeyclass->skeys_are_enabled(ss)) { goto end_stream; } @@ -274,36 +316,52 @@ static void s390_storage_keys_save(QEMUFile *f, void *opaque) goto end_stream; } - /* We only support initial memory. Standby memory is not handled yet. */ - qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS); - qemu_put_be64(f, pages_left); - - while (pages_left) { - read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE); - - if (!error) { - error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf); - if (error) { - /* - * If error: we want to fill the stream with valid data instead - * of stopping early so we pad the stream with 0x00 values and - * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the - * reading side. - */ - error_report("S390_GET_KEYS error %d", error); - memset(buf, 0, S390_SKEYS_BUFFER_SIZE); - eos = S390_SKEYS_SAVE_FLAG_ERROR; + guest_phys_blocks_init(&guest_phys_blocks); + guest_phys_blocks_append(&guest_phys_blocks); + + /* Send each contiguous physical memory range separately. */ + QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE)); + assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE)); + + gfn = block->target_start / TARGET_PAGE_SIZE; + pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE; + qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS); + qemu_put_be64(f, pages); + + while (pages) { + const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE); + + if (!error) { + error = skeyclass->get_skeys(ss, gfn, cur_pages, buf); + if (error) { + /* + * Create a valid stream with all 0x00 and indicate + * S390_SKEYS_SAVE_FLAG_ERROR to the destination. + */ + error_report("S390_GET_KEYS error %d", error); + memset(buf, 0, S390_SKEYS_BUFFER_SIZE); + } } + + qemu_put_buffer(f, buf, cur_pages); + gfn += cur_pages; + pages -= cur_pages; } - qemu_put_buffer(f, buf, read_count); - cur_gfn += read_count; - pages_left -= read_count; + if (error) { + break; + } } + guest_phys_blocks_free(&guest_phys_blocks); g_free(buf); end_stream: - qemu_put_be64(f, eos); + if (error) { + qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR); + } else { + qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS); + } } static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) @@ -312,6 +370,14 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); int ret = 0; + /* + * Make sure to lazy-enable if required to be done explicitly. No need to + * flush any TLB as the VM is not running yet. + */ + if (skeyclass->enable_skeys) { + skeyclass->enable_skeys(ss); + } + while (!ret) { ram_addr_t addr; int flags; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4d25278cf2..61aeccb163 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -803,6 +803,11 @@ DEFINE_CCW_MACHINE(6_2, "6.2", true); static void ccw_machine_6_1_instance_options(MachineState *machine) { ccw_machine_6_2_instance_options(machine); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_NNPA); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_BEAR_ENH); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_RDP); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_PAI); } static void ccw_machine_6_1_class_options(MachineClass *mc) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index edb6e3ea01..89c30a8a91 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -51,7 +51,7 @@ static bool sccb_verify_boundary(uint64_t sccb_addr, uint16_t sccb_len, uint32_t code) { uint64_t sccb_max_addr = sccb_addr + sccb_len - 1; - uint64_t sccb_boundary = (sccb_addr & PAGE_MASK) + PAGE_SIZE; + uint64_t sccb_boundary = (sccb_addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; switch (code & SCLP_CMD_CODE_MASK) { case SCLP_CMDW_READ_SCP_INFO: diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 42e139849e..7f3a7c0027 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -803,11 +803,11 @@ static void cpu_devinit(const char *cpu_type, unsigned int id, cpu = SPARC_CPU(object_new(cpu_type)); env = &cpu->env; - cpu_sparc_set_id(env, id); qemu_register_reset(sun4m_cpu_reset, cpu); object_property_set_bool(OBJECT(cpu), "start-powered-off", id != 0, &error_fatal); qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + cpu_sparc_set_id(env, id); *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, cpu, MAX_PILS); env->prom_addr = prom_addr; } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 000992fb9f..0354737666 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -199,7 +199,7 @@ again: case 0: case -ENODEV: case -EACCES: - return 0; + return ret; case -EFAULT: default: sch_gen_unit_exception(sch); @@ -240,7 +240,7 @@ again: case -EBUSY: case -ENODEV: case -EACCES: - return 0; + return ret; case -EFAULT: default: sch_gen_unit_exception(sch); diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 467529d84c..610cba58fe 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -17,22 +17,6 @@ static inline void gen_io_start(void) tcg_temp_free_i32(tmp); } -/* - * cpu->can_do_io is cleared automatically at the beginning of - * each translation block. The cost is minimal and only paid - * for -icount, plus it would be very easy to forget doing it - * in the translator. Therefore, backends only need to call - * gen_io_start. - */ -static inline void gen_io_end(void) -{ - TCGv_i32 tmp = tcg_const_i32(0); - tcg_gen_st_i32(tmp, cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); - tcg_temp_free_i32(tmp); -} - static inline void gen_tb_start(const TranslationBlock *tb) { TCGv_i32 count; @@ -64,7 +48,16 @@ static inline void gen_tb_start(const TranslationBlock *tb) tcg_gen_st16_i32(count, cpu_env, offsetof(ArchCPU, neg.icount_decr.u16.low) - offsetof(ArchCPU, env)); - gen_io_end(); + /* + * cpu->can_do_io is cleared automatically here at the beginning of + * each translation block. The cost is minimal and only paid for + * -icount, plus it would be very easy to forget doing it in the + * translator. Doing it here means we don't need a gen_io_end() to + * go with gen_io_start(). + */ + tcg_gen_st_i32(tcg_constant_i32(0), cpu_env, + offsetof(ArchCPU, parent_obj.can_do_io) - + offsetof(ArchCPU, env)); } tcg_temp_free_i32(count); diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 3058b30685..4506abe5d0 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -15,6 +15,8 @@ #include "qom/object.h" +#define VIA_SIZE 0x2000 + /* VIA 1 */ #define VIA1_IRQ_ONE_SECOND_BIT 0 #define VIA1_IRQ_60HZ_BIT 1 @@ -24,11 +26,11 @@ #define VIA1_IRQ_NB 8 -#define VIA1_IRQ_ONE_SECOND (1 << VIA1_IRQ_ONE_SECOND_BIT) -#define VIA1_IRQ_60HZ (1 << VIA1_IRQ_60HZ_BIT) -#define VIA1_IRQ_ADB_READY (1 << VIA1_IRQ_ADB_READY_BIT) -#define VIA1_IRQ_ADB_DATA (1 << VIA1_IRQ_ADB_DATA_BIT) -#define VIA1_IRQ_ADB_CLOCK (1 << VIA1_IRQ_ADB_CLOCK_BIT) +#define VIA1_IRQ_ONE_SECOND (1 << VIA1_IRQ_ONE_SECOND_BIT) +#define VIA1_IRQ_60HZ (1 << VIA1_IRQ_60HZ_BIT) +#define VIA1_IRQ_ADB_READY (1 << VIA1_IRQ_ADB_READY_BIT) +#define VIA1_IRQ_ADB_DATA (1 << VIA1_IRQ_ADB_DATA_BIT) +#define VIA1_IRQ_ADB_CLOCK (1 << VIA1_IRQ_ADB_CLOCK_BIT) #define TYPE_MOS6522_Q800_VIA1 "mos6522-q800-via1" @@ -38,9 +40,35 @@ struct MOS6522Q800VIA1State { /*< private >*/ MOS6522State parent_obj; + MemoryRegion via_mem; + qemu_irq irqs[VIA1_IRQ_NB]; uint8_t last_b; + + /* RTC */ uint8_t PRAM[256]; + BlockBackend *blk; + VMChangeStateEntry *vmstate; + + uint32_t tick_offset; + + uint8_t data_out; + int data_out_cnt; + uint8_t data_in; + uint8_t data_in_cnt; + uint8_t cmd; + int wprotect; + int alt; + + /* ADB */ + ADBBusState adb_bus; + qemu_irq adb_data_ready; + int adb_data_in_size; + int adb_data_in_index; + int adb_data_out_index; + uint8_t adb_data_in[128]; + uint8_t adb_data_out[16]; + uint8_t adb_autopoll_cmd; /* external timers */ QEMUTimer *one_second_timer; @@ -52,18 +80,28 @@ struct MOS6522Q800VIA1State { /* VIA 2 */ #define VIA2_IRQ_SCSI_DATA_BIT 0 -#define VIA2_IRQ_SLOT_BIT 1 +#define VIA2_IRQ_NUBUS_BIT 1 #define VIA2_IRQ_UNUSED_BIT 2 #define VIA2_IRQ_SCSI_BIT 3 #define VIA2_IRQ_ASC_BIT 4 #define VIA2_IRQ_NB 8 -#define VIA2_IRQ_SCSI_DATA (1 << VIA2_IRQ_SCSI_DATA_BIT) -#define VIA2_IRQ_SLOT (1 << VIA2_IRQ_SLOT_BIT) -#define VIA2_IRQ_UNUSED (1 << VIA2_IRQ_SCSI_BIT) -#define VIA2_IRQ_SCSI (1 << VIA2_IRQ_UNUSED_BIT) -#define VIA2_IRQ_ASC (1 << VIA2_IRQ_ASC_BIT) +#define VIA2_IRQ_SCSI_DATA (1 << VIA2_IRQ_SCSI_DATA_BIT) +#define VIA2_IRQ_NUBUS (1 << VIA2_IRQ_NUBUS_BIT) +#define VIA2_IRQ_UNUSED (1 << VIA2_IRQ_SCSI_BIT) +#define VIA2_IRQ_SCSI (1 << VIA2_IRQ_UNUSED_BIT) +#define VIA2_IRQ_ASC (1 << VIA2_IRQ_ASC_BIT) + +#define VIA2_NUBUS_IRQ_NB 7 + +#define VIA2_NUBUS_IRQ_9 0 +#define VIA2_NUBUS_IRQ_A 1 +#define VIA2_NUBUS_IRQ_B 2 +#define VIA2_NUBUS_IRQ_C 3 +#define VIA2_NUBUS_IRQ_D 4 +#define VIA2_NUBUS_IRQ_E 5 +#define VIA2_NUBUS_IRQ_INTVIDEO 6 #define TYPE_MOS6522_Q800_VIA2 "mos6522-q800-via2" OBJECT_DECLARE_SIMPLE_TYPE(MOS6522Q800VIA2State, MOS6522_Q800_VIA2) @@ -71,47 +109,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(MOS6522Q800VIA2State, MOS6522_Q800_VIA2) struct MOS6522Q800VIA2State { /*< private >*/ MOS6522State parent_obj; -}; - -#define TYPE_MAC_VIA "mac_via" -OBJECT_DECLARE_SIMPLE_TYPE(MacVIAState, MAC_VIA) - -struct MacVIAState { - SysBusDevice busdev; - - VMChangeStateEntry *vmstate; - - /* MMIO */ - MemoryRegion mmio; - MemoryRegion via1mem; - MemoryRegion via2mem; - - /* VIAs */ - MOS6522Q800VIA1State mos6522_via1; - MOS6522Q800VIA2State mos6522_via2; - - /* RTC */ - uint32_t tick_offset; - - uint8_t data_out; - int data_out_cnt; - uint8_t data_in; - uint8_t data_in_cnt; - uint8_t cmd; - int wprotect; - int alt; - BlockBackend *blk; - - /* ADB */ - ADBBusState adb_bus; - qemu_irq adb_data_ready; - int adb_data_in_size; - int adb_data_in_index; - int adb_data_out_index; - uint8_t adb_data_in[128]; - uint8_t adb_data_out[16]; - uint8_t adb_autopoll_cmd; + MemoryRegion via_mem; }; #endif diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 10ed1df1bb..75e5381613 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -146,7 +146,8 @@ struct SubchDev { static inline void sch_gen_unit_exception(SubchDev *sch) { - sch->curr_status.scsw.ctrl &= ~SCSW_ACTL_START_PEND; + sch->curr_status.scsw.ctrl &= ~(SCSW_ACTL_DEVICE_ACTIVE | + SCSW_ACTL_SUBCH_ACTIVE); sch->curr_status.scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h index 49ae9f03d3..aa891c178d 100644 --- a/include/hw/s390x/s390-pci-bus.h +++ b/include/hw/s390x/s390-pci-bus.h @@ -81,9 +81,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(S390PCIIOMMU, S390_PCI_IOMMU) #define ZPCI_SDMA_ADDR 0x100000000ULL #define ZPCI_EDMA_ADDR 0x1ffffffffffffffULL -#define PAGE_SHIFT 12 -#define PAGE_SIZE (1 << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) #define PAGE_DEFAULT_ACC 0 #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) @@ -137,7 +134,7 @@ enum ZpciIoatDtype { #define ZPCI_TABLE_BITS 11 #define ZPCI_PT_BITS 8 -#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT) +#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + TARGET_PAGE_BITS) #define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS) #define ZPCI_RTE_FLAG_MASK 0x3fffULL diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index 2888d42d0b..aa2ec2aae5 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -28,9 +28,72 @@ struct S390SKeysState { struct S390SKeysClass { DeviceClass parent_class; - int (*skeys_enabled)(S390SKeysState *ks); + + /** + * @skeys_are_enabled: + * + * Check whether storage keys are enabled. If not enabled, they were not + * enabled lazily either by the guest via a storage key instruction or + * by the host during migration. + * + * If disabled, everything not explicitly triggered by the guest, + * such as outgoing migration or dirty/change tracking, should not touch + * storage keys and should not lazily enable it. + * + * @ks: the #S390SKeysState + * + * Returns false if not enabled and true if enabled. + */ + bool (*skeys_are_enabled)(S390SKeysState *ks); + + /** + * @enable_skeys: + * + * Lazily enable storage keys. If this function is not implemented, + * setting a storage key will lazily enable storage keys implicitly + * instead. TCG guests have to make sure to flush the TLB of all CPUs + * if storage keys were not enabled before this call. + * + * @ks: the #S390SKeysState + * + * Returns false if not enabled before this call, and true if already + * enabled. + */ + bool (*enable_skeys)(S390SKeysState *ks); + + /** + * @get_skeys: + * + * Get storage keys for the given PFN range. This call will fail if + * storage keys have not been lazily enabled yet. + * + * Callers have to validate that a GFN is valid before this call. + * + * @ks: the #S390SKeysState + * @start_gfn: the start GFN to get storage keys for + * @count: the number of storage keys to get + * @keys: the byte array where storage keys will be stored to + * + * Returns 0 on success, returns an error if getting a storage key failed. + */ int (*get_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count, uint8_t *keys); + /** + * @set_skeys: + * + * Set storage keys for the given PFN range. This call will fail if + * storage keys have not been lazily enabled yet and implicit + * enablement is not supported. + * + * Callers have to validate that a GFN is valid before this call. + * + * @ks: the #S390SKeysState + * @start_gfn: the start GFN to set storage keys for + * @count: the number of storage keys to set + * @keys: the byte array where storage keys will be read from + * + * Returns 0 on success, returns an error if setting a storage key failed. + */ int (*set_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count, uint8_t *keys); }; diff --git a/include/qemu/vfio-helpers.h b/include/qemu/vfio-helpers.h index 4491c8e1a6..bde9495b25 100644 --- a/include/qemu/vfio-helpers.h +++ b/include/qemu/vfio-helpers.h @@ -18,7 +18,7 @@ typedef struct QEMUVFIOState QEMUVFIOState; QEMUVFIOState *qemu_vfio_open_pci(const char *device, Error **errp); void qemu_vfio_close(QEMUVFIOState *s); int qemu_vfio_dma_map(QEMUVFIOState *s, void *host, size_t size, - bool temporary, uint64_t *iova_list); + bool temporary, uint64_t *iova_list, Error **errp); int qemu_vfio_dma_reset_temporary(QEMUVFIOState *s); void qemu_vfio_dma_unmap(QEMUVFIOState *s, void *host); void *qemu_vfio_pci_map_bar(QEMUVFIOState *s, int index, diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index e40e1d7025..91a73db9a3 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc Binary files differdiff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 0c0aa094ec..a5b7389191 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 Binary files differdiff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index a3e458517a..f7a501efc6 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 Binary files differdiff --git a/roms/openbios b/roms/openbios -Subproject 4a0041107b8ef77e0e8337bfcb5f8078887261a +Subproject d657b653186c0fd6e062cab133497415c2a5a5b diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 7d85322d68..7cb1a6ec10 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -663,7 +663,13 @@ static uint16_t default_GEN15_GA1[] = { S390_FEAT_ETOKEN, }; -#define default_GEN16_GA1 EmptyFeat +static uint16_t default_GEN16_GA1[] = { + S390_FEAT_NNPA, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, + S390_FEAT_BEAR_ENH, + S390_FEAT_RDP, + S390_FEAT_PAI, +}; /* QEMU (CPU model) features */ diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 6215ca00bc..271b081e8c 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -336,9 +336,9 @@ DEF_HELPER_FLAGS_4(stctl, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_FLAGS_4(stctg, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_FLAGS_2(testblock, TCG_CALL_NO_WG, i32, env, i64) DEF_HELPER_FLAGS_3(tprot, TCG_CALL_NO_WG, i32, env, i64, i64) -DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64) -DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i64, i64) -DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_NO_RWG, i32, env, i64) +DEF_HELPER_2(iske, i64, env, i64) +DEF_HELPER_3(sske, void, env, i64, i64) +DEF_HELPER_2(rrbe, i32, env, i64) DEF_HELPER_4(mvcs, i32, env, i64, i64, i64) DEF_HELPER_4(mvcp, i32, env, i64, i64, i64) DEF_HELPER_4(sigp, i32, env, i64, i32, i32) diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 4eb0a7a9f8..bdae5090bc 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -123,7 +123,7 @@ static int ioinst_schib_valid(SCHIB *schib) } /* for MB format 1 bits 26-31 of word 11 must be 0 */ /* MBA uses words 10 and 11, it means align on 2**6 */ - if ((be16_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_MBFC) && + if ((be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_MBFC) && (be64_to_cpu(schib->mba) & 0x03fUL)) { return 0; } diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index d779a9fc51..b04b57c235 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -94,6 +94,14 @@ target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr) return raddr; } +bool mmu_absolute_addr_valid(target_ulong addr, bool is_write) +{ + return address_space_access_valid(&address_space_memory, + addr & TARGET_PAGE_MASK, + TARGET_PAGE_SIZE, is_write, + MEMTXATTRS_UNSPECIFIED); +} + static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr, uint64_t *entry) { @@ -117,7 +125,7 @@ static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr, static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t asce, target_ulong *raddr, - int *flags, int rw) + int *flags) { const bool edat1 = (env->cregs[0] & CR0_EDAT) && s390_has_feat(S390_FEAT_EDAT); @@ -293,20 +301,27 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) { static S390SKeysClass *skeyclass; static S390SKeysState *ss; - MachineState *ms = MACHINE(qdev_get_machine()); - uint8_t key; + uint8_t key, old_key; int rc; - if (unlikely(addr >= ms->ram_size)) { - return; - } - + /* + * We expect to be called with an absolute address that has already been + * validated, such that we can reliably use it to lookup the storage key. + */ if (unlikely(!ss)) { ss = s390_get_skeys_device(); skeyclass = S390_SKEYS_GET_CLASS(ss); } /* + * Don't enable storage keys if they are still disabled, i.e., no actual + * storage key instruction was issued yet. + */ + if (!skeyclass->skeys_are_enabled(ss)) { + return; + } + + /* * Whenever we create a new TLB entry, we set the storage key reference * bit. In case we allow write accesses, we set the storage key change * bit. Whenever the guest changes the storage key, we have to flush the @@ -330,6 +345,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) trace_get_skeys_nonzero(rc); return; } + old_key = key; switch (rw) { case MMU_DATA_LOAD: @@ -353,20 +369,23 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) /* Any store/fetch sets the reference bit */ key |= SK_R; - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_set_skeys_nonzero(rc); + if (key != old_key) { + rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (rc) { + trace_set_skeys_nonzero(rc); + } } } /** * Translate a virtual (logical) address into a physical (absolute) address. * @param vaddr the virtual address - * @param rw 0 = read, 1 = write, 2 = code fetch + * @param rw 0 = read, 1 = write, 2 = code fetch, < 0 = load real address * @param asc address space control (one of the PSW_ASC_* modes) * @param raddr the translated address is stored to this pointer * @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer - * @param exc true = inject a program check if a fault occurred + * @param tec the translation exception code if stored to this pointer if + * there is an exception to raise * @return 0 = success, != 0, the exception to raise */ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, @@ -420,7 +439,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, } /* perform the DAT translation */ - r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw); + r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags); if (unlikely(r)) { return r; } @@ -440,10 +459,17 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, } nodat: - /* Convert real address -> absolute address */ - *raddr = mmu_real2abs(env, *raddr); + if (rw >= 0) { + /* Convert real address -> absolute address */ + *raddr = mmu_real2abs(env, *raddr); - mmu_handle_skey(*raddr, rw, flags); + if (!mmu_absolute_addr_valid(*raddr, rw == MMU_DATA_STORE)) { + *tec = 0; /* unused */ + return PGM_ADDRESSING; + } + + mmu_handle_skey(*raddr, rw, flags); + } return 0; } @@ -464,12 +490,6 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, if (ret) { return ret; } - if (!address_space_access_valid(&address_space_memory, pages[i], - TARGET_PAGE_SIZE, is_write, - MEMTXATTRS_UNSPECIFIED)) { - *tec = 0; /* unused */ - return PGM_ADDRESSING; - } addr += TARGET_PAGE_SIZE; } @@ -579,6 +599,12 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, *addr = mmu_real2abs(env, raddr & TARGET_PAGE_MASK); + if (!mmu_absolute_addr_valid(*addr, rw == MMU_DATA_STORE)) { + /* unused */ + *tec = 0; + return PGM_ADDRESSING; + } + mmu_handle_skey(*addr, rw, flags); return 0; } diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 5506f185e8..7a6aa4dacc 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -373,6 +373,9 @@ void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, /* mmu_helper.c */ +bool mmu_absolute_addr_valid(target_ulong addr, bool is_write); +/* Special access mode only valid for mmu_translate() */ +#define MMU_S390_LRA -1 int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, target_ulong *raddr, int *flags, uint64_t *tec); int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index a61917d04f..3d6662a53c 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -150,19 +150,6 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, g_assert_not_reached(); } - /* check out of RAM access */ - if (!excp && - !address_space_access_valid(&address_space_memory, raddr, - TARGET_PAGE_SIZE, access_type, - MEMTXATTRS_UNSPECIFIED)) { - MachineState *ms = MACHINE(qdev_get_machine()); - qemu_log_mask(CPU_LOG_MMU, - "%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", - __func__, (uint64_t)raddr, (uint64_t)ms->ram_size); - excp = PGM_ADDRESSING; - tec = 0; /* unused */ - } - env->tlb_fill_exc = excp; env->tlb_fill_tec = tec; diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 21a4de4067..0bf775a37d 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -28,6 +28,7 @@ #include "qemu/int128.h" #include "qemu/atomic128.h" #include "tcg/tcg.h" +#include "trace.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/storage-keys.h" @@ -2171,22 +2172,28 @@ uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2) /* insert storage key extended */ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) { - MachineState *ms = MACHINE(qdev_get_machine()); static S390SKeysState *ss; static S390SKeysClass *skeyclass; uint64_t addr = wrap_address(env, r2); uint8_t key; + int rc; - if (addr > ms->ram_size) { - return 0; + addr = mmu_real2abs(env, addr); + if (!mmu_absolute_addr_valid(addr, false)) { + tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC()); } if (unlikely(!ss)) { ss = s390_get_skeys_device(); skeyclass = S390_SKEYS_GET_CLASS(ss); + if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) { + tlb_flush_all_cpus_synced(env_cpu(env)); + } } - if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { + rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (rc) { + trace_get_skeys_nonzero(rc); return 0; } return key; @@ -2195,23 +2202,30 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) /* set storage key extended */ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) { - MachineState *ms = MACHINE(qdev_get_machine()); static S390SKeysState *ss; static S390SKeysClass *skeyclass; uint64_t addr = wrap_address(env, r2); uint8_t key; + int rc; - if (addr > ms->ram_size) { - return; + addr = mmu_real2abs(env, addr); + if (!mmu_absolute_addr_valid(addr, false)) { + tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC()); } if (unlikely(!ss)) { ss = s390_get_skeys_device(); skeyclass = S390_SKEYS_GET_CLASS(ss); + if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) { + tlb_flush_all_cpus_synced(env_cpu(env)); + } } - key = (uint8_t) r1; - skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + key = r1 & 0xfe; + rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (rc) { + trace_set_skeys_nonzero(rc); + } /* * As we can only flush by virtual address and not all the entries * that point to a physical address we have to flush the whole TLB. @@ -2222,28 +2236,37 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) /* reset reference bit extended */ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) { - MachineState *ms = MACHINE(qdev_get_machine()); + uint64_t addr = wrap_address(env, r2); static S390SKeysState *ss; static S390SKeysClass *skeyclass; uint8_t re, key; + int rc; - if (r2 > ms->ram_size) { - return 0; + addr = mmu_real2abs(env, addr); + if (!mmu_absolute_addr_valid(addr, false)) { + tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC()); } if (unlikely(!ss)) { ss = s390_get_skeys_device(); skeyclass = S390_SKEYS_GET_CLASS(ss); + if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) { + tlb_flush_all_cpus_synced(env_cpu(env)); + } } - if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { + rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (rc) { + trace_get_skeys_nonzero(rc); return 0; } re = key & (SK_R | SK_C); key &= ~SK_R; - if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { + rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + if (rc) { + trace_set_skeys_nonzero(rc); return 0; } /* @@ -2441,7 +2464,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC()); } - exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec); + exc = mmu_translate(env, addr, MMU_S390_LRA, asc, &ret, &flags, &tec); if (exc) { cc = 3; ret = exc | 0x80000000; diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 33e6999e15..aab9c47747 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -151,13 +151,26 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) /* Set Prefix */ void HELPER(spx)(CPUS390XState *env, uint64_t a1) { + const uint32_t prefix = a1 & 0x7fffe000; + const uint32_t old_prefix = env->psa; CPUState *cs = env_cpu(env); - uint32_t prefix = a1 & 0x7fffe000; + + if (prefix == old_prefix) { + return; + } env->psa = prefix; HELPER_LOG("prefix: %#x\n", prefix); tlb_flush_page(cs, 0); tlb_flush_page(cs, TARGET_PAGE_SIZE); + if (prefix != 0) { + tlb_flush_page(cs, prefix); + tlb_flush_page(cs, prefix + TARGET_PAGE_SIZE); + } + if (old_prefix != 0) { + tlb_flush_page(cs, old_prefix); + tlb_flush_page(cs, old_prefix + TARGET_PAGE_SIZE); + } } static void update_ckc_timer(CPUS390XState *env) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 11de5a4963..bb70ba17de 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -3401,7 +3401,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); + /* I/O operations in icount mode must end the TB */ + dc->base.is_jmp = DISAS_EXIT; } } break; @@ -3454,7 +3455,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); + /* I/O operations in icount mode must end the TB */ + dc->base.is_jmp = DISAS_EXIT; } } break; @@ -3588,7 +3590,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); + /* I/O operations in icount mode must end the TB */ + dc->base.is_jmp = DISAS_EXIT; } } break; @@ -4582,7 +4585,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } gen_helper_wrpstate(cpu_env, cpu_tmp0); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); + /* I/O ops in icount mode must end the TB */ + dc->base.is_jmp = DISAS_EXIT; } dc->npc = DYNAMIC_PC; break; @@ -4598,7 +4602,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } gen_helper_wrpil(cpu_env, cpu_tmp0); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); + /* I/O ops in icount mode must end the TB */ + dc->base.is_jmp = DISAS_EXIT; } break; case 9: // cwp @@ -4697,10 +4702,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); tcg_temp_free_ptr(r_tickptr); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_end(); - } /* End TB to handle timer interrupt */ dc->base.is_jmp = DISAS_EXIT; } @@ -5327,9 +5328,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_io_start(); } gen_helper_done(cpu_env); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - } goto jmp_insn; case 1: if (!supervisor(dc)) @@ -5340,9 +5338,6 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) gen_io_start(); } gen_helper_retry(cpu_env); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - } goto jmp_insn; default: goto illegal_insn; diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index bd084c7840..cc64dd32d2 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -1,4 +1,5 @@ -VPATH+=$(SRC_PATH)/tests/tcg/s390x +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x +VPATH+=$(S390X_SRC) CFLAGS+=-march=zEC12 -m64 TESTS+=hello-s390x TESTS+=csst @@ -9,3 +10,17 @@ TESTS+=pack TESTS+=mvo TESTS+=mvc TESTS+=trap +TESTS+=signals-s390x + +ifneq ($(HAVE_GDB_BIN),) +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py + +run-gdbstub-signals-s390x: signals-s390x + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ + "mixing signals and debugging on s390x") + +EXTRA_RUNS += run-gdbstub-signals-s390x +endif diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py b/tests/tcg/s390x/gdbstub/test-signals-s390x.py new file mode 100644 index 0000000000..80a284b475 --- /dev/null +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -0,0 +1,76 @@ +from __future__ import print_function + +# +# Test that signals and debugging mix well together on s390x. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +failcount = 0 + + +def report(cond, msg): + """Report success/fail of test""" + if cond: + print("PASS: %s" % (msg)) + else: + print("FAIL: %s" % (msg)) + global failcount + failcount += 1 + + +def run_test(): + """Run through the tests one by one""" + illegal_op = gdb.Breakpoint("illegal_op") + stg = gdb.Breakpoint("stg") + mvc_8 = gdb.Breakpoint("mvc_8") + + # Expect the following events: + # 1x illegal_op breakpoint + # 2x stg breakpoint, segv, breakpoint + # 2x mvc_8 breakpoint, segv, breakpoint + for _ in range(14): + gdb.execute("c") + report(illegal_op.hit_count == 1, "illegal_op.hit_count == 1") + report(stg.hit_count == 4, "stg.hit_count == 4") + report(mvc_8.hit_count == 4, "mvc_8.hit_count == 4") + + # The test must succeed. + gdb.Breakpoint("_exit") + gdb.execute("c") + status = int(gdb.parse_and_eval("$r2")) + report(status == 0, "status == 0"); + + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + arch = inferior.architecture() + print("ATTACHED: %s" % arch.name()) +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +if gdb.parse_and_eval("$pc") == 0: + print("SKIP: PC not set") + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except (gdb.error): + print("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + pass + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/s390x/signals-s390x.c b/tests/tcg/s390x/signals-s390x.c new file mode 100644 index 0000000000..dc2f8ee59a --- /dev/null +++ b/tests/tcg/s390x/signals-s390x.c @@ -0,0 +1,165 @@ +#include <assert.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <ucontext.h> +#include <unistd.h> + +/* + * Various instructions that generate SIGILL and SIGSEGV. They could have been + * defined in a separate .s file, but this would complicate the build, so the + * inline asm is used instead. + */ + +void illegal_op(void); +void after_illegal_op(void); +asm(".globl\tillegal_op\n" + "illegal_op:\t.byte\t0x00,0x00\n" + "\t.globl\tafter_illegal_op\n" + "after_illegal_op:\tbr\t%r14"); + +void stg(void *dst, unsigned long src); +asm(".globl\tstg\n" + "stg:\tstg\t%r3,0(%r2)\n" + "\tbr\t%r14"); + +void mvc_8(void *dst, void *src); +asm(".globl\tmvc_8\n" + "mvc_8:\tmvc\t0(8,%r2),0(%r3)\n" + "\tbr\t%r14"); + +static void safe_puts(const char *s) +{ + write(0, s, strlen(s)); + write(0, "\n", 1); +} + +enum exception { + exception_operation, + exception_translation, + exception_protection, +}; + +static struct { + int sig; + void *addr; + unsigned long psw_addr; + enum exception exception; +} expected; + +static void handle_signal(int sig, siginfo_t *info, void *ucontext) +{ + void *page; + int err; + + if (sig != expected.sig) { + safe_puts("[ FAILED ] wrong signal"); + _exit(1); + } + + if (info->si_addr != expected.addr) { + safe_puts("[ FAILED ] wrong si_addr"); + _exit(1); + } + + if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) { + safe_puts("[ FAILED ] wrong psw.addr"); + _exit(1); + } + + switch (expected.exception) { + case exception_translation: + page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (page != expected.addr) { + safe_puts("[ FAILED ] mmap() failed"); + _exit(1); + } + break; + case exception_protection: + err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); + if (err != 0) { + safe_puts("[ FAILED ] mprotect() failed"); + _exit(1); + } + break; + default: + break; + } +} + +static void check_sigsegv(void *func, enum exception exception, + unsigned long val) +{ + int prot; + unsigned long *page; + unsigned long *addr; + int err; + + prot = exception == exception_translation ? PROT_NONE : PROT_READ; + page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(page != MAP_FAILED); + if (exception == exception_translation) { + /* Hopefully nothing will be mapped at this address. */ + err = munmap(page, 4096); + assert(err == 0); + } + addr = page + (val & 0x1ff); + + expected.sig = SIGSEGV; + expected.addr = page; + expected.psw_addr = (unsigned long)func; + expected.exception = exception; + if (func == stg) { + stg(addr, val); + } else { + assert(func == mvc_8); + mvc_8(addr, &val); + } + assert(*addr == val); + + err = munmap(page, 4096); + assert(err == 0); +} + +int main(void) +{ + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_signal; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + safe_puts("[ RUN ] Operation exception"); + expected.sig = SIGILL; + expected.addr = illegal_op; + expected.psw_addr = (unsigned long)after_illegal_op; + expected.exception = exception_operation; + illegal_op(); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Translation exception from stg"); + check_sigsegv(stg, exception_translation, 42); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Translation exception from mvc"); + check_sigsegv(mvc_8, exception_translation, 4242); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Protection exception from stg"); + check_sigsegv(stg, exception_protection, 424242); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Protection exception from mvc"); + check_sigsegv(mvc_8, exception_protection, 42424242); + safe_puts("[ OK ]"); + + safe_puts("[ PASSED ]"); + + _exit(0); +} diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 911115b86e..00a80431a0 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -463,13 +463,15 @@ static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, size_t max_size) { QEMUVFIOState *s = container_of(n, QEMUVFIOState, ram_notifier); + Error *local_err = NULL; int ret; trace_qemu_vfio_ram_block_added(s, host, max_size); - ret = qemu_vfio_dma_map(s, host, max_size, false, NULL); + ret = qemu_vfio_dma_map(s, host, max_size, false, NULL, &local_err); if (ret) { - error_report("qemu_vfio_dma_map(%p, %zu) failed: %s", host, max_size, - strerror(-ret)); + error_reportf_err(local_err, + "qemu_vfio_dma_map(%p, %zu) failed: ", + host, max_size); } } @@ -608,7 +610,7 @@ static IOVAMapping *qemu_vfio_add_mapping(QEMUVFIOState *s, /* Do the DMA mapping with VFIO. */ static int qemu_vfio_do_mapping(QEMUVFIOState *s, void *host, size_t size, - uint64_t iova) + uint64_t iova, Error **errp) { struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map), @@ -620,7 +622,7 @@ static int qemu_vfio_do_mapping(QEMUVFIOState *s, void *host, size_t size, trace_qemu_vfio_do_mapping(s, host, iova, size); if (ioctl(s->container, VFIO_IOMMU_MAP_DMA, &dma_map)) { - error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); + error_setg_errno(errp, errno, "VFIO_MAP_DMA failed"); return -errno; } return 0; @@ -660,13 +662,13 @@ static bool qemu_vfio_verify_mappings(QEMUVFIOState *s) if (QEMU_VFIO_DEBUG) { for (i = 0; i < s->nr_mappings - 1; ++i) { if (!(s->mappings[i].host < s->mappings[i + 1].host)) { - fprintf(stderr, "item %d not sorted!\n", i); + error_report("item %d not sorted!", i); qemu_vfio_dump_mappings(s); return false; } if (!(s->mappings[i].host + s->mappings[i].size <= s->mappings[i + 1].host)) { - fprintf(stderr, "item %d overlap with next!\n", i); + error_report("item %d overlap with next!", i); qemu_vfio_dump_mappings(s); return false; } @@ -675,8 +677,8 @@ static bool qemu_vfio_verify_mappings(QEMUVFIOState *s) return true; } -static int -qemu_vfio_find_fixed_iova(QEMUVFIOState *s, size_t size, uint64_t *iova) +static bool qemu_vfio_find_fixed_iova(QEMUVFIOState *s, size_t size, + uint64_t *iova, Error **errp) { int i; @@ -691,14 +693,16 @@ qemu_vfio_find_fixed_iova(QEMUVFIOState *s, size_t size, uint64_t *iova) s->usable_iova_ranges[i].end - s->low_water_mark + 1 == 0) { *iova = s->low_water_mark; s->low_water_mark += size; - return 0; + return true; } } - return -ENOMEM; + error_setg(errp, "fixed iova range not found"); + + return false; } -static int -qemu_vfio_find_temp_iova(QEMUVFIOState *s, size_t size, uint64_t *iova) +static bool qemu_vfio_find_temp_iova(QEMUVFIOState *s, size_t size, + uint64_t *iova, Error **errp) { int i; @@ -713,10 +717,27 @@ qemu_vfio_find_temp_iova(QEMUVFIOState *s, size_t size, uint64_t *iova) s->high_water_mark - s->usable_iova_ranges[i].start + 1 == 0) { *iova = s->high_water_mark - size; s->high_water_mark = *iova; - return 0; + return true; } } - return -ENOMEM; + error_setg(errp, "temporary iova range not found"); + + return false; +} + +/** + * qemu_vfio_water_mark_reached: + * + * Returns %true if high watermark has been reached, %false otherwise. + */ +static bool qemu_vfio_water_mark_reached(QEMUVFIOState *s, size_t size, + Error **errp) +{ + if (s->high_water_mark - s->low_water_mark + 1 < size) { + error_setg(errp, "iova exhausted (water mark reached)"); + return true; + } + return false; } /* Map [host, host + size) area into a contiguous IOVA address space, and store @@ -725,9 +746,8 @@ qemu_vfio_find_temp_iova(QEMUVFIOState *s, size_t size, uint64_t *iova) * mapping status within this area is not allowed). */ int qemu_vfio_dma_map(QEMUVFIOState *s, void *host, size_t size, - bool temporary, uint64_t *iova) + bool temporary, uint64_t *iova, Error **errp) { - int ret = 0; int index; IOVAMapping *mapping; uint64_t iova0; @@ -735,41 +755,36 @@ int qemu_vfio_dma_map(QEMUVFIOState *s, void *host, size_t size, assert(QEMU_PTR_IS_ALIGNED(host, qemu_real_host_page_size)); assert(QEMU_IS_ALIGNED(size, qemu_real_host_page_size)); trace_qemu_vfio_dma_map(s, host, size, temporary, iova); - qemu_mutex_lock(&s->lock); + QEMU_LOCK_GUARD(&s->lock); mapping = qemu_vfio_find_mapping(s, host, &index); if (mapping) { iova0 = mapping->iova + ((uint8_t *)host - (uint8_t *)mapping->host); } else { - if (s->high_water_mark - s->low_water_mark + 1 < size) { - ret = -ENOMEM; - goto out; + int ret; + + if (qemu_vfio_water_mark_reached(s, size, errp)) { + return -ENOMEM; } if (!temporary) { - if (qemu_vfio_find_fixed_iova(s, size, &iova0)) { - ret = -ENOMEM; - goto out; + if (!qemu_vfio_find_fixed_iova(s, size, &iova0, errp)) { + return -ENOMEM; } mapping = qemu_vfio_add_mapping(s, host, size, index + 1, iova0); - if (!mapping) { - ret = -ENOMEM; - goto out; - } assert(qemu_vfio_verify_mappings(s)); - ret = qemu_vfio_do_mapping(s, host, size, iova0); - if (ret) { + ret = qemu_vfio_do_mapping(s, host, size, iova0, errp); + if (ret < 0) { qemu_vfio_undo_mapping(s, mapping, NULL); - goto out; + return ret; } qemu_vfio_dump_mappings(s); } else { - if (qemu_vfio_find_temp_iova(s, size, &iova0)) { - ret = -ENOMEM; - goto out; + if (!qemu_vfio_find_temp_iova(s, size, &iova0, errp)) { + return -ENOMEM; } - ret = qemu_vfio_do_mapping(s, host, size, iova0); - if (ret) { - goto out; + ret = qemu_vfio_do_mapping(s, host, size, iova0, errp); + if (ret < 0) { + return ret; } } } @@ -777,9 +792,7 @@ int qemu_vfio_dma_map(QEMUVFIOState *s, void *host, size_t size, if (iova) { *iova = iova0; } -out: - qemu_mutex_unlock(&s->lock); - return ret; + return 0; } /* Reset the high watermark and free all "temporary" mappings. */ @@ -813,14 +826,12 @@ void qemu_vfio_dma_unmap(QEMUVFIOState *s, void *host) } trace_qemu_vfio_dma_unmap(s, host); - qemu_mutex_lock(&s->lock); + QEMU_LOCK_GUARD(&s->lock); m = qemu_vfio_find_mapping(s, host, &index); if (!m) { - goto out; + return; } qemu_vfio_undo_mapping(s, m, NULL); -out: - qemu_mutex_unlock(&s->lock); } static void qemu_vfio_reset(QEMUVFIOState *s) |