aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel/tcg/cpu-exec.c15
-rw-r--r--block/nbd-client.c492
-rw-r--r--block/nbd-client.h1
-rw-r--r--hw/arm/msf2-soc.c11
-rw-r--r--hw/arm/xlnx-zcu102.c1
-rw-r--r--hw/dma/sparc32_dma.c252
-rw-r--r--hw/dma/sun4m_iommu.c101
-rw-r--r--hw/dma/trace-events8
-rw-r--r--hw/ide/Makefile.objs1
-rw-r--r--hw/ide/ahci-allwinner.c127
-rw-r--r--hw/ide/ahci.c95
-rw-r--r--hw/ide/core.c4
-rw-r--r--hw/net/lance.c11
-rw-r--r--hw/pci-host/gpex.c10
-rw-r--r--hw/scsi/esp.c13
-rw-r--r--hw/sparc/sun4m.c82
-rw-r--r--hw/ssi/mss-spi.c18
-rw-r--r--include/block/nbd.h106
-rw-r--r--include/hw/net/lance.h45
-rw-r--r--include/hw/scsi/esp.h14
-rw-r--r--include/hw/sparc/sparc32_dma.h55
-rw-r--r--include/hw/sparc/sun4m.h35
-rw-r--r--nbd/client.c203
-rw-r--r--nbd/common.c84
-rw-r--r--nbd/nbd-internal.h23
-rw-r--r--nbd/server.c210
-rw-r--r--nbd/trace-events15
-rw-r--r--target/arm/helper.h2
-rw-r--r--target/arm/internals.h3
-rw-r--r--target/arm/op_helper.c7
-rw-r--r--target/arm/psci.c2
-rw-r--r--target/arm/translate-a64.c7
-rw-r--r--target/arm/translate.c10
-rw-r--r--tcg/s390/tcg-target.inc.c44
-rw-r--r--tcg/tcg.c49
-rw-r--r--tests/qemu-iotests/083.out15
-rw-r--r--ui/cocoa.m2
-rw-r--r--util/oslib-posix.c11
38 files changed, 1640 insertions, 544 deletions
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 4318441e4c..61297f8f4a 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -233,6 +233,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
uint32_t flags;
uint32_t cflags = 1;
uint32_t cf_mask = cflags & CF_HASH_MASK;
+ /* volatile because we modify it between setjmp and longjmp */
+ volatile bool in_exclusive_region = false;
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
@@ -251,14 +253,12 @@ void cpu_exec_step_atomic(CPUState *cpu)
/* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false;
+ in_exclusive_region = true;
cc->cpu_exec_enter(cpu);
/* execute the generated code */
trace_exec_tb(tb, pc);
cpu_tb_exec(cpu, tb);
cc->cpu_exec_exit(cpu);
- parallel_cpus = true;
-
- end_exclusive();
} else {
/* We may have exited due to another problem here, so we need
* to reset any tb_locks we may have taken but didn't release.
@@ -270,6 +270,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
#endif
tb_lock_reset();
}
+
+ if (in_exclusive_region) {
+ /* We might longjump out of either the codegen or the
+ * execution, so must make sure we only end the exclusive
+ * region if we started it.
+ */
+ parallel_cpus = true;
+ end_exclusive();
+ }
}
struct tb_desc {
diff --git a/block/nbd-client.c b/block/nbd-client.c
index c0683c3c83..b44d4d4a01 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -92,7 +92,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
i = HANDLE_TO_INDEX(s, s->reply.handle);
if (i >= MAX_NBD_REQUESTS ||
!s->requests[i].coroutine ||
- !s->requests[i].receiving) {
+ !s->requests[i].receiving ||
+ (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply))
+ {
break;
}
@@ -139,6 +141,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
assert(i < MAX_NBD_REQUESTS);
s->requests[i].coroutine = qemu_coroutine_self();
+ s->requests[i].offset = request->from;
s->requests[i].receiving = false;
request->handle = INDEX_TO_HANDLE(s, i);
@@ -179,75 +182,489 @@ err:
return rc;
}
-static int nbd_co_receive_reply(NBDClientSession *s,
- uint64_t handle,
- QEMUIOVector *qiov)
+static inline uint16_t payload_advance16(uint8_t **payload)
+{
+ *payload += 2;
+ return lduw_be_p(*payload - 2);
+}
+
+static inline uint32_t payload_advance32(uint8_t **payload)
+{
+ *payload += 4;
+ return ldl_be_p(*payload - 4);
+}
+
+static inline uint64_t payload_advance64(uint8_t **payload)
+{
+ *payload += 8;
+ return ldq_be_p(*payload - 8);
+}
+
+static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
+ uint8_t *payload, uint64_t orig_offset,
+ QEMUIOVector *qiov, Error **errp)
+{
+ uint64_t offset;
+ uint32_t hole_size;
+
+ if (chunk->length != sizeof(offset) + sizeof(hole_size)) {
+ error_setg(errp, "Protocol error: invalid payload for "
+ "NBD_REPLY_TYPE_OFFSET_HOLE");
+ return -EINVAL;
+ }
+
+ offset = payload_advance64(&payload);
+ hole_size = payload_advance32(&payload);
+
+ if (offset < orig_offset || hole_size > qiov->size ||
+ offset > orig_offset + qiov->size - hole_size) {
+ error_setg(errp, "Protocol error: server sent chunk exceeding requested"
+ " region");
+ return -EINVAL;
+ }
+
+ qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size);
+
+ return 0;
+}
+
+/* nbd_parse_error_payload
+ * on success @errp contains message describing nbd error reply
+ */
+static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk,
+ uint8_t *payload, int *request_ret,
+ Error **errp)
+{
+ uint32_t error;
+ uint16_t message_size;
+
+ assert(chunk->type & (1 << 15));
+
+ if (chunk->length < sizeof(error) + sizeof(message_size)) {
+ error_setg(errp,
+ "Protocol error: invalid payload for structured error");
+ return -EINVAL;
+ }
+
+ error = nbd_errno_to_system_errno(payload_advance32(&payload));
+ if (error == 0) {
+ error_setg(errp, "Protocol error: server sent structured error chunk"
+ "with error = 0");
+ return -EINVAL;
+ }
+
+ *request_ret = -error;
+ message_size = payload_advance16(&payload);
+
+ if (message_size > chunk->length - sizeof(error) - sizeof(message_size)) {
+ error_setg(errp, "Protocol error: server sent structured error chunk"
+ "with incorrect message size");
+ return -EINVAL;
+ }
+
+ /* TODO: Add a trace point to mention the server complaint */
+
+ /* TODO handle ERROR_OFFSET */
+
+ return 0;
+}
+
+static int nbd_co_receive_offset_data_payload(NBDClientSession *s,
+ uint64_t orig_offset,
+ QEMUIOVector *qiov, Error **errp)
+{
+ QEMUIOVector sub_qiov;
+ uint64_t offset;
+ size_t data_size;
+ int ret;
+ NBDStructuredReplyChunk *chunk = &s->reply.structured;
+
+ assert(nbd_reply_is_structured(&s->reply));
+
+ if (chunk->length < sizeof(offset)) {
+ error_setg(errp, "Protocol error: invalid payload for "
+ "NBD_REPLY_TYPE_OFFSET_DATA");
+ return -EINVAL;
+ }
+
+ if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) {
+ return -EIO;
+ }
+ be64_to_cpus(&offset);
+
+ data_size = chunk->length - sizeof(offset);
+ if (offset < orig_offset || data_size > qiov->size ||
+ offset > orig_offset + qiov->size - data_size) {
+ error_setg(errp, "Protocol error: server sent chunk exceeding requested"
+ " region");
+ return -EINVAL;
+ }
+
+ qemu_iovec_init(&sub_qiov, qiov->niov);
+ qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size);
+ ret = qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, errp);
+ qemu_iovec_destroy(&sub_qiov);
+
+ return ret < 0 ? -EIO : 0;
+}
+
+#define NBD_MAX_MALLOC_PAYLOAD 1000
+/* nbd_co_receive_structured_payload
+ */
+static coroutine_fn int nbd_co_receive_structured_payload(
+ NBDClientSession *s, void **payload, Error **errp)
+{
+ int ret;
+ uint32_t len;
+
+ assert(nbd_reply_is_structured(&s->reply));
+
+ len = s->reply.structured.length;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ if (payload == NULL) {
+ error_setg(errp, "Unexpected structured payload");
+ return -EINVAL;
+ }
+
+ if (len > NBD_MAX_MALLOC_PAYLOAD) {
+ error_setg(errp, "Payload too large");
+ return -EINVAL;
+ }
+
+ *payload = g_new(char, len);
+ ret = nbd_read(s->ioc, *payload, len, errp);
+ if (ret < 0) {
+ g_free(*payload);
+ *payload = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+/* nbd_co_do_receive_one_chunk
+ * for simple reply:
+ * set request_ret to received reply error
+ * if qiov is not NULL: read payload to @qiov
+ * for structured reply chunk:
+ * if error chunk: read payload, set @request_ret, do not set @payload
+ * else if offset_data chunk: read payload data to @qiov, do not set @payload
+ * else: read payload to @payload
+ *
+ * If function fails, @errp contains corresponding error message, and the
+ * connection with the server is suspect. If it returns 0, then the
+ * transaction succeeded (although @request_ret may be a negative errno
+ * corresponding to the server's error reply), and errp is unchanged.
+ */
+static coroutine_fn int nbd_co_do_receive_one_chunk(
+ NBDClientSession *s, uint64_t handle, bool only_structured,
+ int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp)
{
int ret;
int i = HANDLE_TO_INDEX(s, handle);
+ void *local_payload = NULL;
+ NBDStructuredReplyChunk *chunk;
+
+ if (payload) {
+ *payload = NULL;
+ }
+ *request_ret = 0;
/* Wait until we're woken up by nbd_read_reply_entry. */
s->requests[i].receiving = true;
qemu_coroutine_yield();
s->requests[i].receiving = false;
if (!s->ioc || s->quit) {
- ret = -EIO;
- } else {
- assert(s->reply.handle == handle);
- ret = -s->reply.error;
- if (qiov && s->reply.error == 0) {
- if (qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov,
- NULL) < 0) {
- ret = -EIO;
- s->quit = true;
- }
+ error_setg(errp, "Connection closed");
+ return -EIO;
+ }
+
+ assert(s->reply.handle == handle);
+
+ if (nbd_reply_is_simple(&s->reply)) {
+ if (only_structured) {
+ error_setg(errp, "Protocol error: simple reply when structured "
+ "reply chunk was expected");
+ return -EINVAL;
}
- /* Tell the read handler to read another header. */
- s->reply.handle = 0;
+ *request_ret = -nbd_errno_to_system_errno(s->reply.simple.error);
+ if (*request_ret < 0 || !qiov) {
+ return 0;
+ }
+
+ return qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov,
+ errp) < 0 ? -EIO : 0;
+ }
+
+ /* handle structured reply chunk */
+ assert(s->info.structured_reply);
+ chunk = &s->reply.structured;
+
+ if (chunk->type == NBD_REPLY_TYPE_NONE) {
+ if (!(chunk->flags & NBD_REPLY_FLAG_DONE)) {
+ error_setg(errp, "Protocol error: NBD_REPLY_TYPE_NONE chunk without"
+ "NBD_REPLY_FLAG_DONE flag set");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (chunk->type == NBD_REPLY_TYPE_OFFSET_DATA) {
+ if (!qiov) {
+ error_setg(errp, "Unexpected NBD_REPLY_TYPE_OFFSET_DATA chunk");
+ return -EINVAL;
+ }
+
+ return nbd_co_receive_offset_data_payload(s, s->requests[i].offset,
+ qiov, errp);
+ }
+
+ if (nbd_reply_type_is_error(chunk->type)) {
+ payload = &local_payload;
+ }
+
+ ret = nbd_co_receive_structured_payload(s, payload, errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (nbd_reply_type_is_error(chunk->type)) {
+ ret = nbd_parse_error_payload(chunk, local_payload, request_ret, errp);
+ g_free(local_payload);
+ return ret;
}
- s->requests[i].coroutine = NULL;
+ return 0;
+}
+
+/* nbd_co_receive_one_chunk
+ * Read reply, wake up read_reply_co and set s->quit if needed.
+ * Return value is a fatal error code or normal nbd reply error code
+ */
+static coroutine_fn int nbd_co_receive_one_chunk(
+ NBDClientSession *s, uint64_t handle, bool only_structured,
+ QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp)
+{
+ int request_ret;
+ int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured,
+ &request_ret, qiov, payload, errp);
+
+ if (ret < 0) {
+ s->quit = true;
+ } else {
+ /* For assert at loop start in nbd_read_reply_entry */
+ if (reply) {
+ *reply = s->reply;
+ }
+ s->reply.handle = 0;
+ ret = request_ret;
+ }
- /* Kick the read_reply_co to get the next reply. */
if (s->read_reply_co) {
aio_co_wake(s->read_reply_co);
}
+ return ret;
+}
+
+typedef struct NBDReplyChunkIter {
+ int ret;
+ Error *err;
+ bool done, only_structured;
+} NBDReplyChunkIter;
+
+static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
+ int ret, Error **local_err)
+{
+ assert(ret < 0);
+
+ if (fatal || iter->ret == 0) {
+ if (iter->ret != 0) {
+ error_free(iter->err);
+ iter->err = NULL;
+ }
+ iter->ret = ret;
+ error_propagate(&iter->err, *local_err);
+ } else {
+ error_free(*local_err);
+ }
+
+ *local_err = NULL;
+}
+
+/* NBD_FOREACH_REPLY_CHUNK
+ */
+#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \
+ qiov, reply, payload) \
+ for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \
+ nbd_reply_chunk_iter_receive(s, &iter, handle, qiov, reply, payload);)
+
+/* nbd_reply_chunk_iter_receive
+ */
+static bool nbd_reply_chunk_iter_receive(NBDClientSession *s,
+ NBDReplyChunkIter *iter,
+ uint64_t handle,
+ QEMUIOVector *qiov, NBDReply *reply,
+ void **payload)
+{
+ int ret;
+ NBDReply local_reply;
+ NBDStructuredReplyChunk *chunk;
+ Error *local_err = NULL;
+ if (s->quit) {
+ error_setg(&local_err, "Connection closed");
+ nbd_iter_error(iter, true, -EIO, &local_err);
+ goto break_loop;
+ }
+
+ if (iter->done) {
+ /* Previous iteration was last. */
+ goto break_loop;
+ }
+
+ if (reply == NULL) {
+ reply = &local_reply;
+ }
+
+ ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured,
+ qiov, reply, payload, &local_err);
+ if (ret < 0) {
+ /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */
+ nbd_iter_error(iter, s->quit, ret, &local_err);
+ }
+
+ /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
+ if (nbd_reply_is_simple(&s->reply) || s->quit) {
+ goto break_loop;
+ }
+
+ chunk = &reply->structured;
+ iter->only_structured = true;
+
+ if (chunk->type == NBD_REPLY_TYPE_NONE) {
+ /* NBD_REPLY_FLAG_DONE is already checked in nbd_co_receive_one_chunk */
+ assert(chunk->flags & NBD_REPLY_FLAG_DONE);
+ goto break_loop;
+ }
+
+ if (chunk->flags & NBD_REPLY_FLAG_DONE) {
+ /* This iteration is last. */
+ iter->done = true;
+ }
+
+ /* Execute the loop body */
+ return true;
+
+break_loop:
+ s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;
+
qemu_co_mutex_lock(&s->send_mutex);
s->in_flight--;
qemu_co_queue_next(&s->free_sema);
qemu_co_mutex_unlock(&s->send_mutex);
- return ret;
+ return false;
}
-static int nbd_co_request(BlockDriverState *bs,
- NBDRequest *request,
- QEMUIOVector *qiov)
+static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle,
+ Error **errp)
+{
+ NBDReplyChunkIter iter;
+
+ NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, NULL, NULL) {
+ /* nbd_reply_chunk_iter_receive does all the work */
+ }
+
+ error_propagate(errp, iter.err);
+ return iter.ret;
+}
+
+static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
+ uint64_t offset, QEMUIOVector *qiov,
+ Error **errp)
+{
+ NBDReplyChunkIter iter;
+ NBDReply reply;
+ void *payload = NULL;
+ Error *local_err = NULL;
+
+ NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
+ qiov, &reply, &payload)
+ {
+ int ret;
+ NBDStructuredReplyChunk *chunk = &reply.structured;
+
+ assert(nbd_reply_is_structured(&reply));
+
+ switch (chunk->type) {
+ case NBD_REPLY_TYPE_OFFSET_DATA:
+ /* special cased in nbd_co_receive_one_chunk, data is already
+ * in qiov */
+ break;
+ case NBD_REPLY_TYPE_OFFSET_HOLE:
+ ret = nbd_parse_offset_hole_payload(&reply.structured, payload,
+ offset, qiov, &local_err);
+ if (ret < 0) {
+ s->quit = true;
+ nbd_iter_error(&iter, true, ret, &local_err);
+ }
+ break;
+ default:
+ if (!nbd_reply_type_is_error(chunk->type)) {
+ /* not allowed reply type */
+ s->quit = true;
+ error_setg(&local_err,
+ "Unexpected reply type: %d (%s) for CMD_READ",
+ chunk->type, nbd_reply_type_lookup(chunk->type));
+ nbd_iter_error(&iter, true, -EINVAL, &local_err);
+ }
+ }
+
+ g_free(payload);
+ payload = NULL;
+ }
+
+ error_propagate(errp, iter.err);
+ return iter.ret;
+}
+
+static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
+ QEMUIOVector *write_qiov)
{
- NBDClientSession *client = nbd_get_client_session(bs);
int ret;
+ Error *local_err = NULL;
+ NBDClientSession *client = nbd_get_client_session(bs);
- if (qiov) {
- assert(request->type == NBD_CMD_WRITE || request->type == NBD_CMD_READ);
- assert(request->len == iov_size(qiov->iov, qiov->niov));
+ assert(request->type != NBD_CMD_READ);
+ if (write_qiov) {
+ assert(request->type == NBD_CMD_WRITE);
+ assert(request->len == iov_size(write_qiov->iov, write_qiov->niov));
} else {
- assert(request->type != NBD_CMD_WRITE && request->type != NBD_CMD_READ);
+ assert(request->type != NBD_CMD_WRITE);
}
- ret = nbd_co_send_request(bs, request,
- request->type == NBD_CMD_WRITE ? qiov : NULL);
+ ret = nbd_co_send_request(bs, request, write_qiov);
if (ret < 0) {
return ret;
}
- return nbd_co_receive_reply(client, request->handle,
- request->type == NBD_CMD_READ ? qiov : NULL);
+ ret = nbd_co_receive_return_code(client, request->handle, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ return ret;
}
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, int flags)
{
+ int ret;
+ Error *local_err = NULL;
+ NBDClientSession *client = nbd_get_client_session(bs);
NBDRequest request = {
.type = NBD_CMD_READ,
.from = offset,
@@ -257,7 +674,17 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
assert(bytes <= NBD_MAX_BUFFER_SIZE);
assert(!flags);
- return nbd_co_request(bs, &request, qiov);
+ ret = nbd_co_send_request(bs, &request, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov,
+ &local_err);
+ if (ret < 0) {
+ error_report_err(local_err);
+ }
+ return ret;
}
int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
@@ -379,6 +806,7 @@ int nbd_client_init(BlockDriverState *bs,
qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL);
client->info.request_sizes = true;
+ client->info.structured_reply = true;
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
tlscreds, hostname,
&client->ioc, &client->info, errp);
diff --git a/block/nbd-client.h b/block/nbd-client.h
index b435754b82..612c4c21a0 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -19,6 +19,7 @@
typedef struct {
Coroutine *coroutine;
+ uint64_t offset; /* original offset of the request */
bool receiving; /* waiting for read_reply_co? */
} NBDClientRequest;
diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c
index 6f97fa9fe3..a8ec2cdf36 100644
--- a/hw/arm/msf2-soc.c
+++ b/hw/arm/msf2-soc.c
@@ -57,6 +57,13 @@ static const int spi_irq[MSF2_NUM_SPIS] = { 2, 3 };
static const int uart_irq[MSF2_NUM_UARTS] = { 10, 11 };
static const int timer_irq[MSF2_NUM_TIMERS] = { 14, 15 };
+static void do_sys_reset(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+}
+
static void m2sxxx_soc_initfn(Object *obj)
{
MSF2State *s = MSF2_SOC(obj);
@@ -125,6 +132,10 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
error_append_hint(errp, "m3clk can not be zero\n");
return;
}
+
+ qdev_connect_gpio_out_named(DEVICE(&s->armv7m.nvic), "SYSRESETREQ", 0,
+ qemu_allocate_irq(&do_sys_reset, NULL, 0));
+
system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk;
for (i = 0; i < MSF2_NUM_UARTS; i++) {
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 519a16ed98..e2d15a1c9d 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -240,6 +240,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data)
mc->block_default_type = IF_IDE;
mc->units_per_default_bus = 1;
mc->ignore_memory_transaction_failures = true;
+ mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS;
}
static const TypeInfo xlnx_zcu102_machine_init_typeinfo = {
diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c
index eb491b50ca..01afb758b6 100644
--- a/hw/dma/sparc32_dma.c
+++ b/hw/dma/sparc32_dma.c
@@ -30,6 +30,8 @@
#include "hw/sparc/sparc32_dma.h"
#include "hw/sparc/sun4m.h"
#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+#include "qapi/error.h"
#include "trace.h"
/*
@@ -40,7 +42,6 @@
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/DMA2.txt
*/
-#define DMA_REGS 4
#define DMA_SIZE (4 * sizeof(uint32_t))
/* We need the mask, because one instance of the device is not page
aligned (ledma, start address 0x0010) */
@@ -61,22 +62,6 @@
/* XXX SCSI and ethernet should have different read-only bit masks */
#define DMA_CSR_RO_MASK 0xfe000007
-#define TYPE_SPARC32_DMA "sparc32_dma"
-#define SPARC32_DMA(obj) OBJECT_CHECK(DMAState, (obj), TYPE_SPARC32_DMA)
-
-typedef struct DMAState DMAState;
-
-struct DMAState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint32_t dmaregs[DMA_REGS];
- qemu_irq irq;
- void *iommu;
- qemu_irq gpio[2];
- uint32_t is_ledma;
-};
-
enum {
GPIO_RESET = 0,
GPIO_DMA,
@@ -86,17 +71,18 @@ enum {
void ledma_memory_read(void *opaque, hwaddr addr,
uint8_t *buf, int len, int do_bswap)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
+ IOMMUState *is = (IOMMUState *)s->iommu;
int i;
addr |= s->dmaregs[3];
- trace_ledma_memory_read(addr);
+ trace_ledma_memory_read(addr, len);
if (do_bswap) {
- sparc_iommu_memory_read(s->iommu, addr, buf, len);
+ dma_memory_read(&is->iommu_as, addr, buf, len);
} else {
addr &= ~1;
len &= ~1;
- sparc_iommu_memory_read(s->iommu, addr, buf, len);
+ dma_memory_read(&is->iommu_as, addr, buf, len);
for(i = 0; i < len; i += 2) {
bswap16s((uint16_t *)(buf + i));
}
@@ -106,14 +92,15 @@ void ledma_memory_read(void *opaque, hwaddr addr,
void ledma_memory_write(void *opaque, hwaddr addr,
uint8_t *buf, int len, int do_bswap)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
+ IOMMUState *is = (IOMMUState *)s->iommu;
int l, i;
uint16_t tmp_buf[32];
addr |= s->dmaregs[3];
- trace_ledma_memory_write(addr);
+ trace_ledma_memory_write(addr, len);
if (do_bswap) {
- sparc_iommu_memory_write(s->iommu, addr, buf, len);
+ dma_memory_write(&is->iommu_as, addr, buf, len);
} else {
addr &= ~1;
len &= ~1;
@@ -124,7 +111,7 @@ void ledma_memory_write(void *opaque, hwaddr addr,
for(i = 0; i < l; i += 2) {
tmp_buf[i >> 1] = bswap16(*(uint16_t *)(buf + i));
}
- sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)tmp_buf, l);
+ dma_memory_write(&is->iommu_as, addr, tmp_buf, l);
len -= l;
buf += l;
addr += l;
@@ -134,7 +121,7 @@ void ledma_memory_write(void *opaque, hwaddr addr,
static void dma_set_irq(void *opaque, int irq, int level)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
if (level) {
s->dmaregs[0] |= DMA_INTR;
if (s->dmaregs[0] & DMA_INTREN) {
@@ -154,34 +141,30 @@ static void dma_set_irq(void *opaque, int irq, int level)
void espdma_memory_read(void *opaque, uint8_t *buf, int len)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
+ IOMMUState *is = (IOMMUState *)s->iommu;
- trace_espdma_memory_read(s->dmaregs[1]);
- sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len);
+ trace_espdma_memory_read(s->dmaregs[1], len);
+ dma_memory_read(&is->iommu_as, s->dmaregs[1], buf, len);
s->dmaregs[1] += len;
}
void espdma_memory_write(void *opaque, uint8_t *buf, int len)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
+ IOMMUState *is = (IOMMUState *)s->iommu;
- trace_espdma_memory_write(s->dmaregs[1]);
- sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len);
+ trace_espdma_memory_write(s->dmaregs[1], len);
+ dma_memory_write(&is->iommu_as, s->dmaregs[1], buf, len);
s->dmaregs[1] += len;
}
static uint64_t dma_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
uint32_t saddr;
- if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) {
- /* aliased to espdma, but we can't get there from here */
- /* buggy driver if using undocumented behavior, just return 0 */
- trace_sparc32_dma_mem_readl(addr, 0);
- return 0;
- }
saddr = (addr & DMA_MASK) >> 2;
trace_sparc32_dma_mem_readl(addr, s->dmaregs[saddr]);
return s->dmaregs[saddr];
@@ -190,14 +173,9 @@ static uint64_t dma_mem_read(void *opaque, hwaddr addr,
static void dma_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
- DMAState *s = opaque;
+ DMADeviceState *s = opaque;
uint32_t saddr;
- if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) {
- /* aliased to espdma, but we can't get there from here */
- trace_sparc32_dma_mem_writel(addr, 0, val);
- return;
- }
saddr = (addr & DMA_MASK) >> 2;
trace_sparc32_dma_mem_writel(addr, s->dmaregs[saddr], val);
switch (saddr) {
@@ -252,76 +230,216 @@ static const MemoryRegionOps dma_mem_ops = {
},
};
-static void dma_reset(DeviceState *d)
+static void sparc32_dma_device_reset(DeviceState *d)
{
- DMAState *s = SPARC32_DMA(d);
+ DMADeviceState *s = SPARC32_DMA_DEVICE(d);
memset(s->dmaregs, 0, DMA_SIZE);
s->dmaregs[0] = DMA_VER;
}
-static const VMStateDescription vmstate_dma = {
+static const VMStateDescription vmstate_sparc32_dma_device = {
.name ="sparc32_dma",
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(dmaregs, DMAState, DMA_REGS),
+ VMSTATE_UINT32_ARRAY(dmaregs, DMADeviceState, DMA_REGS),
VMSTATE_END_OF_LIST()
}
};
-static void sparc32_dma_init(Object *obj)
+static void sparc32_dma_device_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
- DMAState *s = SPARC32_DMA(obj);
+ DMADeviceState *s = SPARC32_DMA_DEVICE(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_mmio(sbd, &s->iomem);
+ object_property_add_link(OBJECT(dev), "iommu", TYPE_SUN4M_IOMMU,
+ (Object **) &s->iommu,
+ qdev_prop_allow_set_link_before_realize,
+ 0, NULL);
+
qdev_init_gpio_in(dev, dma_set_irq, 1);
qdev_init_gpio_out(dev, s->gpio, 2);
}
-static void sparc32_dma_realize(DeviceState *dev, Error **errp)
+static void sparc32_dma_device_class_init(ObjectClass *klass, void *data)
{
- DMAState *s = SPARC32_DMA(dev);
- int reg_size;
+ DeviceClass *dc = DEVICE_CLASS(klass);
- reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE;
- memory_region_init_io(&s->iomem, OBJECT(dev), &dma_mem_ops, s,
- "dma", reg_size);
+ dc->reset = sparc32_dma_device_reset;
+ dc->vmsd = &vmstate_sparc32_dma_device;
}
-static Property sparc32_dma_properties[] = {
- DEFINE_PROP_PTR("iommu_opaque", DMAState, iommu),
- DEFINE_PROP_UINT32("is_ledma", DMAState, is_ledma, 0),
- DEFINE_PROP_END_OF_LIST(),
+static const TypeInfo sparc32_dma_device_info = {
+ .name = TYPE_SPARC32_DMA_DEVICE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .abstract = true,
+ .instance_size = sizeof(DMADeviceState),
+ .instance_init = sparc32_dma_device_init,
+ .class_init = sparc32_dma_device_class_init,
};
+static void sparc32_espdma_device_init(Object *obj)
+{
+ DMADeviceState *s = SPARC32_DMA_DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s,
+ "espdma-mmio", DMA_SIZE);
+}
+
+static void sparc32_espdma_device_realize(DeviceState *dev, Error **errp)
+{
+ DeviceState *d;
+ SysBusESPState *sysbus;
+ ESPState *esp;
+
+ d = qdev_create(NULL, TYPE_ESP);
+ object_property_add_child(OBJECT(dev), "esp", OBJECT(d), errp);
+ sysbus = ESP_STATE(d);
+ esp = &sysbus->esp;
+ esp->dma_memory_read = espdma_memory_read;
+ esp->dma_memory_write = espdma_memory_write;
+ esp->dma_opaque = SPARC32_DMA_DEVICE(dev);
+ sysbus->it_shift = 2;
+ esp->dma_enabled = 1;
+ qdev_init_nofail(d);
+}
+
+static void sparc32_espdma_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = sparc32_espdma_device_realize;
+}
+
+static const TypeInfo sparc32_espdma_device_info = {
+ .name = TYPE_SPARC32_ESPDMA_DEVICE,
+ .parent = TYPE_SPARC32_DMA_DEVICE,
+ .instance_size = sizeof(ESPDMADeviceState),
+ .instance_init = sparc32_espdma_device_init,
+ .class_init = sparc32_espdma_device_class_init,
+};
+
+static void sparc32_ledma_device_init(Object *obj)
+{
+ DMADeviceState *s = SPARC32_DMA_DEVICE(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s,
+ "ledma-mmio", DMA_SIZE);
+}
+
+static void sparc32_ledma_device_realize(DeviceState *dev, Error **errp)
+{
+ DeviceState *d;
+ NICInfo *nd = &nd_table[0];
+
+ qemu_check_nic_model(nd, TYPE_LANCE);
+
+ d = qdev_create(NULL, TYPE_LANCE);
+ object_property_add_child(OBJECT(dev), "lance", OBJECT(d), errp);
+ qdev_set_nic_properties(d, nd);
+ qdev_prop_set_ptr(d, "dma", dev);
+ qdev_init_nofail(d);
+}
+
+static void sparc32_ledma_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = sparc32_ledma_device_realize;
+}
+
+static const TypeInfo sparc32_ledma_device_info = {
+ .name = TYPE_SPARC32_LEDMA_DEVICE,
+ .parent = TYPE_SPARC32_DMA_DEVICE,
+ .instance_size = sizeof(LEDMADeviceState),
+ .instance_init = sparc32_ledma_device_init,
+ .class_init = sparc32_ledma_device_class_init,
+};
+
+static void sparc32_dma_realize(DeviceState *dev, Error **errp)
+{
+ SPARC32DMAState *s = SPARC32_DMA(dev);
+ DeviceState *espdma, *esp, *ledma, *lance;
+ SysBusDevice *sbd;
+ Object *iommu;
+
+ iommu = object_resolve_path_type("", TYPE_SUN4M_IOMMU, NULL);
+ if (!iommu) {
+ error_setg(errp, "unable to locate sun4m IOMMU device");
+ return;
+ }
+
+ espdma = qdev_create(NULL, TYPE_SPARC32_ESPDMA_DEVICE);
+ object_property_set_link(OBJECT(espdma), iommu, "iommu", errp);
+ object_property_add_child(OBJECT(s), "espdma", OBJECT(espdma), errp);
+ qdev_init_nofail(espdma);
+
+ esp = DEVICE(object_resolve_path_component(OBJECT(espdma), "esp"));
+ sbd = SYS_BUS_DEVICE(esp);
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(espdma, 0));
+ qdev_connect_gpio_out(espdma, 0, qdev_get_gpio_in(esp, 0));
+ qdev_connect_gpio_out(espdma, 1, qdev_get_gpio_in(esp, 1));
+
+ sbd = SYS_BUS_DEVICE(espdma);
+ memory_region_add_subregion(&s->dmamem, 0x0,
+ sysbus_mmio_get_region(sbd, 0));
+
+ ledma = qdev_create(NULL, TYPE_SPARC32_LEDMA_DEVICE);
+ object_property_set_link(OBJECT(ledma), iommu, "iommu", errp);
+ object_property_add_child(OBJECT(s), "ledma", OBJECT(ledma), errp);
+ qdev_init_nofail(ledma);
+
+ lance = DEVICE(object_resolve_path_component(OBJECT(ledma), "lance"));
+ sbd = SYS_BUS_DEVICE(lance);
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(ledma, 0));
+ qdev_connect_gpio_out(ledma, 0, qdev_get_gpio_in(lance, 0));
+
+ sbd = SYS_BUS_DEVICE(ledma);
+ memory_region_add_subregion(&s->dmamem, 0x10,
+ sysbus_mmio_get_region(sbd, 0));
+
+ /* Add ledma alias to handle SunOS 5.7 - Solaris 9 invalid access bug */
+ memory_region_init_alias(&s->ledma_alias, OBJECT(dev), "ledma-alias",
+ sysbus_mmio_get_region(sbd, 0), 0x4, 0x4);
+ memory_region_add_subregion(&s->dmamem, 0x20, &s->ledma_alias);
+}
+
+static void sparc32_dma_init(Object *obj)
+{
+ SPARC32DMAState *s = SPARC32_DMA(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ memory_region_init(&s->dmamem, OBJECT(s), "dma", DMA_SIZE + DMA_ETH_SIZE);
+ sysbus_init_mmio(sbd, &s->dmamem);
+}
+
static void sparc32_dma_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->reset = dma_reset;
- dc->vmsd = &vmstate_dma;
- dc->props = sparc32_dma_properties;
dc->realize = sparc32_dma_realize;
- /* Reason: pointer property "iommu_opaque" */
- dc->user_creatable = false;
}
static const TypeInfo sparc32_dma_info = {
.name = TYPE_SPARC32_DMA,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(DMAState),
+ .instance_size = sizeof(SPARC32DMAState),
.instance_init = sparc32_dma_init,
.class_init = sparc32_dma_class_init,
};
+
static void sparc32_dma_register_types(void)
{
+ type_register_static(&sparc32_dma_device_info);
+ type_register_static(&sparc32_espdma_device_info);
+ type_register_static(&sparc32_ledma_device_info);
type_register_static(&sparc32_dma_info);
}
diff --git a/hw/dma/sun4m_iommu.c b/hw/dma/sun4m_iommu.c
index 335ef63cbc..30a05e8823 100644
--- a/hw/dma/sun4m_iommu.c
+++ b/hw/dma/sun4m_iommu.c
@@ -36,7 +36,6 @@
* http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf
*/
-#define IOMMU_NREGS (4*4096/4)
#define IOMMU_CTRL (0x0000 >> 2)
#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
@@ -128,19 +127,6 @@
#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT)
#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1)
-#define TYPE_SUN4M_IOMMU "iommu"
-#define SUN4M_IOMMU(obj) OBJECT_CHECK(IOMMUState, (obj), TYPE_SUN4M_IOMMU)
-
-typedef struct IOMMUState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint32_t regs[IOMMU_NREGS];
- hwaddr iostart;
- qemu_irq irq;
- uint32_t version;
-} IOMMUState;
-
static uint64_t iommu_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -292,37 +278,47 @@ static void iommu_bad_addr(IOMMUState *s, hwaddr addr,
qemu_irq_raise(s->irq);
}
-void sparc_iommu_memory_rw(void *opaque, hwaddr addr,
- uint8_t *buf, int len, int is_write)
+/* Called from RCU critical section */
+static IOMMUTLBEntry sun4m_translate_iommu(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flags)
{
- int l;
- uint32_t flags;
- hwaddr page, phys_addr;
-
- while (len > 0) {
- page = addr & IOMMU_PAGE_MASK;
- l = (page + IOMMU_PAGE_SIZE) - addr;
- if (l > len)
- l = len;
- flags = iommu_page_get_flags(opaque, page);
- if (!(flags & IOPTE_VALID)) {
- iommu_bad_addr(opaque, page, is_write);
- return;
- }
- phys_addr = iommu_translate_pa(addr, flags);
- if (is_write) {
- if (!(flags & IOPTE_WRITE)) {
- iommu_bad_addr(opaque, page, is_write);
- return;
- }
- cpu_physical_memory_write(phys_addr, buf, l);
- } else {
- cpu_physical_memory_read(phys_addr, buf, l);
- }
- len -= l;
- buf += l;
- addr += l;
+ IOMMUState *is = container_of(iommu, IOMMUState, iommu);
+ hwaddr page, pa;
+ int is_write = (flags & IOMMU_WO) ? 1 : 0;
+ uint32_t pte;
+ IOMMUTLBEntry ret = {
+ .target_as = &address_space_memory,
+ .iova = 0,
+ .translated_addr = 0,
+ .addr_mask = ~(hwaddr)0,
+ .perm = IOMMU_NONE,
+ };
+
+ page = addr & IOMMU_PAGE_MASK;
+ pte = iommu_page_get_flags(is, page);
+ if (!(pte & IOPTE_VALID)) {
+ iommu_bad_addr(is, page, is_write);
+ return ret;
}
+
+ pa = iommu_translate_pa(addr, pte);
+ if (is_write && !(pte & IOPTE_WRITE)) {
+ iommu_bad_addr(is, page, is_write);
+ return ret;
+ }
+
+ if (pte & IOPTE_WRITE) {
+ ret.perm = IOMMU_RW;
+ } else {
+ ret.perm = IOMMU_RO;
+ }
+
+ ret.iova = page;
+ ret.translated_addr = pa;
+ ret.addr_mask = ~IOMMU_PAGE_MASK;
+
+ return ret;
}
static const VMStateDescription vmstate_iommu = {
@@ -354,6 +350,11 @@ static void iommu_init(Object *obj)
IOMMUState *s = SUN4M_IOMMU(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+ memory_region_init_iommu(&s->iommu, sizeof(s->iommu),
+ TYPE_SUN4M_IOMMU_MEMORY_REGION, OBJECT(dev),
+ "iommu-sun4m", UINT64_MAX);
+ address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu), "iommu-as");
+
sysbus_init_irq(dev, &s->irq);
memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu",
@@ -383,9 +384,23 @@ static const TypeInfo iommu_info = {
.class_init = iommu_class_init,
};
+static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = sun4m_translate_iommu;
+}
+
+static const TypeInfo sun4m_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_SUN4M_IOMMU_MEMORY_REGION,
+ .class_init = sun4m_iommu_memory_region_class_init,
+};
+
static void iommu_register_types(void)
{
type_register_static(&iommu_info);
+ type_register_static(&sun4m_iommu_memory_region_info);
}
type_init(iommu_register_types)
diff --git a/hw/dma/trace-events b/hw/dma/trace-events
index 428469a140..6b367f053b 100644
--- a/hw/dma/trace-events
+++ b/hw/dma/trace-events
@@ -7,12 +7,12 @@ rc4030_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x"
rc4030_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x"
# hw/dma/sparc32_dma.c
-ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64
-ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64
+ledma_memory_read(uint64_t addr, int len) "DMA read addr 0x%"PRIx64 " len %d"
+ledma_memory_write(uint64_t addr, int len) "DMA write addr 0x%"PRIx64 " len %d"
sparc32_dma_set_irq_raise(void) "Raise IRQ"
sparc32_dma_set_irq_lower(void) "Lower IRQ"
-espdma_memory_read(uint32_t addr) "DMA read addr 0x%08x"
-espdma_memory_write(uint32_t addr) "DMA write addr 0x%08x"
+espdma_memory_read(uint32_t addr, int len) "DMA read addr 0x%08x len %d"
+espdma_memory_write(uint32_t addr, int len) "DMA write addr 0x%08x len %d"
sparc32_dma_mem_readl(uint64_t addr, uint32_t ret) "read dmareg 0x%"PRIx64": 0x%08x"
sparc32_dma_mem_writel(uint64_t addr, uint32_t old, uint32_t val) "write dmareg 0x%"PRIx64": 0x%08x -> 0x%08x"
sparc32_dma_enable_raise(void) "Raise DMA enable"
diff --git a/hw/ide/Makefile.objs b/hw/ide/Makefile.objs
index 729e9bd0db..f0edca3300 100644
--- a/hw/ide/Makefile.objs
+++ b/hw/ide/Makefile.objs
@@ -10,3 +10,4 @@ common-obj-$(CONFIG_IDE_VIA) += via.o
common-obj-$(CONFIG_MICRODRIVE) += microdrive.o
common-obj-$(CONFIG_AHCI) += ahci.o
common-obj-$(CONFIG_AHCI) += ich.o
+common-obj-$(CONFIG_ALLWINNER_A10) += ahci-allwinner.o
diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c
new file mode 100644
index 0000000000..c3f1604936
--- /dev/null
+++ b/hw/ide/ahci-allwinner.c
@@ -0,0 +1,127 @@
+/*
+ * QEMU Allwinner AHCI Emulation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "qemu/error-report.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/dma.h"
+#include "hw/ide/internal.h"
+#include "hw/ide/ahci_internal.h"
+
+#include "trace.h"
+
+#define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTSR ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTDECR ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR0 ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR1 ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_OOBR ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS0R ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS1R ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS2R ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TIMER1MS ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM1R ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM2R ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PPARAMR ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TESTR ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_VERSIONR ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_IDR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_RWCR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+
+static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+ AHCIState *s = &(SYSBUS_AHCI(a)->ahci);
+ uint64_t val = a->regs[addr / 4];
+
+ switch (addr / 4) {
+ case ALLWINNER_AHCI_PHYCS0R:
+ val |= 0x2 << 28;
+ break;
+ case ALLWINNER_AHCI_PHYCS2R:
+ val &= ~(0x1 << 24);
+ break;
+ }
+ trace_allwinner_ahci_mem_read(s, a, addr, val, size);
+ return val;
+}
+
+static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+ AHCIState *s = &(SYSBUS_AHCI(a)->ahci);
+
+ trace_allwinner_ahci_mem_write(s, a, addr, val, size);
+ a->regs[addr / 4] = val;
+}
+
+static const MemoryRegionOps allwinner_ahci_mem_ops = {
+ .read = allwinner_ahci_mem_read,
+ .write = allwinner_ahci_mem_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void allwinner_ahci_init(Object *obj)
+{
+ SysbusAHCIState *s = SYSBUS_AHCI(obj);
+ AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
+
+ memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
+ "allwinner-ahci", ALLWINNER_AHCI_MMIO_SIZE);
+ memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
+ &a->mmio);
+}
+
+static const VMStateDescription vmstate_allwinner_ahci = {
+ .name = "allwinner-ahci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
+ ALLWINNER_AHCI_MMIO_SIZE / 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_allwinner_ahci;
+}
+
+static const TypeInfo allwinner_ahci_info = {
+ .name = TYPE_ALLWINNER_AHCI,
+ .parent = TYPE_SYSBUS_AHCI,
+ .instance_size = sizeof(AllwinnerAHCIState),
+ .instance_init = allwinner_ahci_init,
+ .class_init = allwinner_ahci_class_init,
+};
+
+static void sysbus_ahci_register_types(void)
+{
+ type_register_static(&allwinner_ahci_info);
+}
+
+type_init(sysbus_ahci_register_types)
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 32d1296a64..373311f91a 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1737,104 +1737,9 @@ static const TypeInfo sysbus_ahci_info = {
.class_init = sysbus_ahci_class_init,
};
-#define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_BISTSR ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_BISTDECR ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_DIAGNR0 ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_DIAGNR1 ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_OOBR ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_PHYCS0R ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_PHYCS1R ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_PHYCS2R ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_TIMER1MS ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_GPARAM1R ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_GPARAM2R ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_PPARAMR ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_TESTR ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_VERSIONR ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_IDR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
-#define ALLWINNER_AHCI_RWCR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
-
-static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- AllwinnerAHCIState *a = opaque;
- AHCIState *s = &(SYSBUS_AHCI(a)->ahci);
- uint64_t val = a->regs[addr/4];
-
- switch (addr / 4) {
- case ALLWINNER_AHCI_PHYCS0R:
- val |= 0x2 << 28;
- break;
- case ALLWINNER_AHCI_PHYCS2R:
- val &= ~(0x1 << 24);
- break;
- }
- trace_allwinner_ahci_mem_read(s, a, addr, val, size);
- return val;
-}
-
-static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
- AllwinnerAHCIState *a = opaque;
- AHCIState *s = &(SYSBUS_AHCI(a)->ahci);
-
- trace_allwinner_ahci_mem_write(s, a, addr, val, size);
- a->regs[addr/4] = val;
-}
-
-static const MemoryRegionOps allwinner_ahci_mem_ops = {
- .read = allwinner_ahci_mem_read,
- .write = allwinner_ahci_mem_write,
- .valid.min_access_size = 4,
- .valid.max_access_size = 4,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void allwinner_ahci_init(Object *obj)
-{
- SysbusAHCIState *s = SYSBUS_AHCI(obj);
- AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
-
- memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
- "allwinner-ahci", ALLWINNER_AHCI_MMIO_SIZE);
- memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
- &a->mmio);
-}
-
-static const VMStateDescription vmstate_allwinner_ahci = {
- .name = "allwinner-ahci",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
- ALLWINNER_AHCI_MMIO_SIZE/4),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->vmsd = &vmstate_allwinner_ahci;
-}
-
-static const TypeInfo allwinner_ahci_info = {
- .name = TYPE_ALLWINNER_AHCI,
- .parent = TYPE_SYSBUS_AHCI,
- .instance_size = sizeof(AllwinnerAHCIState),
- .instance_init = allwinner_ahci_init,
- .class_init = allwinner_ahci_class_init,
-};
-
static void sysbus_ahci_register_types(void)
{
type_register_static(&sysbus_ahci_info);
- type_register_static(&allwinner_ahci_info);
}
type_init(sysbus_ahci_register_types)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index a04766aee7..471d0c928b 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -208,7 +208,9 @@ static void ide_identify(IDEState *s)
if (dev && dev->conf.discard_granularity) {
put_le16(p + 169, 1); /* TRIM support */
}
- put_le16(p + 217, dev->rotation_rate); /* Nominal media rotation rate */
+ if (dev) {
+ put_le16(p + 217, dev->rotation_rate); /* Nominal media rotation rate */
+ }
ide_identify_size(s);
s->identify_set = 1;
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 92b0c68274..23929fd1e6 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -41,19 +41,10 @@
#include "qemu/timer.h"
#include "qemu/sockets.h"
#include "hw/sparc/sun4m.h"
-#include "pcnet.h"
+#include "hw/net/lance.h"
#include "trace.h"
#include "sysemu/sysemu.h"
-#define TYPE_LANCE "lance"
-#define SYSBUS_PCNET(obj) \
- OBJECT_CHECK(SysBusPCNetState, (obj), TYPE_LANCE)
-
-typedef struct {
- SysBusDevice parent_obj;
-
- PCNetState state;
-} SysBusPCNetState;
static void parent_lance_reset(void *opaque, int irq, int level)
{
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
index 4090793cf0..edf305b1fd 100644
--- a/hw/pci-host/gpex.c
+++ b/hw/pci-host/gpex.c
@@ -57,9 +57,14 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin)
{
PCIINTxRoute route;
GPEXHost *s = opaque;
+ int gsi = s->irq_num[pin];
- route.mode = PCI_INTX_ENABLED;
- route.irq = s->irq_num[pin];
+ route.irq = gsi;
+ if (gsi < 0) {
+ route.mode = PCI_INTX_DISABLED;
+ } else {
+ route.mode = PCI_INTX_ENABLED;
+ }
return route;
}
@@ -81,6 +86,7 @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(sbd, &s->io_ioport);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
sysbus_init_irq(sbd, &s->irq[i]);
+ s->irq_num[i] = -1;
}
pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq,
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 22c2d91e39..ee586e7d6c 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -592,19 +592,6 @@ const VMStateDescription vmstate_esp = {
}
};
-#define TYPE_ESP "esp"
-#define ESP_STATE(obj) OBJECT_CHECK(SysBusESPState, (obj), TYPE_ESP)
-
-typedef struct {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- MemoryRegion iomem;
- uint32_t it_shift;
- ESPState esp;
-} SysBusESPState;
-
static void sysbus_esp_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index 68b23784c5..24c2b8a555 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -296,7 +296,7 @@ static void *iommu_init(hwaddr addr, uint32_t version, qemu_irq irq)
DeviceState *dev;
SysBusDevice *s;
- dev = qdev_create(NULL, "iommu");
+ dev = qdev_create(NULL, TYPE_SUN4M_IOMMU);
qdev_prop_set_uint32(dev, "version", version);
qdev_init_nofail(dev);
s = SYS_BUS_DEVICE(dev);
@@ -306,42 +306,36 @@ static void *iommu_init(hwaddr addr, uint32_t version, qemu_irq irq)
return s;
}
-static void *sparc32_dma_init(hwaddr daddr, qemu_irq parent_irq,
- void *iommu, qemu_irq *dev_irq, int is_ledma)
+static void *sparc32_dma_init(hwaddr dma_base,
+ hwaddr esp_base, qemu_irq espdma_irq,
+ hwaddr le_base, qemu_irq ledma_irq)
{
- DeviceState *dev;
- SysBusDevice *s;
+ DeviceState *dma;
+ ESPDMADeviceState *espdma;
+ LEDMADeviceState *ledma;
+ SysBusESPState *esp;
+ SysBusPCNetState *lance;
- dev = qdev_create(NULL, "sparc32_dma");
- qdev_prop_set_ptr(dev, "iommu_opaque", iommu);
- qdev_prop_set_uint32(dev, "is_ledma", is_ledma);
- qdev_init_nofail(dev);
- s = SYS_BUS_DEVICE(dev);
- sysbus_connect_irq(s, 0, parent_irq);
- *dev_irq = qdev_get_gpio_in(dev, 0);
- sysbus_mmio_map(s, 0, daddr);
+ dma = qdev_create(NULL, TYPE_SPARC32_DMA);
+ qdev_init_nofail(dma);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, dma_base);
- return s;
-}
+ espdma = SPARC32_ESPDMA_DEVICE(object_resolve_path_component(
+ OBJECT(dma), "espdma"));
+ sysbus_connect_irq(SYS_BUS_DEVICE(espdma), 0, espdma_irq);
-static void lance_init(NICInfo *nd, hwaddr leaddr,
- void *dma_opaque, qemu_irq irq)
-{
- DeviceState *dev;
- SysBusDevice *s;
- qemu_irq reset;
+ esp = ESP_STATE(object_resolve_path_component(OBJECT(espdma), "esp"));
+ sysbus_mmio_map(SYS_BUS_DEVICE(esp), 0, esp_base);
- qemu_check_nic_model(&nd_table[0], "lance");
+ ledma = SPARC32_LEDMA_DEVICE(object_resolve_path_component(
+ OBJECT(dma), "ledma"));
+ sysbus_connect_irq(SYS_BUS_DEVICE(ledma), 0, ledma_irq);
- dev = qdev_create(NULL, "lance");
- qdev_set_nic_properties(dev, nd);
- qdev_prop_set_ptr(dev, "dma", dma_opaque);
- qdev_init_nofail(dev);
- s = SYS_BUS_DEVICE(dev);
- sysbus_mmio_map(s, 0, leaddr);
- sysbus_connect_irq(s, 0, irq);
- reset = qdev_get_gpio_in(dev, 0);
- qdev_connect_gpio_out(dma_opaque, 0, reset);
+ lance = SYSBUS_PCNET(object_resolve_path_component(
+ OBJECT(ledma), "lance"));
+ sysbus_mmio_map(SYS_BUS_DEVICE(lance), 0, le_base);
+
+ return dma;
}
static DeviceState *slavio_intctl_init(hwaddr addr,
@@ -820,10 +814,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
{
DeviceState *slavio_intctl;
unsigned int i;
- void *iommu, *espdma, *ledma, *nvram;
- qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS],
- espdma_irq, ledma_irq;
- qemu_irq esp_reset, dma_enable;
+ void *nvram;
+ qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS];
qemu_irq fdc_tc;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
@@ -867,8 +859,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
afx_init(hwdef->afx_base);
}
- iommu = iommu_init(hwdef->iommu_base, hwdef->iommu_version,
- slavio_irq[30]);
+ iommu_init(hwdef->iommu_base, hwdef->iommu_version, slavio_irq[30]);
if (hwdef->iommu_pad_base) {
/* On the real hardware (SS-5, LX) the MMU is not padded, but aliased.
@@ -878,11 +869,9 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
empty_slot_init(hwdef->iommu_pad_base,hwdef->iommu_pad_len);
}
- espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[18],
- iommu, &espdma_irq, 0);
-
- ledma = sparc32_dma_init(hwdef->dma_base + 16ULL,
- slavio_irq[16], iommu, &ledma_irq, 1);
+ sparc32_dma_init(hwdef->dma_base,
+ hwdef->esp_base, slavio_irq[18],
+ hwdef->le_base, slavio_irq[16]);
if (graphic_depth != 8 && graphic_depth != 24) {
error_report("Unsupported depth: %d", graphic_depth);
@@ -935,8 +924,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
empty_slot_init(hwdef->sx_base, 0x2000);
}
- lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq);
-
nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 1968, 8);
slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus);
@@ -965,13 +952,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
slavio_irq[30], fdc_tc);
- esp_init(hwdef->esp_base, 2,
- espdma_memory_read, espdma_memory_write,
- espdma, espdma_irq, &esp_reset, &dma_enable);
-
- qdev_connect_gpio_out(espdma, 0, esp_reset);
- qdev_connect_gpio_out(espdma, 1, dma_enable);
-
if (hwdef->cs_base) {
sysbus_create_simple("SUNW,CS4231", hwdef->cs_base,
slavio_irq[5]);
diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c
index 5a8e308e69..d60daba882 100644
--- a/hw/ssi/mss-spi.c
+++ b/hw/ssi/mss-spi.c
@@ -76,9 +76,10 @@
#define C_BIGFIFO (1 << 29)
#define C_RESET (1 << 31)
-#define FRAMESZ_MASK 0x1F
+#define FRAMESZ_MASK 0x3F
#define FMCOUNT_MASK 0x00FFFF00
#define FMCOUNT_SHIFT 8
+#define FRAMESZ_MAX 32
static void txfifo_reset(MSSSpiState *s)
{
@@ -104,10 +105,8 @@ static void set_fifodepth(MSSSpiState *s)
s->fifo_depth = 32;
} else if (size <= 16) {
s->fifo_depth = 16;
- } else if (size <= 32) {
- s->fifo_depth = 8;
} else {
- s->fifo_depth = 4;
+ s->fifo_depth = 8;
}
}
@@ -301,6 +300,17 @@ static void spi_write(void *opaque, hwaddr addr,
if (s->enabled) {
break;
}
+ /*
+ * [31:6] bits are reserved bits and for future use.
+ * [5:0] are for frame size. Only [5:0] bits are validated
+ * during write, [31:6] bits are untouched.
+ */
+ if ((value & FRAMESZ_MASK) > FRAMESZ_MAX) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Incorrect size %u provided."
+ "Maximum frame size is %u\n",
+ __func__, value & FRAMESZ_MASK, FRAMESZ_MAX);
+ break;
+ }
s->regs[R_SPI_DFSIZE] = value;
break;
diff --git a/include/block/nbd.h b/include/block/nbd.h
index a6df5ce8b5..92d1723d7c 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -57,18 +57,48 @@ struct NBDRequest {
};
typedef struct NBDRequest NBDRequest;
-struct NBDReply {
- uint64_t handle;
- uint32_t error;
-};
-typedef struct NBDReply NBDReply;
-
typedef struct NBDSimpleReply {
uint32_t magic; /* NBD_SIMPLE_REPLY_MAGIC */
uint32_t error;
uint64_t handle;
} QEMU_PACKED NBDSimpleReply;
+/* Header of all structured replies */
+typedef struct NBDStructuredReplyChunk {
+ uint32_t magic; /* NBD_STRUCTURED_REPLY_MAGIC */
+ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */
+ uint16_t type; /* NBD_REPLY_TYPE_* */
+ uint64_t handle; /* request handle */
+ uint32_t length; /* length of payload */
+} QEMU_PACKED NBDStructuredReplyChunk;
+
+typedef union NBDReply {
+ NBDSimpleReply simple;
+ NBDStructuredReplyChunk structured;
+ struct {
+ /* @magic and @handle fields have the same offset and size both in
+ * simple reply and structured reply chunk, so let them be accessible
+ * without ".simple." or ".structured." specification
+ */
+ uint32_t magic;
+ uint32_t _skip;
+ uint64_t handle;
+ } QEMU_PACKED;
+} NBDReply;
+
+/* Header of NBD_REPLY_TYPE_OFFSET_DATA, complete NBD_REPLY_TYPE_OFFSET_HOLE */
+typedef struct NBDStructuredRead {
+ NBDStructuredReplyChunk h;
+ uint64_t offset;
+} QEMU_PACKED NBDStructuredRead;
+
+/* Header of all NBD_REPLY_TYPE_ERROR* errors */
+typedef struct NBDStructuredError {
+ NBDStructuredReplyChunk h;
+ uint32_t error;
+ uint16_t message_length;
+} QEMU_PACKED NBDStructuredError;
+
/* Transmission (export) flags: sent from server to client during handshake,
but describe what will happen during transmission */
#define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */
@@ -79,6 +109,7 @@ typedef struct NBDSimpleReply {
rotational media */
#define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */
#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */
+#define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */
/* New-style handshake (global) flags, sent from server to client, and
control what will happen during handshake phase. */
@@ -125,6 +156,7 @@ typedef struct NBDSimpleReply {
/* Request flags, sent from client to server during transmission phase */
#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */
#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */
+#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */
/* Supported request types */
enum {
@@ -149,10 +181,49 @@ enum {
* aren't overflowing some other buffer. */
#define NBD_MAX_NAME_SIZE 256
+/* Two types of reply structures */
+#define NBD_SIMPLE_REPLY_MAGIC 0x67446698
+#define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef
+
+/* Structured reply flags */
+#define NBD_REPLY_FLAG_DONE (1 << 0) /* This reply-chunk is last */
+
+/* Structured reply types */
+#define NBD_REPLY_ERR(value) ((1 << 15) | (value))
+
+#define NBD_REPLY_TYPE_NONE 0
+#define NBD_REPLY_TYPE_OFFSET_DATA 1
+#define NBD_REPLY_TYPE_OFFSET_HOLE 2
+#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1)
+#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2)
+
+static inline bool nbd_reply_type_is_error(int type)
+{
+ return type & (1 << 15);
+}
+
+/* NBD errors are based on errno numbers, so there is a 1:1 mapping,
+ * but only a limited set of errno values is specified in the protocol.
+ * Everything else is squashed to EINVAL.
+ */
+#define NBD_SUCCESS 0
+#define NBD_EPERM 1
+#define NBD_EIO 5
+#define NBD_ENOMEM 12
+#define NBD_EINVAL 22
+#define NBD_ENOSPC 28
+#define NBD_EOVERFLOW 75
+#define NBD_ESHUTDOWN 108
+
/* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */
struct NBDExportInfo {
/* Set by client before nbd_receive_negotiate() */
bool request_sizes;
+
+ /* In-out fields, set by client before nbd_receive_negotiate() and
+ * updated by server results during nbd_receive_negotiate() */
+ bool structured_reply;
+
/* Set by server results during nbd_receive_negotiate() */
uint64_t size;
uint16_t flags;
@@ -172,6 +243,7 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp);
int nbd_client(int fd);
int nbd_disconnect(int fd);
+int nbd_errno_to_system_errno(int err);
typedef struct NBDExport NBDExport;
typedef struct NBDClient NBDClient;
@@ -202,4 +274,26 @@ void nbd_client_put(NBDClient *client);
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
Error **errp);
+
+/* nbd_read
+ * Reads @size bytes from @ioc. Returns 0 on success.
+ */
+static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size,
+ Error **errp)
+{
+ return qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0;
+}
+
+static inline bool nbd_reply_is_simple(NBDReply *reply)
+{
+ return reply->magic == NBD_SIMPLE_REPLY_MAGIC;
+}
+
+static inline bool nbd_reply_is_structured(NBDReply *reply)
+{
+ return reply->magic == NBD_STRUCTURED_REPLY_MAGIC;
+}
+
+const char *nbd_reply_type_lookup(uint16_t type);
+
#endif
diff --git a/include/hw/net/lance.h b/include/hw/net/lance.h
new file mode 100644
index 0000000000..ffdd35c4d7
--- /dev/null
+++ b/include/hw/net/lance.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU Lance (Am7990) device emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ * Copyright (c) 2017 Mark Cave-Ayland
+ *
+ * This represents the Sparc32 lance (Am7990) ethernet device which is an
+ * earlier register-compatible member of the AMD PC-Net II (Am79C970A) family.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef LANCE_H
+#define LANCE_H
+
+#include "net/net.h"
+#include "hw/net/pcnet.h"
+
+#define TYPE_LANCE "lance"
+#define SYSBUS_PCNET(obj) \
+ OBJECT_CHECK(SysBusPCNetState, (obj), TYPE_LANCE)
+
+typedef struct {
+ SysBusDevice parent_obj;
+
+ PCNetState state;
+} SysBusPCNetState;
+
+#endif
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index d2c48869e1..3b160f858c 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -2,6 +2,7 @@
#define QEMU_HW_ESP_H
#include "hw/scsi/scsi.h"
+#include "hw/sysbus.h"
/* esp.c */
#define ESP_MAX_DEVS 7
@@ -52,6 +53,19 @@ struct ESPState {
void (*dma_cb)(ESPState *s);
};
+#define TYPE_ESP "esp"
+#define ESP_STATE(obj) OBJECT_CHECK(SysBusESPState, (obj), TYPE_ESP)
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ uint32_t it_shift;
+ ESPState esp;
+} SysBusESPState;
+
#define ESP_TCLO 0x0
#define ESP_TCMID 0x1
#define ESP_FIFO 0x2
diff --git a/include/hw/sparc/sparc32_dma.h b/include/hw/sparc/sparc32_dma.h
index 9497b13d34..ab42c5421b 100644
--- a/include/hw/sparc/sparc32_dma.h
+++ b/include/hw/sparc/sparc32_dma.h
@@ -1,6 +1,61 @@
#ifndef SPARC32_DMA_H
#define SPARC32_DMA_H
+#include "hw/sysbus.h"
+#include "hw/scsi/esp.h"
+#include "hw/net/lance.h"
+
+#define DMA_REGS 4
+
+#define TYPE_SPARC32_DMA_DEVICE "sparc32-dma-device"
+#define SPARC32_DMA_DEVICE(obj) OBJECT_CHECK(DMADeviceState, (obj), \
+ TYPE_SPARC32_DMA_DEVICE)
+
+typedef struct DMADeviceState DMADeviceState;
+
+struct DMADeviceState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint32_t dmaregs[DMA_REGS];
+ qemu_irq irq;
+ void *iommu;
+ qemu_irq gpio[2];
+};
+
+#define TYPE_SPARC32_ESPDMA_DEVICE "sparc32-espdma"
+#define SPARC32_ESPDMA_DEVICE(obj) OBJECT_CHECK(ESPDMADeviceState, (obj), \
+ TYPE_SPARC32_ESPDMA_DEVICE)
+
+typedef struct ESPDMADeviceState {
+ DMADeviceState parent_obj;
+
+ SysBusESPState *esp;
+} ESPDMADeviceState;
+
+#define TYPE_SPARC32_LEDMA_DEVICE "sparc32-ledma"
+#define SPARC32_LEDMA_DEVICE(obj) OBJECT_CHECK(LEDMADeviceState, (obj), \
+ TYPE_SPARC32_LEDMA_DEVICE)
+
+typedef struct LEDMADeviceState {
+ DMADeviceState parent_obj;
+
+ SysBusPCNetState *lance;
+} LEDMADeviceState;
+
+#define TYPE_SPARC32_DMA "sparc32-dma"
+#define SPARC32_DMA(obj) OBJECT_CHECK(SPARC32DMAState, (obj), \
+ TYPE_SPARC32_DMA)
+
+typedef struct SPARC32DMAState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion dmamem;
+ MemoryRegion ledma_alias;
+ ESPDMADeviceState *espdma;
+ LEDMADeviceState *ledma;
+} SPARC32DMAState;
+
/* sparc32_dma.c */
void ledma_memory_read(void *opaque, hwaddr addr,
uint8_t *buf, int len, int do_bswap);
diff --git a/include/hw/sparc/sun4m.h b/include/hw/sparc/sun4m.h
index 580d87b252..c557b0dd53 100644
--- a/include/hw/sparc/sun4m.h
+++ b/include/hw/sparc/sun4m.h
@@ -4,25 +4,30 @@
#include "qemu-common.h"
#include "exec/hwaddr.h"
#include "qapi/qmp/types.h"
+#include "hw/sysbus.h"
/* Devices used by sparc32 system. */
/* iommu.c */
-void sparc_iommu_memory_rw(void *opaque, hwaddr addr,
- uint8_t *buf, int len, int is_write);
-static inline void sparc_iommu_memory_read(void *opaque,
- hwaddr addr,
- uint8_t *buf, int len)
-{
- sparc_iommu_memory_rw(opaque, addr, buf, len, 0);
-}
-
-static inline void sparc_iommu_memory_write(void *opaque,
- hwaddr addr,
- uint8_t *buf, int len)
-{
- sparc_iommu_memory_rw(opaque, addr, buf, len, 1);
-}
+#define TYPE_SUN4M_IOMMU "sun4m-iommu"
+#define SUN4M_IOMMU(obj) OBJECT_CHECK(IOMMUState, (obj), TYPE_SUN4M_IOMMU)
+
+#define TYPE_SUN4M_IOMMU_MEMORY_REGION "sun4m-iommu-memory-region"
+
+#define IOMMU_NREGS (4 * 4096 / 4)
+
+typedef struct IOMMUState {
+ SysBusDevice parent_obj;
+
+ AddressSpace iommu_as;
+ IOMMUMemoryRegion iommu;
+
+ MemoryRegion iomem;
+ uint32_t regs[IOMMU_NREGS];
+ hwaddr iostart;
+ qemu_irq irq;
+ uint32_t version;
+} IOMMUState;
/* sparc32_dma.c */
#include "hw/sparc/sparc32_dma.h"
diff --git a/nbd/client.c b/nbd/client.c
index cd5a2c80ac..3d680e63e1 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -22,38 +22,6 @@
#include "trace.h"
#include "nbd-internal.h"
-static int nbd_errno_to_system_errno(int err)
-{
- int ret;
- switch (err) {
- case NBD_SUCCESS:
- ret = 0;
- break;
- case NBD_EPERM:
- ret = EPERM;
- break;
- case NBD_EIO:
- ret = EIO;
- break;
- case NBD_ENOMEM:
- ret = ENOMEM;
- break;
- case NBD_ENOSPC:
- ret = ENOSPC;
- break;
- case NBD_ESHUTDOWN:
- ret = ESHUTDOWN;
- break;
- default:
- trace_nbd_unknown_error(err);
- /* fallthrough */
- case NBD_EINVAL:
- ret = EINVAL;
- break;
- }
- return ret;
-}
-
/* Definitions for opaque data types */
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
@@ -540,35 +508,61 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
}
}
-static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
- QCryptoTLSCreds *tlscreds,
- const char *hostname, Error **errp)
+/* nbd_request_simple_option: Send an option request, and parse the reply
+ * return 1 for successful negotiation,
+ * 0 if operation is unsupported,
+ * -1 with errp set for any other error
+ */
+static int nbd_request_simple_option(QIOChannel *ioc, int opt, Error **errp)
{
nbd_opt_reply reply;
- QIOChannelTLS *tioc;
- struct NBDTLSHandshakeData data = { 0 };
+ int error;
- trace_nbd_receive_starttls_request();
- if (nbd_send_option_request(ioc, NBD_OPT_STARTTLS, 0, NULL, errp) < 0) {
- return NULL;
+ if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) {
+ return -1;
}
- trace_nbd_receive_starttls_reply();
- if (nbd_receive_option_reply(ioc, NBD_OPT_STARTTLS, &reply, errp) < 0) {
- return NULL;
+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) {
+ return -1;
+ }
+ error = nbd_handle_reply_err(ioc, &reply, errp);
+ if (error <= 0) {
+ return error;
}
if (reply.type != NBD_REP_ACK) {
- error_setg(errp, "Server rejected request to start TLS %" PRIx32,
- reply.type);
+ error_setg(errp, "Server answered option %d (%s) with unexpected "
+ "reply %" PRIx32 " (%s)", opt, nbd_opt_lookup(opt),
+ reply.type, nbd_rep_lookup(reply.type));
nbd_send_opt_abort(ioc);
- return NULL;
+ return -1;
}
if (reply.length != 0) {
- error_setg(errp, "Start TLS response was not zero %" PRIu32,
+ error_setg(errp, "Option %d ('%s') response length is %" PRIu32
+ " (it should be zero)", opt, nbd_opt_lookup(opt),
reply.length);
nbd_send_opt_abort(ioc);
+ return -1;
+ }
+
+ return 1;
+}
+
+static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *hostname, Error **errp)
+{
+ int ret;
+ QIOChannelTLS *tioc;
+ struct NBDTLSHandshakeData data = { 0 };
+
+ ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, errp);
+ if (ret <= 0) {
+ if (ret == 0) {
+ error_setg(errp, "Server don't support STARTTLS option");
+ nbd_send_opt_abort(ioc);
+ }
return NULL;
}
@@ -608,9 +602,11 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
uint64_t magic;
int rc;
bool zeroes = true;
+ bool structured_reply = info->structured_reply;
trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>");
+ info->structured_reply = false;
rc = -EINVAL;
if (outioc) {
@@ -691,6 +687,16 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
if (fixedNewStyle) {
int result;
+ if (structured_reply) {
+ result = nbd_request_simple_option(ioc,
+ NBD_OPT_STRUCTURED_REPLY,
+ errp);
+ if (result < 0) {
+ goto fail;
+ }
+ info->structured_reply = result == 1;
+ }
+
/* Try NBD_OPT_GO first - if it works, we are done (it
* also gives us a good message if the server requires
* TLS). If it is not available, fall back to
@@ -914,6 +920,57 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request)
return nbd_write(ioc, buf, sizeof(buf), NULL);
}
+/* nbd_receive_simple_reply
+ * Read simple reply except magic field (which should be already read).
+ * Payload is not read (payload is possible for CMD_READ, but here we even
+ * don't know whether it take place or not).
+ */
+static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply,
+ Error **errp)
+{
+ int ret;
+
+ assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC);
+
+ ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic),
+ sizeof(*reply) - sizeof(reply->magic), errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ be32_to_cpus(&reply->error);
+ be64_to_cpus(&reply->handle);
+
+ return 0;
+}
+
+/* nbd_receive_structured_reply_chunk
+ * Read structured reply chunk except magic field (which should be already
+ * read).
+ * Payload is not read.
+ */
+static int nbd_receive_structured_reply_chunk(QIOChannel *ioc,
+ NBDStructuredReplyChunk *chunk,
+ Error **errp)
+{
+ int ret;
+
+ assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC);
+
+ ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic),
+ sizeof(*chunk) - sizeof(chunk->magic), errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ be16_to_cpus(&chunk->flags);
+ be16_to_cpus(&chunk->type);
+ be64_to_cpus(&chunk->handle);
+ be32_to_cpus(&chunk->length);
+
+ return 0;
+}
+
/* nbd_receive_reply
* Returns 1 on success
* 0 on eof, when no data was read (errp is not set)
@@ -921,37 +978,47 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request)
*/
int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
{
- uint8_t buf[NBD_REPLY_SIZE];
- uint32_t magic;
int ret;
- ret = nbd_read_eof(ioc, buf, sizeof(buf), errp);
+ ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp);
if (ret <= 0) {
return ret;
}
- /* Reply
- [ 0 .. 3] magic (NBD_SIMPLE_REPLY_MAGIC)
- [ 4 .. 7] error (0 == no error)
- [ 7 .. 15] handle
- */
-
- magic = ldl_be_p(buf);
- reply->error = ldl_be_p(buf + 4);
- reply->handle = ldq_be_p(buf + 8);
+ be32_to_cpus(&reply->magic);
- reply->error = nbd_errno_to_system_errno(reply->error);
+ switch (reply->magic) {
+ case NBD_SIMPLE_REPLY_MAGIC:
+ ret = nbd_receive_simple_reply(ioc, &reply->simple, errp);
+ if (ret < 0) {
+ break;
+ }
- if (reply->error == ESHUTDOWN) {
- /* This works even on mingw which lacks a native ESHUTDOWN */
- error_setg(errp, "server shutting down");
+ trace_nbd_receive_simple_reply(reply->simple.error,
+ nbd_err_lookup(reply->simple.error),
+ reply->handle);
+ if (reply->simple.error == NBD_ESHUTDOWN) {
+ /* This works even on mingw which lacks a native ESHUTDOWN */
+ error_setg(errp, "server shutting down");
+ return -EINVAL;
+ }
+ break;
+ case NBD_STRUCTURED_REPLY_MAGIC:
+ ret = nbd_receive_structured_reply_chunk(ioc, &reply->structured, errp);
+ if (ret < 0) {
+ break;
+ }
+ trace_nbd_receive_structured_reply_chunk(reply->structured.flags,
+ reply->structured.type,
+ reply->structured.handle,
+ reply->structured.length);
+ break;
+ default:
+ error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic);
return -EINVAL;
}
- trace_nbd_receive_reply(magic, reply->error, reply->handle);
-
- if (magic != NBD_SIMPLE_REPLY_MAGIC) {
- error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic);
- return -EINVAL;
+ if (ret < 0) {
+ return ret;
}
return 1;
diff --git a/nbd/common.c b/nbd/common.c
index 59a5316be9..6047d71748 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -18,6 +18,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "trace.h"
#include "nbd-internal.h"
/* Discard length bytes from channel. Return -errno on failure and 0 on
@@ -148,3 +149,86 @@ const char *nbd_cmd_lookup(uint16_t cmd)
return "<unknown>";
}
}
+
+
+const char *nbd_reply_type_lookup(uint16_t type)
+{
+ switch (type) {
+ case NBD_REPLY_TYPE_NONE:
+ return "none";
+ case NBD_REPLY_TYPE_OFFSET_DATA:
+ return "data";
+ case NBD_REPLY_TYPE_OFFSET_HOLE:
+ return "hole";
+ case NBD_REPLY_TYPE_ERROR:
+ return "generic error";
+ case NBD_REPLY_TYPE_ERROR_OFFSET:
+ return "error at offset";
+ default:
+ if (type & (1 << 15)) {
+ return "<unknown error>";
+ }
+ return "<unknown>";
+ }
+}
+
+
+const char *nbd_err_lookup(int err)
+{
+ switch (err) {
+ case NBD_SUCCESS:
+ return "success";
+ case NBD_EPERM:
+ return "EPERM";
+ case NBD_EIO:
+ return "EIO";
+ case NBD_ENOMEM:
+ return "ENOMEM";
+ case NBD_EINVAL:
+ return "EINVAL";
+ case NBD_ENOSPC:
+ return "ENOSPC";
+ case NBD_EOVERFLOW:
+ return "EOVERFLOW";
+ case NBD_ESHUTDOWN:
+ return "ESHUTDOWN";
+ default:
+ return "<unknown>";
+ }
+}
+
+
+int nbd_errno_to_system_errno(int err)
+{
+ int ret;
+ switch (err) {
+ case NBD_SUCCESS:
+ ret = 0;
+ break;
+ case NBD_EPERM:
+ ret = EPERM;
+ break;
+ case NBD_EIO:
+ ret = EIO;
+ break;
+ case NBD_ENOMEM:
+ ret = ENOMEM;
+ break;
+ case NBD_ENOSPC:
+ ret = ENOSPC;
+ break;
+ case NBD_EOVERFLOW:
+ ret = EOVERFLOW;
+ break;
+ case NBD_ESHUTDOWN:
+ ret = ESHUTDOWN;
+ break;
+ default:
+ trace_nbd_unknown_error(err);
+ /* fallthrough */
+ case NBD_EINVAL:
+ ret = EINVAL;
+ break;
+ }
+ return ret;
+}
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 11a130d050..eeff78d3c9 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -47,7 +47,6 @@
#define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124)
#define NBD_REQUEST_MAGIC 0x25609513
-#define NBD_SIMPLE_REPLY_MAGIC 0x67446698
#define NBD_OPTS_MAGIC 0x49484156454F5054LL
#define NBD_CLIENT_MAGIC 0x0000420281861253LL
#define NBD_REP_MAGIC 0x0003e889045565a9LL
@@ -64,18 +63,6 @@
#define NBD_SET_TIMEOUT _IO(0xab, 9)
#define NBD_SET_FLAGS _IO(0xab, 10)
-/* NBD errors are based on errno numbers, so there is a 1:1 mapping,
- * but only a limited set of errno values is specified in the protocol.
- * Everything else is squashed to EINVAL.
- */
-#define NBD_SUCCESS 0
-#define NBD_EPERM 1
-#define NBD_EIO 5
-#define NBD_ENOMEM 12
-#define NBD_EINVAL 22
-#define NBD_ENOSPC 28
-#define NBD_ESHUTDOWN 108
-
/* nbd_read_eof
* Tries to read @size bytes from @ioc.
* Returns 1 on success
@@ -95,15 +82,6 @@ static inline int nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size,
return ret;
}
-/* nbd_read
- * Reads @size bytes from @ioc. Returns 0 on success.
- */
-static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size,
- Error **errp)
-{
- return qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0;
-}
-
/* nbd_write
* Writes @size bytes to @ioc. Returns 0 on success.
*/
@@ -126,6 +104,7 @@ const char *nbd_opt_lookup(uint32_t opt);
const char *nbd_rep_lookup(uint32_t rep);
const char *nbd_info_lookup(uint16_t info);
const char *nbd_cmd_lookup(uint16_t info);
+const char *nbd_err_lookup(int err);
int nbd_drop(QIOChannel *ioc, size_t size, Error **errp);
diff --git a/nbd/server.c b/nbd/server.c
index 3df3548d6d..70b40ed27e 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -40,6 +40,8 @@ static int system_errno_to_nbd_errno(int err)
case EFBIG:
case ENOSPC:
return NBD_ENOSPC;
+ case EOVERFLOW:
+ return NBD_EOVERFLOW;
case ESHUTDOWN:
return NBD_ESHUTDOWN;
case EINVAL:
@@ -98,6 +100,8 @@ struct NBDClient {
QTAILQ_ENTRY(NBDClient) next;
int nb_requests;
bool closing;
+
+ bool structured_reply;
};
/* That's all folks */
@@ -251,21 +255,10 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp,
/* Process the NBD_OPT_LIST command, with a potential series of replies.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length,
- Error **errp)
+static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
{
NBDExport *exp;
- if (length) {
- if (nbd_drop(client->ioc, length, errp) < 0) {
- return -EIO;
- }
- return nbd_negotiate_send_rep_err(client->ioc,
- NBD_REP_ERR_INVALID, NBD_OPT_LIST,
- errp,
- "OPT_LIST should not have length");
- }
-
/* For each export, send a NBD_REP_SERVER reply. */
QTAILQ_FOREACH(exp, &exports, next) {
if (nbd_negotiate_send_rep_list(client->ioc, exp, errp)) {
@@ -529,7 +522,6 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
* new channel for all further (now-encrypted) communication. */
static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
- uint32_t length,
Error **errp)
{
QIOChannel *ioc;
@@ -538,15 +530,6 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
trace_nbd_negotiate_handle_starttls();
ioc = client->ioc;
- if (length) {
- if (nbd_drop(ioc, length, errp) < 0) {
- return NULL;
- }
- nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
- errp,
- "OPT_STARTTLS should not have length");
- return NULL;
- }
if (nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK,
NBD_OPT_STARTTLS, errp) < 0) {
@@ -582,6 +565,34 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
return QIO_CHANNEL(tioc);
}
+/* nbd_reject_length: Handle any unexpected payload.
+ * @fatal requests that we quit talking to the client, even if we are able
+ * to successfully send an error to the guest.
+ * Return:
+ * -errno transmission error occurred or @fatal was requested, errp is set
+ * 0 error message successfully sent to client, errp is not set
+ */
+static int nbd_reject_length(NBDClient *client, uint32_t length,
+ uint32_t option, bool fatal, Error **errp)
+{
+ int ret;
+
+ assert(length);
+ if (nbd_drop(client->ioc, length, errp) < 0) {
+ return -EIO;
+ }
+ ret = nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID,
+ option, errp,
+ "option '%s' should have zero length",
+ nbd_opt_lookup(option));
+ if (fatal && !ret) {
+ error_setg(errp, "option '%s' should have zero length",
+ nbd_opt_lookup(option));
+ return -EINVAL;
+ }
+ return ret;
+}
+
/* nbd_negotiate_options
* Process all NBD_OPT_* client option commands, during fixed newstyle
* negotiation.
@@ -672,10 +683,17 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
}
switch (option) {
case NBD_OPT_STARTTLS:
- tioc = nbd_negotiate_handle_starttls(client, length, errp);
+ if (length) {
+ /* Unconditionally drop the connection if the client
+ * can't start a TLS negotiation correctly */
+ return nbd_reject_length(client, length, option, true,
+ errp);
+ }
+ tioc = nbd_negotiate_handle_starttls(client, errp);
if (!tioc) {
return -EIO;
}
+ ret = 0;
object_unref(OBJECT(client->ioc));
client->ioc = QIO_CHANNEL(tioc);
break;
@@ -696,9 +714,6 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
"Option 0x%" PRIx32
"not permitted before TLS",
option);
- if (ret < 0) {
- return ret;
- }
/* Let the client keep trying, unless they asked to
* quit. In this mode, we've already sent an error, so
* we can't ack the abort. */
@@ -710,9 +725,11 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
} else if (fixedNewstyle) {
switch (option) {
case NBD_OPT_LIST:
- ret = nbd_negotiate_handle_list(client, length, errp);
- if (ret < 0) {
- return ret;
+ if (length) {
+ ret = nbd_reject_length(client, length, option, false,
+ errp);
+ } else {
+ ret = nbd_negotiate_handle_list(client, errp);
}
break;
@@ -736,16 +753,13 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
assert(option == NBD_OPT_GO);
return 0;
}
- if (ret) {
- return ret;
- }
break;
case NBD_OPT_STARTTLS:
- if (nbd_drop(client->ioc, length, errp) < 0) {
- return -EIO;
- }
- if (client->tlscreds) {
+ if (length) {
+ ret = nbd_reject_length(client, length, option, false,
+ errp);
+ } else if (client->tlscreds) {
ret = nbd_negotiate_send_rep_err(client->ioc,
NBD_REP_ERR_INVALID,
option, errp,
@@ -756,10 +770,24 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
option, errp,
"TLS not configured");
}
- if (ret < 0) {
- return ret;
+ break;
+
+ case NBD_OPT_STRUCTURED_REPLY:
+ if (length) {
+ ret = nbd_reject_length(client, length, option, false,
+ errp);
+ } else if (client->structured_reply) {
+ ret = nbd_negotiate_send_rep_err(
+ client->ioc, NBD_REP_ERR_INVALID, option, errp,
+ "structured reply already negotiated");
+ } else {
+ ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK,
+ option, errp);
+ client->structured_reply = true;
+ myflags |= NBD_FLAG_SEND_DF;
}
break;
+
default:
if (nbd_drop(client->ioc, length, errp) < 0) {
return -EIO;
@@ -770,9 +798,6 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
"Unsupported option 0x%"
PRIx32 " (%s)", option,
nbd_opt_lookup(option));
- if (ret < 0) {
- return ret;
- }
break;
}
} else {
@@ -792,6 +817,9 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
return -EINVAL;
}
}
+ if (ret < 0) {
+ return ret;
+ }
}
}
@@ -1227,12 +1255,68 @@ static int nbd_co_send_simple_reply(NBDClient *client,
{.iov_base = data, .iov_len = len}
};
- trace_nbd_co_send_simple_reply(handle, nbd_err, len);
+ trace_nbd_co_send_simple_reply(handle, nbd_err, nbd_err_lookup(nbd_err),
+ len);
set_be_simple_reply(&reply, nbd_err, handle);
return nbd_co_send_iov(client, iov, len ? 2 : 1, errp);
}
+static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags,
+ uint16_t type, uint64_t handle, uint32_t length)
+{
+ stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC);
+ stw_be_p(&chunk->flags, flags);
+ stw_be_p(&chunk->type, type);
+ stq_be_p(&chunk->handle, handle);
+ stl_be_p(&chunk->length, length);
+}
+
+static int coroutine_fn nbd_co_send_structured_read(NBDClient *client,
+ uint64_t handle,
+ uint64_t offset,
+ void *data,
+ size_t size,
+ Error **errp)
+{
+ NBDStructuredRead chunk;
+ struct iovec iov[] = {
+ {.iov_base = &chunk, .iov_len = sizeof(chunk)},
+ {.iov_base = data, .iov_len = size}
+ };
+
+ trace_nbd_co_send_structured_read(handle, offset, data, size);
+ set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_OFFSET_DATA,
+ handle, sizeof(chunk) - sizeof(chunk.h) + size);
+ stq_be_p(&chunk.offset, offset);
+
+ return nbd_co_send_iov(client, iov, 2, errp);
+}
+
+static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
+ uint64_t handle,
+ uint32_t error,
+ const char *msg,
+ Error **errp)
+{
+ NBDStructuredError chunk;
+ int nbd_err = system_errno_to_nbd_errno(error);
+ struct iovec iov[] = {
+ {.iov_base = &chunk, .iov_len = sizeof(chunk)},
+ {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0},
+ };
+
+ assert(nbd_err);
+ trace_nbd_co_send_structured_error(handle, nbd_err,
+ nbd_err_lookup(nbd_err), msg ? msg : "");
+ set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle,
+ sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
+ stl_be_p(&chunk.error, nbd_err);
+ stw_be_p(&chunk.message_length, iov[1].iov_len);
+
+ return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp);
+}
+
/* nbd_co_receive_request
* Collect a client request. Return 0 if request looks valid, -EIO to drop
* connection right away, and any other negative value to report an error to
@@ -1243,6 +1327,7 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
Error **errp)
{
NBDClient *client = req->client;
+ int valid_flags;
g_assert(qemu_in_coroutine());
assert(client->recv_coroutine == qemu_coroutine_self());
@@ -1304,13 +1389,15 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
(uint64_t)client->exp->size);
return request->type == NBD_CMD_WRITE ? -ENOSPC : -EINVAL;
}
- if (request->flags & ~(NBD_CMD_FLAG_FUA | NBD_CMD_FLAG_NO_HOLE)) {
- error_setg(errp, "unsupported flags (got 0x%x)", request->flags);
- return -EINVAL;
+ valid_flags = NBD_CMD_FLAG_FUA;
+ if (request->type == NBD_CMD_READ && client->structured_reply) {
+ valid_flags |= NBD_CMD_FLAG_DF;
+ } else if (request->type == NBD_CMD_WRITE_ZEROES) {
+ valid_flags |= NBD_CMD_FLAG_NO_HOLE;
}
- if (request->type != NBD_CMD_WRITE_ZEROES &&
- (request->flags & NBD_CMD_FLAG_NO_HOLE)) {
- error_setg(errp, "unexpected flags (got 0x%x)", request->flags);
+ if (request->flags & ~valid_flags) {
+ error_setg(errp, "unsupported flags for command %s (got 0x%x)",
+ nbd_cmd_lookup(request->type), request->flags);
return -EINVAL;
}
@@ -1328,6 +1415,7 @@ static coroutine_fn void nbd_trip(void *opaque)
int flags;
int reply_data_len = 0;
Error *local_err = NULL;
+ char *msg = NULL;
trace_nbd_trip();
if (client->closing) {
@@ -1378,6 +1466,7 @@ static coroutine_fn void nbd_trip(void *opaque)
break;
case NBD_CMD_WRITE:
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
+ error_setg(&local_err, "Export is read-only");
ret = -EROFS;
break;
}
@@ -1395,7 +1484,7 @@ static coroutine_fn void nbd_trip(void *opaque)
break;
case NBD_CMD_WRITE_ZEROES:
if (exp->nbdflags & NBD_FLAG_READ_ONLY) {
- error_setg(&local_err, "Server is read-only, return error");
+ error_setg(&local_err, "Export is read-only");
ret = -EROFS;
break;
}
@@ -1443,14 +1532,29 @@ reply:
if (local_err) {
/* If we get here, local_err was not a fatal error, and should be sent
* to the client. */
+ assert(ret < 0);
+ msg = g_strdup(error_get_pretty(local_err));
error_report_err(local_err);
local_err = NULL;
}
- if (nbd_co_send_simple_reply(req->client, request.handle,
- ret < 0 ? -ret : 0,
- req->data, reply_data_len, &local_err) < 0)
- {
+ if (client->structured_reply &&
+ (ret < 0 || request.type == NBD_CMD_READ)) {
+ if (ret < 0) {
+ ret = nbd_co_send_structured_error(req->client, request.handle,
+ -ret, msg, &local_err);
+ } else {
+ ret = nbd_co_send_structured_read(req->client, request.handle,
+ request.from, req->data,
+ reply_data_len, &local_err);
+ }
+ } else {
+ ret = nbd_co_send_simple_reply(req->client, request.handle,
+ ret < 0 ? -ret : 0,
+ req->data, reply_data_len, &local_err);
+ }
+ g_free(msg);
+ if (ret < 0) {
error_prepend(&local_err, "Failed to send reply: ");
goto disconnect;
}
diff --git a/nbd/trace-events b/nbd/trace-events
index e27614f050..4a13757524 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -1,5 +1,4 @@
# nbd/client.c
-nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL"
nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32
nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply 0x%" PRIx32" (%s), type 0x%" PRIx32" (%s), len %" PRIu32
nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request 0x%" PRIx32 " (%s), attempting fallback"
@@ -9,9 +8,7 @@ nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%
nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32
nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'"
nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'"
-nbd_receive_starttls_request(void) "Requesting TLS from server"
-nbd_receive_starttls_reply(void) "Getting TLS reply from server"
-nbd_receive_starttls_new_client(void) "TLS request approved, setting up TLS"
+nbd_receive_starttls_new_client(void) "Setting up TLS"
nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake"
nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s"
nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64
@@ -29,7 +26,11 @@ nbd_client_loop_ret(int ret, const char *error) "NBD loop returned %d: %s"
nbd_client_clear_queue(void) "Clearing NBD queue"
nbd_client_clear_socket(void) "Clearing NBD socket"
nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }"
-nbd_receive_reply(uint32_t magic, int32_t error, uint64_t handle) "Got reply: { magic = 0x%" PRIx32 ", .error = % " PRId32 ", handle = %" PRIu64" }"
+nbd_receive_simple_reply(int32_t error, const char *errname, uint64_t handle) "Got simple reply: { .error = %" PRId32 " (%s), handle = %" PRIu64" }"
+nbd_receive_structured_reply_chunk(uint16_t flags, uint16_t type, uint64_t handle, uint32_t length) "Got structured reply chunk: { flags = 0x%" PRIx16 ", type = %d, handle = %" PRIu64 ", length = %" PRIu32 " }"
+
+# nbd/common.c
+nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL"
# nbd/server.c
nbd_negotiate_send_rep_len(uint32_t opt, const char *optname, uint32_t type, const char *typename, uint32_t len) "Reply opt=0x%" PRIx32 " (%s), type=0x%" PRIx32 " (%s), len=%" PRIu32
@@ -53,7 +54,9 @@ nbd_negotiate_success(void) "Negotiation succeeded"
nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint32_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu32 " }"
nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p\n"
nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p\n"
-nbd_co_send_simple_reply(uint64_t handle, uint32_t error, int len) "Send simple reply: handle = %" PRIu64 ", error = %" PRIu32 ", len = %d"
+nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, int len) "Send simple reply: handle = %" PRIu64 ", error = %" PRIu32 " (%s), len = %d"
+nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu"
+nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'"
nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)"
nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32
nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)"
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 2cf6f74152..439d228420 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -48,7 +48,7 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE,
DEF_HELPER_2(exception_internal, void, env, i32)
DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32)
DEF_HELPER_1(setend, void, env)
-DEF_HELPER_1(wfi, void, env)
+DEF_HELPER_2(wfi, void, env, i32)
DEF_HELPER_1(wfe, void, env)
DEF_HELPER_1(yield, void, env)
DEF_HELPER_1(pre_hvc, void, env)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 43106a2d6c..d9cc75e4c5 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -428,9 +428,10 @@ static inline uint32_t syn_breakpoint(int same_el)
| ARM_EL_IL | 0x22;
}
-static inline uint32_t syn_wfx(int cv, int cond, int ti)
+static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit)
{
return (EC_WFX_TRAP << ARM_EL_EC_SHIFT) |
+ (is_16bit ? 0 : (1 << ARM_EL_IL_SHIFT)) |
(cv << 24) | (cond << 20) | ti;
}
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index 138d0df82f..a40a84ac24 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -463,7 +463,7 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe)
return 0;
}
-void HELPER(wfi)(CPUARMState *env)
+void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
int target_el = check_wfx_trap(env, false);
@@ -476,8 +476,9 @@ void HELPER(wfi)(CPUARMState *env)
}
if (target_el) {
- env->pc -= 4;
- raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0), target_el);
+ env->pc -= insn_len;
+ raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2),
+ target_el);
}
cs->exception_index = EXCP_HLT;
diff --git a/target/arm/psci.c b/target/arm/psci.c
index fc34b263d3..eb7b88e926 100644
--- a/target/arm/psci.c
+++ b/target/arm/psci.c
@@ -189,7 +189,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
} else {
env->regs[0] = 0;
}
- helper_wfi(env);
+ helper_wfi(env, 4);
break;
case QEMU_PSCI_0_1_FN_MIGRATE:
case QEMU_PSCI_0_2_FN_MIGRATE:
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index e98fbcf261..caca05aa41 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -11400,17 +11400,22 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
gen_helper_yield(cpu_env);
break;
case DISAS_WFI:
+ {
/* This is a special case because we don't want to just halt the CPU
* if trying to debug across a WFI.
*/
+ TCGv_i32 tmp = tcg_const_i32(4);
+
gen_a64_set_pc_im(dc->pc);
- gen_helper_wfi(cpu_env);
+ gen_helper_wfi(cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
/* The helper doesn't necessarily throw an exception, but we
* must go back to the main loop to check for interrupts anyway.
*/
tcg_gen_exit_tb(0);
break;
}
+ }
}
/* Functions above can change dc->pc, so re-align db->pc_next */
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 6ba4ae92dc..df57dbb11f 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -12125,6 +12125,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
}
insn = arm_ldl_code(env, dc->pc, dc->sctlr_b);
+ dc->insn = insn;
dc->pc += 4;
disas_arm_insn(dc, insn);
@@ -12200,6 +12201,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
insn = insn << 16 | insn2;
dc->pc += 2;
}
+ dc->insn = insn;
if (dc->condexec_mask && !thumb_insn_is_unconditional(dc, insn)) {
uint32_t cond = dc->condexec_cond;
@@ -12326,12 +12328,18 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
/* nothing more to generate */
break;
case DISAS_WFI:
- gen_helper_wfi(cpu_env);
+ {
+ TCGv_i32 tmp = tcg_const_i32((dc->thumb &&
+ !(dc->insn & (1U << 31))) ? 2 : 4);
+
+ gen_helper_wfi(cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
/* The helper doesn't necessarily throw an exception, but we
* must go back to the main loop to check for interrupts anyway.
*/
tcg_gen_exit_tb(0);
break;
+ }
case DISAS_WFE:
gen_helper_wfe(cpu_env);
break;
diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c
index 38a7cdab75..9af6dcef05 100644
--- a/tcg/s390/tcg-target.inc.c
+++ b/tcg/s390/tcg-target.inc.c
@@ -555,9 +555,6 @@ static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src)
static const S390Opcode lli_insns[4] = {
RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH
};
-static const S390Opcode ii_insns[4] = {
- RI_IILL, RI_IILH, RI_IIHL, RI_IIHH
-};
static bool maybe_out_small_movi(TCGContext *s, TCGType type,
TCGReg ret, tcg_target_long sval)
@@ -647,36 +644,19 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret,
return;
}
- /* When allowed, stuff it in the constant pool. */
- if (!in_prologue) {
- if (USE_REG_TB) {
- tcg_out_insn(s, RXY, LG, ret, TCG_REG_TB, TCG_REG_NONE, 0);
- new_pool_label(s, sval, R_390_20, s->code_ptr - 2,
- -(intptr_t)s->code_gen_ptr);
- } else {
- tcg_out_insn(s, RIL, LGRL, ret, 0);
- new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2);
- }
- return;
- }
-
- /* What's left is for the prologue, loading GUEST_BASE, and because
- it failed to match above, is known to be a full 64-bit quantity.
- We could try more than this, but it probably wouldn't pay off. */
- if (s390_facilities & FACILITY_EXT_IMM) {
- tcg_out_insn(s, RIL, LLILF, ret, uval);
- tcg_out_insn(s, RIL, IIHF, ret, uval >> 32);
+ /* Otherwise, stuff it in the constant pool. */
+ if (s390_facilities & FACILITY_GEN_INST_EXT) {
+ tcg_out_insn(s, RIL, LGRL, ret, 0);
+ new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2);
+ } else if (USE_REG_TB && !in_prologue) {
+ tcg_out_insn(s, RXY, LG, ret, TCG_REG_TB, TCG_REG_NONE, 0);
+ new_pool_label(s, sval, R_390_20, s->code_ptr - 2,
+ -(intptr_t)s->code_gen_ptr);
} else {
- const S390Opcode *insns = lli_insns;
- int i;
-
- for (i = 0; i < 4; i++) {
- uint16_t part = uval >> (16 * i);
- if (part) {
- tcg_out_insn_RI(s, insns[i], ret, part);
- insns = ii_insns;
- }
- }
+ TCGReg base = ret ? ret : TCG_TMP0;
+ tcg_out_insn(s, RIL, LARL, base, 0);
+ new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2);
+ tcg_out_insn(s, RXY, LG, ret, base, TCG_REG_NONE, 0);
}
}
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 683ff4abb7..c22f1c4441 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -771,12 +771,32 @@ void tcg_prologue_init(TCGContext *s)
/* Put the prologue at the beginning of code_gen_buffer. */
buf0 = s->code_gen_buffer;
+ total_size = s->code_gen_buffer_size;
s->code_ptr = buf0;
s->code_buf = buf0;
+ s->data_gen_ptr = NULL;
s->code_gen_prologue = buf0;
+ /* Compute a high-water mark, at which we voluntarily flush the buffer
+ and start over. The size here is arbitrary, significantly larger
+ than we expect the code generation for any one opcode to require. */
+ s->code_gen_highwater = s->code_gen_buffer + (total_size - TCG_HIGHWATER);
+
+#ifdef TCG_TARGET_NEED_POOL_LABELS
+ s->pool_labels = NULL;
+#endif
+
/* Generate the prologue. */
tcg_target_qemu_prologue(s);
+
+#ifdef TCG_TARGET_NEED_POOL_LABELS
+ /* Allow the prologue to put e.g. guest_base into a pool entry. */
+ {
+ bool ok = tcg_out_pool_finalize(s);
+ tcg_debug_assert(ok);
+ }
+#endif
+
buf1 = s->code_ptr;
flush_icache_range((uintptr_t)buf0, (uintptr_t)buf1);
@@ -785,21 +805,36 @@ void tcg_prologue_init(TCGContext *s)
s->code_gen_ptr = buf1;
s->code_gen_buffer = buf1;
s->code_buf = buf1;
- total_size = s->code_gen_buffer_size - prologue_size;
+ total_size -= prologue_size;
s->code_gen_buffer_size = total_size;
- /* Compute a high-water mark, at which we voluntarily flush the buffer
- and start over. The size here is arbitrary, significantly larger
- than we expect the code generation for any one opcode to require. */
- s->code_gen_highwater = s->code_gen_buffer + (total_size - TCG_HIGHWATER);
-
tcg_register_jit(s->code_gen_buffer, total_size);
#ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) {
qemu_log_lock();
qemu_log("PROLOGUE: [size=%zu]\n", prologue_size);
- log_disas(buf0, prologue_size);
+ if (s->data_gen_ptr) {
+ size_t code_size = s->data_gen_ptr - buf0;
+ size_t data_size = prologue_size - code_size;
+ size_t i;
+
+ log_disas(buf0, code_size);
+
+ for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) {
+ if (sizeof(tcg_target_ulong) == 8) {
+ qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n",
+ (uintptr_t)s->data_gen_ptr + i,
+ *(uint64_t *)(s->data_gen_ptr + i));
+ } else {
+ qemu_log("0x%08" PRIxPTR ": .long 0x%08x\n",
+ (uintptr_t)s->data_gen_ptr + i,
+ *(uint32_t *)(s->data_gen_ptr + i));
+ }
+ }
+ } else {
+ log_disas(buf0, prologue_size);
+ }
qemu_log("\n");
qemu_log_flush();
qemu_log_unlock();
diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out
index 25dde519e3..be6079d27e 100644
--- a/tests/qemu-iotests/083.out
+++ b/tests/qemu-iotests/083.out
@@ -41,6 +41,7 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo
=== Check disconnect after neg2 ===
+Connection closed
read failed: Input/output error
=== Check disconnect 8 neg2 ===
@@ -53,32 +54,39 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo
=== Check disconnect before request ===
+Connection closed
read failed: Input/output error
=== Check disconnect after request ===
+Connection closed
read failed: Input/output error
=== Check disconnect before reply ===
+Connection closed
read failed: Input/output error
=== Check disconnect after reply ===
+Unexpected end-of-file before all bytes were read
read failed: Input/output error
=== Check disconnect 4 reply ===
Unexpected end-of-file before all bytes were read
+Connection closed
read failed: Input/output error
=== Check disconnect 8 reply ===
Unexpected end-of-file before all bytes were read
+Connection closed
read failed: Input/output error
=== Check disconnect before data ===
+Unexpected end-of-file before all bytes were read
read failed: Input/output error
=== Check disconnect after data ===
@@ -108,6 +116,7 @@ can't open device nbd+tcp://127.0.0.1:PORT/
=== Check disconnect after neg-classic ===
+Connection closed
read failed: Input/output error
=== Check disconnect before neg1 ===
@@ -168,28 +177,34 @@ read failed: Input/output error
=== Check disconnect after request ===
+Connection closed
read failed: Input/output error
=== Check disconnect before reply ===
+Connection closed
read failed: Input/output error
=== Check disconnect after reply ===
+Unexpected end-of-file before all bytes were read
read failed: Input/output error
=== Check disconnect 4 reply ===
Unexpected end-of-file before all bytes were read
+Connection closed
read failed: Input/output error
=== Check disconnect 8 reply ===
Unexpected end-of-file before all bytes were read
+Connection closed
read failed: Input/output error
=== Check disconnect before data ===
+Unexpected end-of-file before all bytes were read
read failed: Input/output error
=== Check disconnect after data ===
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 93e56d0518..2794f60b27 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -631,7 +631,7 @@ QemuCocoaView *cocoaView;
// enable graphic console
case Q_KEY_CODE_1 ... Q_KEY_CODE_9: // '1' to '9' keys
- console_select(keycode - 11);
+ console_select(keycode - Q_KEY_CODE_1);
break;
}
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 382bd4a231..77369c92ce 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -49,6 +49,10 @@
#include <libutil.h>
#endif
+#ifdef __NetBSD__
+#include <sys/sysctl.h>
+#endif
+
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_DEBUG_STACK_USAGE
@@ -250,9 +254,14 @@ void qemu_init_exec_dir(const char *argv0)
p = buf;
}
}
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) \
+ || (defined(__NetBSD__) && defined(KERN_PROC_PATHNAME))
{
+#if defined(__FreeBSD__)
static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+#else
+ static int mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
+#endif
size_t len = sizeof(buf) - 1;
*buf = '\0';