aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/nvme.c218
-rw-r--r--block/trace-events2
-rwxr-xr-xconfigure73
-rw-r--r--hw/input/adb-kbd.c42
-rw-r--r--hw/input/adb-mouse.c65
-rw-r--r--hw/input/adb.c210
-rw-r--r--hw/input/trace-events27
-rw-r--r--hw/misc/mac_via.c411
-rw-r--r--hw/misc/macio/cuda.c60
-rw-r--r--hw/misc/macio/pmu.c47
-rw-r--r--hw/misc/trace-events3
-rw-r--r--hw/ppc/mac_newworld.c2
-rw-r--r--hw/ppc/pnv.c9
-rw-r--r--hw/ppc/spapr_caps.c28
-rw-r--r--hw/ppc/spapr_vio.c6
-rw-r--r--include/hw/input/adb.h26
-rw-r--r--include/hw/misc/mac_via.h2
-rw-r--r--include/hw/misc/macio/cuda.h4
-rw-r--r--include/hw/misc/macio/pmu.h4
-rw-r--r--include/hw/ppc/xive_regs.h2
-rw-r--r--include/qemu/coroutine_int.h5
-rwxr-xr-xscripts/minikconf.py6
-rw-r--r--target/ppc/translate_init.inc.c5
-rwxr-xr-xtests/check-block.sh12
-rw-r--r--util/coroutine-sigaltstack.c4
-rw-r--r--util/coroutine-ucontext.c28
26 files changed, 919 insertions, 382 deletions
diff --git a/block/nvme.c b/block/nvme.c
index eb2f54dd9d..374e268915 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -33,6 +33,14 @@
#define NVME_QUEUE_SIZE 128
#define NVME_BAR_SIZE 8192
+/*
+ * We have to leave one slot empty as that is the full queue case where
+ * head == tail + 1.
+ */
+#define NVME_NUM_REQS (NVME_QUEUE_SIZE - 1)
+
+typedef struct BDRVNVMeState BDRVNVMeState;
+
typedef struct {
int32_t head, tail;
uint8_t *queue;
@@ -47,24 +55,30 @@ typedef struct {
int cid;
void *prp_list_page;
uint64_t prp_list_iova;
- bool busy;
+ int free_req_next; /* q->reqs[] index of next free req */
} NVMeRequest;
typedef struct {
- CoQueue free_req_queue;
QemuMutex lock;
+ /* Read from I/O code path, initialized under BQL */
+ BDRVNVMeState *s;
+ int index;
+
/* Fields protected by BQL */
- int index;
uint8_t *prp_list_pages;
/* Fields protected by @lock */
+ CoQueue free_req_queue;
NVMeQueue sq, cq;
int cq_phase;
- NVMeRequest reqs[NVME_QUEUE_SIZE];
- bool busy;
+ int free_req_head;
+ NVMeRequest reqs[NVME_NUM_REQS];
int need_kick;
int inflight;
+
+ /* Thread-safe, no lock necessary */
+ QEMUBH *completion_bh;
} NVMeQueuePair;
/* Memory mapped registers */
@@ -89,7 +103,7 @@ typedef volatile struct {
QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000);
-typedef struct {
+struct BDRVNVMeState {
AioContext *aio_context;
QEMUVFIOState *vfio;
NVMeRegs *regs;
@@ -123,11 +137,13 @@ typedef struct {
/* PCI address (required for nvme_refresh_filename()) */
char *device;
-} BDRVNVMeState;
+};
#define NVME_BLOCK_OPT_DEVICE "device"
#define NVME_BLOCK_OPT_NAMESPACE "namespace"
+static void nvme_process_completion_bh(void *opaque);
+
static QemuOptsList runtime_opts = {
.name = "nvme",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
@@ -167,8 +183,11 @@ static void nvme_init_queue(BlockDriverState *bs, NVMeQueue *q,
}
}
-static void nvme_free_queue_pair(BlockDriverState *bs, NVMeQueuePair *q)
+static void nvme_free_queue_pair(NVMeQueuePair *q)
{
+ if (q->completion_bh) {
+ qemu_bh_delete(q->completion_bh);
+ }
qemu_vfree(q->prp_list_pages);
qemu_vfree(q->sq.queue);
qemu_vfree(q->cq.queue);
@@ -198,21 +217,28 @@ static NVMeQueuePair *nvme_create_queue_pair(BlockDriverState *bs,
uint64_t prp_list_iova;
qemu_mutex_init(&q->lock);
+ q->s = s;
q->index = idx;
qemu_co_queue_init(&q->free_req_queue);
- q->prp_list_pages = qemu_blockalign0(bs, s->page_size * NVME_QUEUE_SIZE);
+ q->prp_list_pages = qemu_blockalign0(bs, s->page_size * NVME_NUM_REQS);
+ q->completion_bh = aio_bh_new(bdrv_get_aio_context(bs),
+ nvme_process_completion_bh, q);
r = qemu_vfio_dma_map(s->vfio, q->prp_list_pages,
- s->page_size * NVME_QUEUE_SIZE,
+ s->page_size * NVME_NUM_REQS,
false, &prp_list_iova);
if (r) {
goto fail;
}
- for (i = 0; i < NVME_QUEUE_SIZE; i++) {
+ q->free_req_head = -1;
+ for (i = 0; i < NVME_NUM_REQS; i++) {
NVMeRequest *req = &q->reqs[i];
req->cid = i + 1;
+ req->free_req_next = q->free_req_head;
+ q->free_req_head = i;
req->prp_list_page = q->prp_list_pages + i * s->page_size;
req->prp_list_iova = prp_list_iova + i * s->page_size;
}
+
nvme_init_queue(bs, &q->sq, size, NVME_SQ_ENTRY_BYTES, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -229,13 +255,15 @@ static NVMeQueuePair *nvme_create_queue_pair(BlockDriverState *bs,
return q;
fail:
- nvme_free_queue_pair(bs, q);
+ nvme_free_queue_pair(q);
return NULL;
}
/* With q->lock */
-static void nvme_kick(BDRVNVMeState *s, NVMeQueuePair *q)
+static void nvme_kick(NVMeQueuePair *q)
{
+ BDRVNVMeState *s = q->s;
+
if (s->plugged || !q->need_kick) {
return;
}
@@ -254,13 +282,11 @@ static void nvme_kick(BDRVNVMeState *s, NVMeQueuePair *q)
*/
static NVMeRequest *nvme_get_free_req(NVMeQueuePair *q)
{
- int i;
- NVMeRequest *req = NULL;
+ NVMeRequest *req;
qemu_mutex_lock(&q->lock);
- while (q->inflight + q->need_kick > NVME_QUEUE_SIZE - 2) {
- /* We have to leave one slot empty as that is the full queue case (head
- * == tail + 1). */
+
+ while (q->free_req_head == -1) {
if (qemu_in_coroutine()) {
trace_nvme_free_req_queue_wait(q);
qemu_co_queue_wait(&q->free_req_queue, &q->lock);
@@ -269,20 +295,40 @@ static NVMeRequest *nvme_get_free_req(NVMeQueuePair *q)
return NULL;
}
}
- for (i = 0; i < NVME_QUEUE_SIZE; i++) {
- if (!q->reqs[i].busy) {
- q->reqs[i].busy = true;
- req = &q->reqs[i];
- break;
- }
- }
- /* We have checked inflight and need_kick while holding q->lock, so one
- * free req must be available. */
- assert(req);
+
+ req = &q->reqs[q->free_req_head];
+ q->free_req_head = req->free_req_next;
+ req->free_req_next = -1;
+
qemu_mutex_unlock(&q->lock);
return req;
}
+/* With q->lock */
+static void nvme_put_free_req_locked(NVMeQueuePair *q, NVMeRequest *req)
+{
+ req->free_req_next = q->free_req_head;
+ q->free_req_head = req - q->reqs;
+}
+
+/* With q->lock */
+static void nvme_wake_free_req_locked(NVMeQueuePair *q)
+{
+ if (!qemu_co_queue_empty(&q->free_req_queue)) {
+ replay_bh_schedule_oneshot_event(q->s->aio_context,
+ nvme_free_req_queue_cb, q);
+ }
+}
+
+/* Insert a request in the freelist and wake waiters */
+static void nvme_put_free_req_and_wake(NVMeQueuePair *q, NVMeRequest *req)
+{
+ qemu_mutex_lock(&q->lock);
+ nvme_put_free_req_locked(q, req);
+ nvme_wake_free_req_locked(q);
+ qemu_mutex_unlock(&q->lock);
+}
+
static inline int nvme_translate_error(const NvmeCqe *c)
{
uint16_t status = (le16_to_cpu(c->status) >> 1) & 0xFF;
@@ -306,26 +352,40 @@ static inline int nvme_translate_error(const NvmeCqe *c)
}
/* With q->lock */
-static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q)
+static bool nvme_process_completion(NVMeQueuePair *q)
{
+ BDRVNVMeState *s = q->s;
bool progress = false;
NVMeRequest *preq;
NVMeRequest req;
NvmeCqe *c;
trace_nvme_process_completion(s, q->index, q->inflight);
- if (q->busy || s->plugged) {
- trace_nvme_process_completion_queue_busy(s, q->index);
+ if (s->plugged) {
+ trace_nvme_process_completion_queue_plugged(s, q->index);
return false;
}
- q->busy = true;
+
+ /*
+ * Support re-entrancy when a request cb() function invokes aio_poll().
+ * Pending completions must be visible to aio_poll() so that a cb()
+ * function can wait for the completion of another request.
+ *
+ * The aio_poll() loop will execute our BH and we'll resume completion
+ * processing there.
+ */
+ qemu_bh_schedule(q->completion_bh);
+
assert(q->inflight >= 0);
while (q->inflight) {
+ int ret;
int16_t cid;
+
c = (NvmeCqe *)&q->cq.queue[q->cq.head * NVME_CQ_ENTRY_BYTES];
if ((le16_to_cpu(c->status) & 0x1) == q->cq_phase) {
break;
}
+ ret = nvme_translate_error(c);
q->cq.head = (q->cq.head + 1) % NVME_QUEUE_SIZE;
if (!q->cq.head) {
q->cq_phase = !q->cq_phase;
@@ -336,33 +396,47 @@ static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q)
cid);
continue;
}
- assert(cid <= NVME_QUEUE_SIZE);
trace_nvme_complete_command(s, q->index, cid);
preq = &q->reqs[cid - 1];
req = *preq;
assert(req.cid == cid);
assert(req.cb);
- preq->busy = false;
+ nvme_put_free_req_locked(q, preq);
preq->cb = preq->opaque = NULL;
+ q->inflight--;
qemu_mutex_unlock(&q->lock);
- req.cb(req.opaque, nvme_translate_error(c));
+ req.cb(req.opaque, ret);
qemu_mutex_lock(&q->lock);
- q->inflight--;
progress = true;
}
if (progress) {
/* Notify the device so it can post more completions. */
smp_mb_release();
*q->cq.doorbell = cpu_to_le32(q->cq.head);
- if (!qemu_co_queue_empty(&q->free_req_queue)) {
- replay_bh_schedule_oneshot_event(s->aio_context,
- nvme_free_req_queue_cb, q);
- }
+ nvme_wake_free_req_locked(q);
}
- q->busy = false;
+
+ qemu_bh_cancel(q->completion_bh);
+
return progress;
}
+static void nvme_process_completion_bh(void *opaque)
+{
+ NVMeQueuePair *q = opaque;
+
+ /*
+ * We're being invoked because a nvme_process_completion() cb() function
+ * called aio_poll(). The callback may be waiting for further completions
+ * so notify the device that it has space to fill in more completions now.
+ */
+ smp_mb_release();
+ *q->cq.doorbell = cpu_to_le32(q->cq.head);
+ nvme_wake_free_req_locked(q);
+
+ nvme_process_completion(q);
+}
+
static void nvme_trace_command(const NvmeCmd *cmd)
{
int i;
@@ -374,8 +448,7 @@ static void nvme_trace_command(const NvmeCmd *cmd)
}
}
-static void nvme_submit_command(BDRVNVMeState *s, NVMeQueuePair *q,
- NVMeRequest *req,
+static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req,
NvmeCmd *cmd, BlockCompletionFunc cb,
void *opaque)
{
@@ -384,15 +457,15 @@ static void nvme_submit_command(BDRVNVMeState *s, NVMeQueuePair *q,
req->opaque = opaque;
cmd->cid = cpu_to_le32(req->cid);
- trace_nvme_submit_command(s, q->index, req->cid);
+ trace_nvme_submit_command(q->s, q->index, req->cid);
nvme_trace_command(cmd);
qemu_mutex_lock(&q->lock);
memcpy((uint8_t *)q->sq.queue +
q->sq.tail * NVME_SQ_ENTRY_BYTES, cmd, sizeof(*cmd));
q->sq.tail = (q->sq.tail + 1) % NVME_QUEUE_SIZE;
q->need_kick++;
- nvme_kick(s, q);
- nvme_process_completion(s, q);
+ nvme_kick(q);
+ nvme_process_completion(q);
qemu_mutex_unlock(&q->lock);
}
@@ -407,13 +480,12 @@ static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
NvmeCmd *cmd)
{
NVMeRequest *req;
- BDRVNVMeState *s = bs->opaque;
int ret = -EINPROGRESS;
req = nvme_get_free_req(q);
if (!req) {
return -EBUSY;
}
- nvme_submit_command(s, q, req, cmd, nvme_cmd_sync_cb, &ret);
+ nvme_submit_command(q, req, cmd, nvme_cmd_sync_cb, &ret);
BDRV_POLL_WHILE(bs, ret == -EINPROGRESS);
return ret;
@@ -512,8 +584,20 @@ static bool nvme_poll_queues(BDRVNVMeState *s)
for (i = 0; i < s->nr_queues; i++) {
NVMeQueuePair *q = s->queues[i];
+ const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES;
+ NvmeCqe *cqe = (NvmeCqe *)&q->cq.queue[cqe_offset];
+
+ /*
+ * Do an early check for completions. q->lock isn't needed because
+ * nvme_process_completion() only runs in the event loop thread and
+ * cannot race with itself.
+ */
+ if ((le16_to_cpu(cqe->status) & 0x1) == q->cq_phase) {
+ continue;
+ }
+
qemu_mutex_lock(&q->lock);
- while (nvme_process_completion(s, q)) {
+ while (nvme_process_completion(q)) {
/* Keep polling */
progress = true;
}
@@ -551,7 +635,7 @@ static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
};
if (nvme_cmd_sync(bs, s->queues[0], &cmd)) {
error_setg(errp, "Failed to create io queue [%d]", n);
- nvme_free_queue_pair(bs, q);
+ nvme_free_queue_pair(q);
return false;
}
cmd = (NvmeCmd) {
@@ -562,7 +646,7 @@ static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
};
if (nvme_cmd_sync(bs, s->queues[0], &cmd)) {
error_setg(errp, "Failed to create io queue [%d]", n);
- nvme_free_queue_pair(bs, q);
+ nvme_free_queue_pair(q);
return false;
}
s->queues = g_renew(NVMeQueuePair *, s->queues, n + 1);
@@ -757,7 +841,7 @@ static void nvme_close(BlockDriverState *bs)
BDRVNVMeState *s = bs->opaque;
for (i = 0; i < s->nr_queues; ++i) {
- nvme_free_queue_pair(bs, s->queues[i]);
+ nvme_free_queue_pair(s->queues[i]);
}
g_free(s->queues);
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
@@ -987,10 +1071,10 @@ static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs,
r = nvme_cmd_map_qiov(bs, &cmd, req, qiov);
qemu_co_mutex_unlock(&s->dma_map_lock);
if (r) {
- req->busy = false;
+ nvme_put_free_req_and_wake(ioq, req);
return r;
}
- nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
+ nvme_submit_command(ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
while (data.ret == -EINPROGRESS) {
@@ -1090,7 +1174,7 @@ static coroutine_fn int nvme_co_flush(BlockDriverState *bs)
assert(s->nr_queues > 1);
req = nvme_get_free_req(ioq);
assert(req);
- nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
+ nvme_submit_command(ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
if (data.ret == -EINPROGRESS) {
@@ -1143,7 +1227,7 @@ static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs,
req = nvme_get_free_req(ioq);
assert(req);
- nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
+ nvme_submit_command(ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
while (data.ret == -EINPROGRESS) {
@@ -1204,13 +1288,13 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs,
qemu_co_mutex_unlock(&s->dma_map_lock);
if (ret) {
- req->busy = false;
+ nvme_put_free_req_and_wake(ioq, req);
goto out;
}
trace_nvme_dsm(s, offset, bytes);
- nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
+ nvme_submit_command(ioq, req, &cmd, nvme_rw_cb, &data);
data.co = qemu_coroutine_self();
while (data.ret == -EINPROGRESS) {
@@ -1262,6 +1346,13 @@ static void nvme_detach_aio_context(BlockDriverState *bs)
{
BDRVNVMeState *s = bs->opaque;
+ for (int i = 0; i < s->nr_queues; i++) {
+ NVMeQueuePair *q = s->queues[i];
+
+ qemu_bh_delete(q->completion_bh);
+ q->completion_bh = NULL;
+ }
+
aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier,
false, NULL, NULL);
}
@@ -1274,6 +1365,13 @@ static void nvme_attach_aio_context(BlockDriverState *bs,
s->aio_context = new_context;
aio_set_event_notifier(new_context, &s->irq_notifier,
false, nvme_handle_event, nvme_poll_cb);
+
+ for (int i = 0; i < s->nr_queues; i++) {
+ NVMeQueuePair *q = s->queues[i];
+
+ q->completion_bh =
+ aio_bh_new(new_context, nvme_process_completion_bh, q);
+ }
}
static void nvme_aio_plug(BlockDriverState *bs)
@@ -1292,8 +1390,8 @@ static void nvme_aio_unplug(BlockDriverState *bs)
for (i = 1; i < s->nr_queues; i++) {
NVMeQueuePair *q = s->queues[i];
qemu_mutex_lock(&q->lock);
- nvme_kick(s, q);
- nvme_process_completion(s, q);
+ nvme_kick(q);
+ nvme_process_completion(q);
qemu_mutex_unlock(&q->lock);
}
}
diff --git a/block/trace-events b/block/trace-events
index 29dff8881c..dbe76a7613 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -158,7 +158,7 @@ nvme_kick(void *s, int queue) "s %p queue %d"
nvme_dma_flush_queue_wait(void *s) "s %p"
nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x"
nvme_process_completion(void *s, int index, int inflight) "s %p queue %d inflight %d"
-nvme_process_completion_queue_busy(void *s, int index) "s %p queue %d"
+nvme_process_completion_queue_plugged(void *s, int index) "s %p queue %d"
nvme_complete_command(void *s, int index, int cid) "s %p queue %d cid %d"
nvme_submit_command(void *s, int index, int cid) "s %p queue %d cid %d"
nvme_submit_command_raw(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7) "%02x %02x %02x %02x %02x %02x %02x %02x"
diff --git a/configure b/configure
index 8d9435a0e0..4a22dcd563 100755
--- a/configure
+++ b/configure
@@ -307,6 +307,7 @@ audio_win_int=""
libs_qga=""
debug_info="yes"
stack_protector=""
+safe_stack=""
use_containers="yes"
gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb")
@@ -1288,6 +1289,10 @@ for opt do
;;
--disable-stack-protector) stack_protector="no"
;;
+ --enable-safe-stack) safe_stack="yes"
+ ;;
+ --disable-safe-stack) safe_stack="no"
+ ;;
--disable-curses) curses="no"
;;
--enable-curses) curses="yes"
@@ -1834,6 +1839,8 @@ disabled with --disable-FEATURE, default is enabled if available:
debug-tcg TCG debugging (default is disabled)
debug-info debugging information
sparse sparse checker
+ safe-stack SafeStack Stack Smash Protection. Depends on
+ clang/llvm >= 3.7 and requires coroutine backend ucontext.
gnutls GNUTLS cryptography support
nettle nettle cryptography support
@@ -5579,6 +5586,67 @@ if test "$debug_stack_usage" = "yes"; then
fi
fi
+##################################################
+# SafeStack
+
+
+if test "$safe_stack" = "yes"; then
+cat > $TMPC << EOF
+int main(int argc, char *argv[])
+{
+#if ! __has_feature(safe_stack)
+#error SafeStack Disabled
+#endif
+ return 0;
+}
+EOF
+ flag="-fsanitize=safe-stack"
+ # Check that safe-stack is supported and enabled.
+ if compile_prog "-Werror $flag" "$flag"; then
+ # Flag needed both at compilation and at linking
+ QEMU_CFLAGS="$QEMU_CFLAGS $flag"
+ QEMU_LDFLAGS="$QEMU_LDFLAGS $flag"
+ else
+ error_exit "SafeStack not supported by your compiler"
+ fi
+ if test "$coroutine" != "ucontext"; then
+ error_exit "SafeStack is only supported by the coroutine backend ucontext"
+ fi
+else
+cat > $TMPC << EOF
+int main(int argc, char *argv[])
+{
+#if defined(__has_feature)
+#if __has_feature(safe_stack)
+#error SafeStack Enabled
+#endif
+#endif
+ return 0;
+}
+EOF
+if test "$safe_stack" = "no"; then
+ # Make sure that safe-stack is disabled
+ if ! compile_prog "-Werror" ""; then
+ # SafeStack was already enabled, try to explicitly remove the feature
+ flag="-fno-sanitize=safe-stack"
+ if ! compile_prog "-Werror $flag" "$flag"; then
+ error_exit "Configure cannot disable SafeStack"
+ fi
+ QEMU_CFLAGS="$QEMU_CFLAGS $flag"
+ QEMU_LDFLAGS="$QEMU_LDFLAGS $flag"
+ fi
+else # "$safe_stack" = ""
+ # Set safe_stack to yes or no based on pre-existing flags
+ if compile_prog "-Werror" ""; then
+ safe_stack="no"
+ else
+ safe_stack="yes"
+ if test "$coroutine" != "ucontext"; then
+ error_exit "SafeStack is only supported by the coroutine backend ucontext"
+ fi
+ fi
+fi
+fi
##########################################
# check if we have open_by_handle_at
@@ -6789,6 +6857,7 @@ echo "sparse enabled $sparse"
echo "strip binaries $strip_opt"
echo "profiler $profiler"
echo "static build $static"
+echo "safe stack $safe_stack"
if test "$darwin" = "yes" ; then
echo "Cocoa support $cocoa"
fi
@@ -8399,6 +8468,10 @@ if test "$ccache_cpp2" = "yes"; then
echo "export CCACHE_CPP2=y" >> $config_host_mak
fi
+if test "$safe_stack" = "yes"; then
+ echo "CONFIG_SAFESTACK=y" >> $config_host_mak
+fi
+
# If we're using a separate build tree, set it up now.
# DIRS are directories which we simply mkdir in the build tree;
# LINKS are things to symlink back into the source tree
diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c
index a6d5c9b7c9..3cfb6a7a20 100644
--- a/hw/input/adb-kbd.c
+++ b/hw/input/adb-kbd.c
@@ -243,7 +243,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
olen = 0;
switch (cmd) {
case ADB_WRITEREG:
- trace_adb_kbd_writereg(reg, buf[1]);
+ trace_adb_device_kbd_writereg(reg, buf[1]);
switch (reg) {
case 2:
/* LED status */
@@ -256,24 +256,22 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
case ADB_CMD_CHANGE_ID_AND_ACT:
case ADB_CMD_CHANGE_ID_AND_ENABLE:
d->devaddr = buf[1] & 0xf;
- trace_adb_kbd_request_change_addr(d->devaddr);
+ trace_adb_device_kbd_request_change_addr(d->devaddr);
break;
default:
- if (!d->disable_direct_reg3_writes) {
- d->devaddr = buf[1] & 0xf;
-
- /* we support handlers:
- * 1: Apple Standard Keyboard
- * 2: Apple Extended Keyboard (LShift = RShift)
- * 3: Apple Extended Keyboard (LShift != RShift)
- */
- if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) {
- d->handler = buf[2];
- }
-
- trace_adb_kbd_request_change_addr_and_handler(d->devaddr,
- d->handler);
+ d->devaddr = buf[1] & 0xf;
+ /*
+ * we support handlers:
+ * 1: Apple Standard Keyboard
+ * 2: Apple Extended Keyboard (LShift = RShift)
+ * 3: Apple Extended Keyboard (LShift != RShift)
+ */
+ if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) {
+ d->handler = buf[2];
}
+
+ trace_adb_device_kbd_request_change_addr_and_handler(
+ d->devaddr, d->handler);
break;
}
}
@@ -296,12 +294,19 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
olen = 2;
break;
}
- trace_adb_kbd_readreg(reg, obuf[0], obuf[1]);
+ trace_adb_device_kbd_readreg(reg, obuf[0], obuf[1]);
break;
}
return olen;
}
+static bool adb_kbd_has_data(ADBDevice *d)
+{
+ KBDState *s = ADB_KEYBOARD(d);
+
+ return s->count > 0;
+}
+
/* This is where keyboard events enter this file */
static void adb_keyboard_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
@@ -316,7 +321,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src,
/* FIXME: take handler into account when translating qcode */
keycode = qcode_to_adb_keycode[qcode];
if (keycode == NO_KEY) { /* We don't want to send this to the guest */
- trace_adb_kbd_no_key();
+ trace_adb_device_kbd_no_key();
return;
}
if (evt->u.key.data->down == false) { /* if key release event */
@@ -384,6 +389,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data)
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_kbd_request;
+ adc->devhasdata = adb_kbd_has_data;
dc->reset = adb_kbd_reset;
dc->vmsd = &vmstate_adb_kbd;
}
diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c
index aeba41bddd..577a38ff2e 100644
--- a/hw/input/adb-mouse.c
+++ b/hw/input/adb-mouse.c
@@ -121,7 +121,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
s->dx = 0;
s->dy = 0;
s->dz = 0;
- trace_adb_mouse_flush();
+ trace_adb_device_mouse_flush();
return 0;
}
@@ -130,11 +130,21 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
olen = 0;
switch (cmd) {
case ADB_WRITEREG:
- trace_adb_mouse_writereg(reg, buf[1]);
+ trace_adb_device_mouse_writereg(reg, buf[1]);
switch (reg) {
case 2:
break;
case 3:
+ /*
+ * MacOS 9 has a bug in its ADB driver whereby after configuring
+ * the ADB bus devices it sends another write of invalid length
+ * to reg 3. Make sure we ignore it to prevent an address clash
+ * with the previous device.
+ */
+ if (len != 3) {
+ return 0;
+ }
+
switch (buf[2]) {
case ADB_CMD_SELF_TEST:
break;
@@ -142,30 +152,28 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
case ADB_CMD_CHANGE_ID_AND_ACT:
case ADB_CMD_CHANGE_ID_AND_ENABLE:
d->devaddr = buf[1] & 0xf;
- trace_adb_mouse_request_change_addr(d->devaddr);
+ trace_adb_device_mouse_request_change_addr(d->devaddr);
break;
default:
- if (!d->disable_direct_reg3_writes) {
- d->devaddr = buf[1] & 0xf;
-
- /* we support handlers:
- * 0x01: Classic Apple Mouse Protocol / 100 cpi operations
- * 0x02: Classic Apple Mouse Protocol / 200 cpi operations
- * we don't support handlers (at least):
- * 0x03: Mouse systems A3 trackball
- * 0x04: Extended Apple Mouse Protocol
- * 0x2f: Microspeed mouse
- * 0x42: Macally
- * 0x5f: Microspeed mouse
- * 0x66: Microspeed mouse
- */
- if (buf[2] == 1 || buf[2] == 2) {
- d->handler = buf[2];
- }
-
- trace_adb_mouse_request_change_addr_and_handler(
- d->devaddr, d->handler);
+ d->devaddr = buf[1] & 0xf;
+ /*
+ * we support handlers:
+ * 0x01: Classic Apple Mouse Protocol / 100 cpi operations
+ * 0x02: Classic Apple Mouse Protocol / 200 cpi operations
+ * we don't support handlers (at least):
+ * 0x03: Mouse systems A3 trackball
+ * 0x04: Extended Apple Mouse Protocol
+ * 0x2f: Microspeed mouse
+ * 0x42: Macally
+ * 0x5f: Microspeed mouse
+ * 0x66: Microspeed mouse
+ */
+ if (buf[2] == 1 || buf[2] == 2) {
+ d->handler = buf[2];
}
+
+ trace_adb_device_mouse_request_change_addr_and_handler(
+ d->devaddr, d->handler);
break;
}
}
@@ -183,12 +191,20 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
olen = 2;
break;
}
- trace_adb_mouse_readreg(reg, obuf[0], obuf[1]);
+ trace_adb_device_mouse_readreg(reg, obuf[0], obuf[1]);
break;
}
return olen;
}
+static bool adb_mouse_has_data(ADBDevice *d)
+{
+ MouseState *s = ADB_MOUSE(d);
+
+ return !(s->last_buttons_state == s->buttons_state &&
+ s->dx == 0 && s->dy == 0);
+}
+
static void adb_mouse_reset(DeviceState *dev)
{
ADBDevice *d = ADB_DEVICE(dev);
@@ -244,6 +260,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data)
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_mouse_request;
+ adc->devhasdata = adb_mouse_has_data;
dc->reset = adb_mouse_reset;
dc->vmsd = &vmstate_adb_mouse;
}
diff --git a/hw/input/adb.c b/hw/input/adb.c
index b1ac4a3852..013fcc9c54 100644
--- a/hw/input/adb.c
+++ b/hw/input/adb.c
@@ -27,41 +27,85 @@
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qemu/module.h"
+#include "qemu/timer.h"
#include "adb-internal.h"
+#include "trace.h"
/* error codes */
#define ADB_RET_NOTPRESENT (-2)
+static const char *adb_commands[] = {
+ "RESET", "FLUSH", "(Reserved 0x2)", "(Reserved 0x3)",
+ "Reserved (0x4)", "(Reserved 0x5)", "(Reserved 0x6)", "(Reserved 0x7)",
+ "LISTEN r0", "LISTEN r1", "LISTEN r2", "LISTEN r3",
+ "TALK r0", "TALK r1", "TALK r2", "TALK r3",
+};
+
static void adb_device_reset(ADBDevice *d)
{
qdev_reset_all(DEVICE(d));
}
-int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf,
+ int len)
{
ADBDevice *d;
- int devaddr, cmd, i;
+ ADBDeviceClass *adc;
+ int devaddr, cmd, olen, i;
cmd = buf[0] & 0xf;
if (cmd == ADB_BUSRESET) {
- for(i = 0; i < s->nb_devices; i++) {
+ for (i = 0; i < s->nb_devices; i++) {
d = s->devices[i];
adb_device_reset(d);
}
+ s->status = 0;
return 0;
}
+
+ s->pending = 0;
+ for (i = 0; i < s->nb_devices; i++) {
+ d = s->devices[i];
+ adc = ADB_DEVICE_GET_CLASS(d);
+
+ if (adc->devhasdata(d)) {
+ s->pending |= (1 << d->devaddr);
+ }
+ }
+
+ s->status = 0;
devaddr = buf[0] >> 4;
- for(i = 0; i < s->nb_devices; i++) {
+ for (i = 0; i < s->nb_devices; i++) {
d = s->devices[i];
+ adc = ADB_DEVICE_GET_CLASS(d);
+
if (d->devaddr == devaddr) {
- ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d);
- return adc->devreq(d, obuf, buf, len);
+ olen = adc->devreq(d, obuf, buf, len);
+ if (!olen) {
+ s->status |= ADB_STATUS_BUSTIMEOUT;
+ }
+ return olen;
}
}
+
+ s->status |= ADB_STATUS_BUSTIMEOUT;
return ADB_RET_NOTPRESENT;
}
-/* XXX: move that to cuda ? */
+int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+{
+ int ret;
+
+ trace_adb_bus_request(buf[0] >> 4, adb_commands[buf[0] & 0xf], len);
+
+ assert(s->autopoll_blocked);
+
+ ret = do_adb_request(s, obuf, buf, len);
+
+ trace_adb_bus_request_done(buf[0] >> 4, adb_commands[buf[0] & 0xf], ret);
+ return ret;
+}
+
int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
{
ADBDevice *d;
@@ -69,18 +113,20 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
uint8_t buf[1];
olen = 0;
- for(i = 0; i < s->nb_devices; i++) {
- if (s->poll_index >= s->nb_devices)
+ for (i = 0; i < s->nb_devices; i++) {
+ if (s->poll_index >= s->nb_devices) {
s->poll_index = 0;
+ }
d = s->devices[s->poll_index];
if ((1 << d->devaddr) & poll_mask) {
buf[0] = ADB_READREG | (d->devaddr << 4);
- olen = adb_request(s, obuf + 1, buf, 1);
+ olen = do_adb_request(s, obuf + 1, buf, 1);
/* if there is data, we poll again the same device */
if (olen > 0) {
+ s->status |= ADB_STATUS_POLLREPLY;
obuf[0] = buf[0];
olen++;
- break;
+ return olen;
}
}
s->poll_index++;
@@ -88,10 +134,145 @@ int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask)
return olen;
}
+void adb_set_autopoll_enabled(ADBBusState *s, bool enabled)
+{
+ if (s->autopoll_enabled != enabled) {
+ s->autopoll_enabled = enabled;
+ if (s->autopoll_enabled) {
+ timer_mod(s->autopoll_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ s->autopoll_rate_ms);
+ } else {
+ timer_del(s->autopoll_timer);
+ }
+ }
+}
+
+void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms)
+{
+ s->autopoll_rate_ms = rate_ms;
+
+ if (s->autopoll_enabled) {
+ timer_mod(s->autopoll_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ s->autopoll_rate_ms);
+ }
+}
+
+void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask)
+{
+ if (s->autopoll_mask != mask) {
+ s->autopoll_mask = mask;
+ if (s->autopoll_enabled && s->autopoll_mask) {
+ timer_mod(s->autopoll_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ s->autopoll_rate_ms);
+ } else {
+ timer_del(s->autopoll_timer);
+ }
+ }
+}
+
+void adb_autopoll_block(ADBBusState *s)
+{
+ s->autopoll_blocked = true;
+ trace_adb_bus_autopoll_block(s->autopoll_blocked);
+
+ if (s->autopoll_enabled) {
+ timer_del(s->autopoll_timer);
+ }
+}
+
+void adb_autopoll_unblock(ADBBusState *s)
+{
+ s->autopoll_blocked = false;
+ trace_adb_bus_autopoll_block(s->autopoll_blocked);
+
+ if (s->autopoll_enabled) {
+ timer_mod(s->autopoll_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ s->autopoll_rate_ms);
+ }
+}
+
+static void adb_autopoll(void *opaque)
+{
+ ADBBusState *s = opaque;
+
+ if (!s->autopoll_blocked) {
+ trace_adb_bus_autopoll_cb(s->autopoll_mask);
+ s->autopoll_cb(s->autopoll_cb_opaque);
+ trace_adb_bus_autopoll_cb_done(s->autopoll_mask);
+ }
+
+ timer_mod(s->autopoll_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ s->autopoll_rate_ms);
+}
+
+void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
+ void *opaque)
+{
+ s->autopoll_cb = cb;
+ s->autopoll_cb_opaque = opaque;
+}
+
+static const VMStateDescription vmstate_adb_bus = {
+ .name = "adb_bus",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState),
+ VMSTATE_BOOL(autopoll_enabled, ADBBusState),
+ VMSTATE_UINT8(autopoll_rate_ms, ADBBusState),
+ VMSTATE_UINT16(autopoll_mask, ADBBusState),
+ VMSTATE_BOOL(autopoll_blocked, ADBBusState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void adb_bus_reset(BusState *qbus)
+{
+ ADBBusState *adb_bus = ADB_BUS(qbus);
+
+ adb_bus->autopoll_enabled = false;
+ adb_bus->autopoll_mask = 0xffff;
+ adb_bus->autopoll_rate_ms = 20;
+}
+
+static void adb_bus_realize(BusState *qbus, Error **errp)
+{
+ ADBBusState *adb_bus = ADB_BUS(qbus);
+
+ adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
+ adb_bus);
+
+ vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus);
+}
+
+static void adb_bus_unrealize(BusState *qbus)
+{
+ ADBBusState *adb_bus = ADB_BUS(qbus);
+
+ timer_del(adb_bus->autopoll_timer);
+
+ vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus);
+}
+
+static void adb_bus_class_init(ObjectClass *klass, void *data)
+{
+ BusClass *k = BUS_CLASS(klass);
+
+ k->realize = adb_bus_realize;
+ k->unrealize = adb_bus_unrealize;
+ k->reset = adb_bus_reset;
+}
+
static const TypeInfo adb_bus_type_info = {
.name = TYPE_ADB_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(ADBBusState),
+ .class_init = adb_bus_class_init,
};
const VMStateDescription vmstate_adb_device = {
@@ -117,18 +298,11 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp)
bus->devices[bus->nb_devices++] = d;
}
-static Property adb_device_properties[] = {
- DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice,
- disable_direct_reg3_writes, false),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void adb_device_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = adb_device_realizefn;
- device_class_set_props(dc, adb_device_properties);
dc->bus_type = TYPE_ADB_BUS;
}
diff --git a/hw/input/trace-events b/hw/input/trace-events
index a2888fd10c..1dd8ad6018 100644
--- a/hw/input/trace-events
+++ b/hw/input/trace-events
@@ -1,18 +1,25 @@
# See docs/devel/tracing.txt for syntax documentation.
# adb-kbd.c
-adb_kbd_no_key(void) "Ignoring NO_KEY"
-adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
-adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
-adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x"
-adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
+adb_device_kbd_no_key(void) "Ignoring NO_KEY"
+adb_device_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
+adb_device_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
+adb_device_kbd_request_change_addr(int devaddr) "change addr to 0x%x"
+adb_device_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
# adb-mouse.c
-adb_mouse_flush(void) "flush"
-adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
-adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
-adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x"
-adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
+adb_device_mouse_flush(void) "flush"
+adb_device_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
+adb_device_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
+adb_device_mouse_request_change_addr(int devaddr) "change addr to 0x%x"
+adb_device_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
+
+# adb.c
+adb_bus_request(uint8_t addr, const char *cmd, int size) "device 0x%x %s cmdsize=%d"
+adb_bus_request_done(uint8_t addr, const char *cmd, int size) "device 0x%x %s replysize=%d"
+adb_bus_autopoll_block(bool blocked) "blocked: %d"
+adb_bus_autopoll_cb(uint16_t mask) "executing autopoll_cb with autopoll mask 0x%x"
+adb_bus_autopoll_cb_done(uint16_t mask) "done executing autopoll_cb with autopoll mask 0x%x"
# pckbd.c
pckbd_kbd_read_data(uint32_t val) "0x%02x"
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index 9cd313c812..d76d7b28d3 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -599,179 +599,310 @@ static void via1_rtc_update(MacVIAState *m)
m->cmd = REG_EMPTY;
}
-static int adb_via_poll(MacVIAState *s, int state, uint8_t *data)
+static void adb_via_poll(void *opaque)
{
- if (state != ADB_STATE_IDLE) {
- return 0;
- }
+ MacVIAState *m = opaque;
+ MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1);
+ MOS6522State *s = MOS6522(v1s);
+ ADBBusState *adb_bus = &m->adb_bus;
+ uint8_t obuf[9];
+ uint8_t *data = &s->sr;
+ int olen;
+ uint16_t pending;
- if (s->adb_data_in_size < s->adb_data_in_index) {
- return 0;
- }
+ /*
+ * Setting vADBInt below indicates that an autopoll reply has been
+ * received, however we must block autopoll until the point where
+ * the entire reply has been read back to the host
+ */
+ adb_autopoll_block(adb_bus);
+
+ m->adb_data_in_index = 0;
+ m->adb_data_out_index = 0;
+ olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask);
+
+ if (olen > 0) {
+ /* Autopoll response */
+ *data = obuf[0];
+ olen--;
+ memcpy(m->adb_data_in, &obuf[1], olen);
+ m->adb_data_in_size = olen;
+
+ s->b &= ~VIA1B_vADBInt;
+ qemu_irq_raise(m->adb_data_ready);
+ } else if (olen < 0) {
+ /* Bus timeout (device does not exist) */
+ *data = 0xff;
+ s->b |= VIA1B_vADBInt;
+ adb_autopoll_unblock(adb_bus);
+ } else {
+ pending = adb_bus->pending & ~(1 << (m->adb_autopoll_cmd >> 4));
+
+ if (pending) {
+ /*
+ * Bus timeout (device exists but another device has data). Block
+ * autopoll so the OS can read out the first EVEN and first ODD
+ * byte to determine bus timeout and SRQ status
+ */
+ *data = m->adb_autopoll_cmd;
+ s->b &= ~VIA1B_vADBInt;
- if (s->adb_data_out_index != 0) {
- return 0;
- }
+ obuf[0] = 0xff;
+ obuf[1] = 0xff;
+ olen = 2;
- s->adb_data_in_index = 0;
- s->adb_data_out_index = 0;
- s->adb_data_in_size = adb_poll(&s->adb_bus, s->adb_data_in, 0xffff);
+ memcpy(m->adb_data_in, obuf, olen);
+ m->adb_data_in_size = olen;
- if (s->adb_data_in_size) {
- *data = s->adb_data_in[s->adb_data_in_index++];
- qemu_irq_raise(s->adb_data_ready);
+ qemu_irq_raise(m->adb_data_ready);
+ } else {
+ /* Bus timeout (device exists but no other device has data) */
+ *data = 0;
+ s->b |= VIA1B_vADBInt;
+ adb_autopoll_unblock(adb_bus);
+ }
}
- return s->adb_data_in_size;
+ trace_via1_adb_poll(*data, (s->b & VIA1B_vADBInt) ? "+" : "-",
+ adb_bus->status, m->adb_data_in_index, olen);
}
-static int adb_via_send(MacVIAState *s, int state, uint8_t data)
+static int adb_via_send_len(uint8_t data)
{
- switch (state) {
- case ADB_STATE_NEW:
- s->adb_data_out_index = 0;
- break;
- case ADB_STATE_EVEN:
- if ((s->adb_data_out_index & 1) == 0) {
- return 0;
- }
- break;
- case ADB_STATE_ODD:
- if (s->adb_data_out_index & 1) {
- return 0;
+ /* Determine the send length from the given ADB command */
+ uint8_t cmd = data & 0xc;
+ uint8_t reg = data & 0x3;
+
+ switch (cmd) {
+ case 0x8:
+ /* Listen command */
+ switch (reg) {
+ case 2:
+ /* Register 2 is only used for the keyboard */
+ return 3;
+ case 3:
+ /*
+ * Fortunately our devices only implement writes
+ * to register 3 which is fixed at 2 bytes
+ */
+ return 3;
+ default:
+ qemu_log_mask(LOG_UNIMP, "ADB unknown length for register %d\n",
+ reg);
+ return 1;
}
- break;
- case ADB_STATE_IDLE:
- return 0;
+ default:
+ /* Talk, BusReset */
+ return 1;
}
-
- assert(s->adb_data_out_index < sizeof(s->adb_data_out) - 1);
-
- s->adb_data_out[s->adb_data_out_index++] = data;
- qemu_irq_raise(s->adb_data_ready);
- return 1;
}
-static int adb_via_receive(MacVIAState *s, int state, uint8_t *data)
+static void adb_via_send(MacVIAState *s, int state, uint8_t data)
{
+ MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1);
+ MOS6522State *ms = MOS6522(v1s);
+ ADBBusState *adb_bus = &s->adb_bus;
+ uint16_t autopoll_mask;
+
switch (state) {
case ADB_STATE_NEW:
- return 0;
-
- case ADB_STATE_EVEN:
- if (s->adb_data_in_size <= 0) {
- qemu_irq_raise(s->adb_data_ready);
- return 0;
- }
-
- if (s->adb_data_in_index >= s->adb_data_in_size) {
- *data = 0;
- qemu_irq_raise(s->adb_data_ready);
- return 1;
- }
-
- if ((s->adb_data_in_index & 1) == 0) {
- return 0;
+ /*
+ * Command byte: vADBInt tells host autopoll data already present
+ * in VIA shift register and ADB transceiver
+ */
+ adb_autopoll_block(adb_bus);
+
+ if (adb_bus->status & ADB_STATUS_POLLREPLY) {
+ /* Tell the host the existing data is from autopoll */
+ ms->b &= ~VIA1B_vADBInt;
+ } else {
+ ms->b |= VIA1B_vADBInt;
+ s->adb_data_out_index = 0;
+ s->adb_data_out[s->adb_data_out_index++] = data;
}
+ trace_via1_adb_send(" NEW", data, (ms->b & VIA1B_vADBInt) ? "+" : "-");
+ qemu_irq_raise(s->adb_data_ready);
break;
+ case ADB_STATE_EVEN:
case ADB_STATE_ODD:
- if (s->adb_data_in_size <= 0) {
- qemu_irq_raise(s->adb_data_ready);
- return 0;
- }
-
- if (s->adb_data_in_index >= s->adb_data_in_size) {
- *data = 0;
- qemu_irq_raise(s->adb_data_ready);
- return 1;
- }
-
- if (s->adb_data_in_index & 1) {
- return 0;
- }
+ ms->b |= VIA1B_vADBInt;
+ s->adb_data_out[s->adb_data_out_index++] = data;
+ trace_via1_adb_send(state == ADB_STATE_EVEN ? "EVEN" : " ODD",
+ data, (ms->b & VIA1B_vADBInt) ? "+" : "-");
+ qemu_irq_raise(s->adb_data_ready);
break;
case ADB_STATE_IDLE:
- if (s->adb_data_out_index == 0) {
- return 0;
- }
+ return;
+ }
- s->adb_data_in_size = adb_request(&s->adb_bus, s->adb_data_in,
+ /* If the command is complete, execute it */
+ if (s->adb_data_out_index == adb_via_send_len(s->adb_data_out[0])) {
+ s->adb_data_in_size = adb_request(adb_bus, s->adb_data_in,
s->adb_data_out,
s->adb_data_out_index);
- s->adb_data_out_index = 0;
s->adb_data_in_index = 0;
- if (s->adb_data_in_size < 0) {
- *data = 0xff;
- qemu_irq_raise(s->adb_data_ready);
- return -1;
- }
- if (s->adb_data_in_size == 0) {
- return 0;
+ if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) {
+ /*
+ * Bus timeout (but allow first EVEN and ODD byte to indicate
+ * timeout via vADBInt and SRQ status)
+ */
+ s->adb_data_in[0] = 0xff;
+ s->adb_data_in[1] = 0xff;
+ s->adb_data_in_size = 2;
}
- break;
- }
-
- assert(s->adb_data_in_index < sizeof(s->adb_data_in) - 1);
+ /*
+ * If last command is TALK, store it for use by autopoll and adjust
+ * the autopoll mask accordingly
+ */
+ if ((s->adb_data_out[0] & 0xc) == 0xc) {
+ s->adb_autopoll_cmd = s->adb_data_out[0];
- *data = s->adb_data_in[s->adb_data_in_index++];
- qemu_irq_raise(s->adb_data_ready);
- if (*data == 0xff || *data == 0) {
- return 0;
+ autopoll_mask = 1 << (s->adb_autopoll_cmd >> 4);
+ adb_set_autopoll_mask(adb_bus, autopoll_mask);
+ }
}
- return 1;
}
-static void via1_adb_update(MacVIAState *m)
+static void adb_via_receive(MacVIAState *s, int state, uint8_t *data)
{
- MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1);
- MOS6522State *s = MOS6522(v1s);
- int state;
- int ret;
+ MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&s->mos6522_via1);
+ MOS6522State *ms = MOS6522(v1s);
+ ADBBusState *adb_bus = &s->adb_bus;
+ uint16_t pending;
- state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
+ switch (state) {
+ case ADB_STATE_NEW:
+ ms->b |= VIA1B_vADBInt;
+ return;
- if (s->acr & VIA1ACR_vShiftOut) {
- /* output mode */
- ret = adb_via_send(m, state, s->sr);
- if (ret > 0) {
- s->b &= ~VIA1B_vADBInt;
+ case ADB_STATE_IDLE:
+ /*
+ * Since adb_request() will have already consumed the data from the
+ * device, we must detect this extra state change and re-inject the
+ * reponse as either a "fake" autopoll reply or bus timeout
+ * accordingly
+ */
+ if (s->adb_data_in_index == 0) {
+ if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) {
+ *data = 0xff;
+ ms->b |= VIA1B_vADBInt;
+ qemu_irq_raise(s->adb_data_ready);
+ } else if (s->adb_data_in_size > 0) {
+ adb_bus->status = ADB_STATUS_POLLREPLY;
+ *data = s->adb_autopoll_cmd;
+ ms->b &= ~VIA1B_vADBInt;
+ qemu_irq_raise(s->adb_data_ready);
+ }
} else {
- s->b |= VIA1B_vADBInt;
+ ms->b |= VIA1B_vADBInt;
+ adb_autopoll_unblock(adb_bus);
}
- } else {
- /* input mode */
- ret = adb_via_receive(m, state, &s->sr);
- if (ret > 0 && s->sr != 0xff) {
- s->b &= ~VIA1B_vADBInt;
- } else {
- s->b |= VIA1B_vADBInt;
+
+ trace_via1_adb_receive("IDLE", *data,
+ (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status,
+ s->adb_data_in_index, s->adb_data_in_size);
+
+ break;
+
+ case ADB_STATE_EVEN:
+ case ADB_STATE_ODD:
+ switch (s->adb_data_in_index) {
+ case 0:
+ /* First EVEN byte: vADBInt indicates bus timeout */
+ trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD",
+ *data, (ms->b & VIA1B_vADBInt) ? "+" : "-",
+ adb_bus->status, s->adb_data_in_index,
+ s->adb_data_in_size);
+
+ *data = s->adb_data_in[s->adb_data_in_index++];
+ if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) {
+ ms->b &= ~VIA1B_vADBInt;
+ } else {
+ ms->b |= VIA1B_vADBInt;
+ }
+ break;
+
+ case 1:
+ /* First ODD byte: vADBInt indicates SRQ */
+ trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD",
+ *data, (ms->b & VIA1B_vADBInt) ? "+" : "-",
+ adb_bus->status, s->adb_data_in_index,
+ s->adb_data_in_size);
+
+ *data = s->adb_data_in[s->adb_data_in_index++];
+ pending = adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> 4));
+ if (pending) {
+ ms->b &= ~VIA1B_vADBInt;
+ } else {
+ ms->b |= VIA1B_vADBInt;
+ }
+ break;
+
+ default:
+ /*
+ * Otherwise vADBInt indicates end of data. Note that Linux
+ * specifically checks for the sequence 0x0 0xff to confirm the
+ * end of the poll reply, so provide these extra bytes below to
+ * keep it happy
+ */
+ trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD",
+ *data, (ms->b & VIA1B_vADBInt) ? "+" : "-",
+ adb_bus->status, s->adb_data_in_index,
+ s->adb_data_in_size);
+
+ if (s->adb_data_in_index < s->adb_data_in_size) {
+ /* Next data byte */
+ *data = s->adb_data_in[s->adb_data_in_index++];
+ ms->b |= VIA1B_vADBInt;
+ } else if (s->adb_data_in_index == s->adb_data_in_size) {
+ if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) {
+ /* Bus timeout (no more data) */
+ *data = 0xff;
+ } else {
+ /* Return 0x0 after reply */
+ *data = 0;
+ }
+ s->adb_data_in_index++;
+ ms->b &= ~VIA1B_vADBInt;
+ } else {
+ /* Bus timeout (no more data) */
+ *data = 0xff;
+ ms->b &= ~VIA1B_vADBInt;
+ adb_bus->status = 0;
+ adb_autopoll_unblock(adb_bus);
+ }
+ break;
}
+
+ qemu_irq_raise(s->adb_data_ready);
+ break;
}
}
-static void via_adb_poll(void *opaque)
+static void via1_adb_update(MacVIAState *m)
{
- MacVIAState *m = opaque;
MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(&m->mos6522_via1);
MOS6522State *s = MOS6522(v1s);
- int state;
+ int oldstate, state;
- if (s->b & VIA1B_vADBInt) {
- state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
- if (adb_via_poll(m, state, &s->sr)) {
- s->b &= ~VIA1B_vADBInt;
+ oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
+ state = (s->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
+
+ if (state != oldstate) {
+ if (s->acr & VIA1ACR_vShiftOut) {
+ /* output mode */
+ adb_via_send(m, state, s->sr);
+ } else {
+ /* input mode */
+ adb_via_receive(m, state, &s->sr);
}
}
-
- timer_mod(m->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ));
}
static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size)
@@ -802,11 +933,21 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque);
+ MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1);
MOS6522State *ms = MOS6522(v1s);
addr = (addr >> 9) & 0xf;
mos6522_write(ms, addr, val, size);
+ switch (addr) {
+ case VIA_REG_B:
+ via1_rtc_update(m);
+ via1_adb_update(m);
+
+ v1s->last_b = ms->b;
+ break;
+ }
+
via1_one_second_update(v1s);
via1_VBL_update(v1s);
}
@@ -854,10 +995,9 @@ static void mac_via_reset(DeviceState *dev)
{
MacVIAState *m = MAC_VIA(dev);
MOS6522Q800VIA1State *v1s = &m->mos6522_via1;
+ ADBBusState *adb_bus = &m->adb_bus;
- timer_mod(m->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (NANOSECONDS_PER_SECOND / VIA_ADB_POLL_FREQ));
+ adb_set_autopoll_enabled(adb_bus, true);
timer_del(v1s->VBL_timer);
v1s->next_VBL = 0;
@@ -872,6 +1012,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp)
{
MacVIAState *m = MAC_VIA(dev);
MOS6522State *ms;
+ ADBBusState *adb_bus = &m->adb_bus;
struct tm tm;
int ret;
@@ -907,7 +1048,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp)
qemu_get_timedate(&tm, 0);
m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
- m->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via_adb_poll, m);
+ adb_register_autopoll_callback(adb_bus, adb_via_poll, m);
m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq",
VIA1_IRQ_ADB_READY_BIT);
@@ -980,8 +1121,8 @@ static int mac_via_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_mac_via = {
.name = "mac-via",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.post_load = mac_via_post_load,
.fields = (VMStateField[]) {
/* VIAs */
@@ -1005,12 +1146,12 @@ static const VMStateDescription vmstate_mac_via = {
VMSTATE_INT32(wprotect, MacVIAState),
VMSTATE_INT32(alt, MacVIAState),
/* ADB */
- VMSTATE_TIMER_PTR(adb_poll_timer, MacVIAState),
VMSTATE_INT32(adb_data_in_size, MacVIAState),
VMSTATE_INT32(adb_data_in_index, MacVIAState),
VMSTATE_INT32(adb_data_out_index, MacVIAState),
VMSTATE_BUFFER(adb_data_in, MacVIAState),
VMSTATE_BUFFER(adb_data_out, MacVIAState),
+ VMSTATE_UINT8(adb_autopoll_cmd, MacVIAState),
VMSTATE_END_OF_LIST()
}
};
@@ -1039,18 +1180,6 @@ static TypeInfo mac_via_info = {
};
/* VIA 1 */
-static void mos6522_q800_via1_portB_write(MOS6522State *s)
-{
- MOS6522Q800VIA1State *v1s = container_of(s, MOS6522Q800VIA1State,
- parent_obj);
- MacVIAState *m = container_of(v1s, MacVIAState, mos6522_via1);
-
- via1_rtc_update(m);
- via1_adb_update(m);
-
- v1s->last_b = s->b;
-}
-
static void mos6522_q800_via1_reset(DeviceState *dev)
{
MOS6522State *ms = MOS6522(dev);
@@ -1073,10 +1202,8 @@ static void mos6522_q800_via1_init(Object *obj)
static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc);
dc->reset = mos6522_q800_via1_reset;
- mdc->portB_write = mos6522_q800_via1_portB_write;
}
static const TypeInfo mos6522_q800_via1_type_info = {
diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c
index 47aa3b0552..5bbc7770fa 100644
--- a/hw/misc/macio/cuda.c
+++ b/hw/misc/macio/cuda.c
@@ -116,6 +116,7 @@ static void cuda_update(CUDAState *s)
{
MOS6522CUDAState *mcs = &s->mos6522_cuda;
MOS6522State *ms = MOS6522(mcs);
+ ADBBusState *adb_bus = &s->adb_bus;
int packet_received, len;
packet_received = 0;
@@ -126,6 +127,9 @@ static void cuda_update(CUDAState *s)
/* data output */
if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
if (s->data_out_index < sizeof(s->data_out)) {
+ if (s->data_out_index == 0) {
+ adb_autopoll_block(adb_bus);
+ }
trace_cuda_data_send(ms->sr);
s->data_out[s->data_out_index++] = ms->sr;
cuda_delay_set_sr_int(s);
@@ -140,6 +144,7 @@ static void cuda_update(CUDAState *s)
/* indicate end of transfer */
if (s->data_in_index >= s->data_in_size) {
ms->b = (ms->b | TREQ);
+ adb_autopoll_unblock(adb_bus);
}
cuda_delay_set_sr_int(s);
}
@@ -201,17 +206,16 @@ static void cuda_send_packet_to_host(CUDAState *s,
static void cuda_adb_poll(void *opaque)
{
CUDAState *s = opaque;
+ ADBBusState *adb_bus = &s->adb_bus;
uint8_t obuf[ADB_MAX_OUT_LEN + 2];
int olen;
- olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask);
+ olen = adb_poll(adb_bus, obuf + 2, adb_bus->autopoll_mask);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x40; /* polled data */
cuda_send_packet_to_host(s, obuf, olen + 2);
}
- timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
}
/* description of commands */
@@ -227,23 +231,16 @@ static bool cuda_cmd_autopoll(CUDAState *s,
const uint8_t *in_data, int in_len,
uint8_t *out_data, int *out_len)
{
- int autopoll;
+ ADBBusState *adb_bus = &s->adb_bus;
+ bool autopoll;
if (in_len != 1) {
return false;
}
- autopoll = (in_data[0] != 0);
- if (autopoll != s->autopoll) {
- s->autopoll = autopoll;
- if (autopoll) {
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
- } else {
- timer_del(s->adb_poll_timer);
- }
- }
+ autopoll = (in_data[0] != 0) ? true : false;
+
+ adb_set_autopoll_enabled(adb_bus, autopoll);
return true;
}
@@ -251,6 +248,8 @@ static bool cuda_cmd_set_autorate(CUDAState *s,
const uint8_t *in_data, int in_len,
uint8_t *out_data, int *out_len)
{
+ ADBBusState *adb_bus = &s->adb_bus;
+
if (in_len != 1) {
return false;
}
@@ -261,12 +260,7 @@ static bool cuda_cmd_set_autorate(CUDAState *s,
return false;
}
- s->autopoll_rate_ms = in_data[0];
- if (s->autopoll) {
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms)));
- }
+ adb_set_autopoll_rate_ms(adb_bus, in_data[0]);
return true;
}
@@ -274,11 +268,16 @@ static bool cuda_cmd_set_device_list(CUDAState *s,
const uint8_t *in_data, int in_len,
uint8_t *out_data, int *out_len)
{
+ ADBBusState *adb_bus = &s->adb_bus;
+ uint16_t mask;
+
if (in_len != 2) {
return false;
}
- s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
+ mask = (((uint16_t)in_data[0]) << 8) | in_data[1];
+
+ adb_set_autopoll_mask(adb_bus, mask);
return true;
}
@@ -489,8 +488,8 @@ static const MemoryRegionOps mos6522_cuda_ops = {
static const VMStateDescription vmstate_cuda = {
.name = "cuda",
- .version_id = 5,
- .minimum_version_id = 5,
+ .version_id = 6,
+ .minimum_version_id = 6,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522,
MOS6522State),
@@ -499,13 +498,9 @@ static const VMStateDescription vmstate_cuda = {
VMSTATE_INT32(data_in_size, CUDAState),
VMSTATE_INT32(data_in_index, CUDAState),
VMSTATE_INT32(data_out_index, CUDAState),
- VMSTATE_UINT8(autopoll, CUDAState),
- VMSTATE_UINT8(autopoll_rate_ms, CUDAState),
- VMSTATE_UINT16(adb_poll_mask, CUDAState),
VMSTATE_BUFFER(data_in, CUDAState),
VMSTATE_BUFFER(data_out, CUDAState),
VMSTATE_UINT32(tick_offset, CUDAState),
- VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState),
VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState),
VMSTATE_END_OF_LIST()
}
@@ -514,11 +509,13 @@ static const VMStateDescription vmstate_cuda = {
static void cuda_reset(DeviceState *dev)
{
CUDAState *s = CUDA(dev);
+ ADBBusState *adb_bus = &s->adb_bus;
s->data_in_size = 0;
s->data_in_index = 0;
s->data_out_index = 0;
- s->autopoll = 0;
+
+ adb_set_autopoll_enabled(adb_bus, false);
}
static void cuda_realize(DeviceState *dev, Error **errp)
@@ -526,6 +523,7 @@ static void cuda_realize(DeviceState *dev, Error **errp)
CUDAState *s = CUDA(dev);
Error *err = NULL;
SysBusDevice *sbd;
+ ADBBusState *adb_bus = &s->adb_bus;
struct tm tm;
sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_cuda), &err);
@@ -544,9 +542,7 @@ static void cuda_realize(DeviceState *dev, Error **errp)
s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
s->sr_delay_ns = 20 * SCALE_US;
- s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s);
- s->adb_poll_mask = 0xffff;
- s->autopoll_rate_ms = 20;
+ adb_register_autopoll_callback(adb_bus, cuda_adb_poll, s);
}
static void cuda_init(Object *obj)
diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c
index 41b626c46c..598d8e7517 100644
--- a/hw/misc/macio/pmu.c
+++ b/hw/misc/macio/pmu.c
@@ -92,10 +92,11 @@ static void pmu_update_extirq(PMUState *s)
static void pmu_adb_poll(void *opaque)
{
PMUState *s = opaque;
+ ADBBusState *adb_bus = &s->adb_bus;
int olen;
if (!(s->intbits & PMU_INT_ADB)) {
- olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask);
+ olen = adb_poll(adb_bus, s->adb_reply, adb_bus->autopoll_mask);
trace_pmu_adb_poll(olen);
if (olen > 0) {
@@ -104,9 +105,6 @@ static void pmu_adb_poll(void *opaque)
pmu_update_extirq(s);
}
}
-
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30);
}
static void pmu_one_sec_timer(void *opaque)
@@ -173,18 +171,15 @@ static void pmu_cmd_set_int_mask(PMUState *s,
static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask)
{
- trace_pmu_cmd_set_adb_autopoll(mask);
+ ADBBusState *adb_bus = &s->adb_bus;
- if (s->autopoll_mask == mask) {
- return;
- }
+ trace_pmu_cmd_set_adb_autopoll(mask);
- s->autopoll_mask = mask;
if (mask) {
- timer_mod(s->adb_poll_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30);
+ adb_set_autopoll_mask(adb_bus, mask);
+ adb_set_autopoll_enabled(adb_bus, true);
} else {
- timer_del(s->adb_poll_timer);
+ adb_set_autopoll_enabled(adb_bus, false);
}
}
@@ -267,6 +262,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s,
const uint8_t *in_data, uint8_t in_len,
uint8_t *out_data, uint8_t *out_len)
{
+ ADBBusState *adb_bus = &s->adb_bus;
+
if (in_len != 0) {
qemu_log_mask(LOG_GUEST_ERROR,
"PMU: ADB POLL OFF command, invalid len: %d want: 0\n",
@@ -274,9 +271,8 @@ static void pmu_cmd_adb_poll_off(PMUState *s,
return;
}
- if (s->has_adb && s->autopoll_mask) {
- timer_del(s->adb_poll_timer);
- s->autopoll_mask = false;
+ if (s->has_adb) {
+ adb_set_autopoll_enabled(adb_bus, false);
}
}
@@ -521,6 +517,7 @@ static void pmu_update(PMUState *s)
{
MOS6522PMUState *mps = &s->mos6522_pmu;
MOS6522State *ms = MOS6522(mps);
+ ADBBusState *adb_bus = &s->adb_bus;
/* Only react to changes in reg B */
if (ms->b == s->last_b) {
@@ -582,6 +579,7 @@ static void pmu_update(PMUState *s)
s->cmd_rsp_pos = 0;
s->cmd_state = pmu_state_cmd;
+ adb_autopoll_block(adb_bus);
trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen);
break;
@@ -640,6 +638,7 @@ static void pmu_update(PMUState *s)
if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) {
trace_pmu_debug_protocol_cmd_resp_complete(ms->ier);
+ adb_autopoll_unblock(adb_bus);
s->cmd_state = pmu_state_idle;
}
}
@@ -684,12 +683,10 @@ static bool pmu_adb_state_needed(void *opaque)
static const VMStateDescription vmstate_pmu_adb = {
.name = "pmu/adb",
- .version_id = 0,
- .minimum_version_id = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
.needed = pmu_adb_state_needed,
.fields = (VMStateField[]) {
- VMSTATE_UINT16(adb_poll_mask, PMUState),
- VMSTATE_TIMER_PTR(adb_poll_timer, PMUState),
VMSTATE_UINT8(adb_reply_size, PMUState),
VMSTATE_BUFFER(adb_reply, PMUState),
VMSTATE_END_OF_LIST()
@@ -698,8 +695,8 @@ static const VMStateDescription vmstate_pmu_adb = {
static const VMStateDescription vmstate_pmu = {
.name = "pmu",
- .version_id = 0,
- .minimum_version_id = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522,
MOS6522State),
@@ -714,8 +711,6 @@ static const VMStateDescription vmstate_pmu = {
VMSTATE_BUFFER(cmd_rsp, PMUState),
VMSTATE_UINT8(intbits, PMUState),
VMSTATE_UINT8(intmask, PMUState),
- VMSTATE_UINT8(autopoll_rate_ms, PMUState),
- VMSTATE_UINT8(autopoll_mask, PMUState),
VMSTATE_UINT32(tick_offset, PMUState),
VMSTATE_TIMER_PTR(one_sec_timer, PMUState),
VMSTATE_INT64(one_sec_target, PMUState),
@@ -735,7 +730,6 @@ static void pmu_reset(DeviceState *dev)
s->intbits = 0;
s->cmd_state = pmu_state_idle;
- s->autopoll_mask = 0;
}
static void pmu_realize(DeviceState *dev, Error **errp)
@@ -743,6 +737,7 @@ static void pmu_realize(DeviceState *dev, Error **errp)
PMUState *s = VIA_PMU(dev);
Error *err = NULL;
SysBusDevice *sbd;
+ ADBBusState *adb_bus = &s->adb_bus;
struct tm tm;
sysbus_realize(SYS_BUS_DEVICE(&s->mos6522_pmu), &err);
@@ -764,9 +759,7 @@ static void pmu_realize(DeviceState *dev, Error **errp)
if (s->has_adb) {
qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS,
dev, "adb.0");
- s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s);
- s->adb_poll_mask = 0xffff;
- s->autopoll_rate_ms = 20;
+ adb_register_autopoll_callback(adb_bus, pmu_adb_poll, s);
}
}
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 5561746866..68a6d9f2ab 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -202,6 +202,9 @@ via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x"
via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x"
via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x"
via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x"
+via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s"
+via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d"
+via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d"
# grlib_ahb_apb_pnp.c
grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x"
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 5f3a028e6a..828c5992ae 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -404,11 +404,9 @@ static void ppc_core99_init(MachineState *machine)
adb_bus = qdev_get_child_bus(dev, "adb.0");
dev = qdev_new(TYPE_ADB_KEYBOARD);
- qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true);
qdev_realize_and_unref(dev, adb_bus, &error_fatal);
dev = qdev_new(TYPE_ADB_MOUSE);
- qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true);
qdev_realize_and_unref(dev, adb_bus, &error_fatal);
}
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 8bd03f3b10..643098ad5f 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -21,6 +21,7 @@
#include "qemu-common.h"
#include "qemu/units.h"
#include "qapi/error.h"
+#include "sysemu/qtest.h"
#include "sysemu/sysemu.h"
#include "sysemu/numa.h"
#include "sysemu/reset.h"
@@ -587,9 +588,11 @@ static void pnv_reset(MachineState *machine)
bmc = pnv_bmc_find(&error_fatal);
if (!pnv->bmc) {
if (!bmc) {
- warn_report("machine has no BMC device. Use '-device "
- "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' "
- "to define one");
+ if (!qtest_enabled()) {
+ warn_report("machine has no BMC device. Use '-device "
+ "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' "
+ "to define one");
+ }
} else {
pnv_bmc_set_pnor(bmc, pnv->pnor);
pnv->bmc = bmc;
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
index efdc0dbbcf..0c2bc8e06e 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -248,23 +248,18 @@ SpaprCapPossible cap_cfpc_possible = {
static void cap_safe_cache_apply(SpaprMachineState *spapr, uint8_t val,
Error **errp)
{
- Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_cache();
if (tcg_enabled() && val) {
/* TCG only supports broken, allow other values and print a warning */
- error_setg(&local_err,
- "TCG doesn't support requested feature, cap-cfpc=%s",
- cap_cfpc_possible.vals[val]);
+ warn_report("TCG doesn't support requested feature, cap-cfpc=%s",
+ cap_cfpc_possible.vals[val]);
} else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe cache capability level not supported by kvm,"
" try appending -machine cap-cfpc=%s",
cap_cfpc_possible.vals[kvm_val]);
}
-
- if (local_err != NULL)
- warn_report_err(local_err);
}
SpaprCapPossible cap_sbbc_possible = {
@@ -277,23 +272,18 @@ SpaprCapPossible cap_sbbc_possible = {
static void cap_safe_bounds_check_apply(SpaprMachineState *spapr, uint8_t val,
Error **errp)
{
- Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check();
if (tcg_enabled() && val) {
/* TCG only supports broken, allow other values and print a warning */
- error_setg(&local_err,
- "TCG doesn't support requested feature, cap-sbbc=%s",
- cap_sbbc_possible.vals[val]);
+ warn_report("TCG doesn't support requested feature, cap-sbbc=%s",
+ cap_sbbc_possible.vals[val]);
} else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe bounds check capability level not supported by kvm,"
" try appending -machine cap-sbbc=%s",
cap_sbbc_possible.vals[kvm_val]);
}
-
- if (local_err != NULL)
- warn_report_err(local_err);
}
SpaprCapPossible cap_ibs_possible = {
@@ -309,24 +299,18 @@ SpaprCapPossible cap_ibs_possible = {
static void cap_safe_indirect_branch_apply(SpaprMachineState *spapr,
uint8_t val, Error **errp)
{
- Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch();
if (tcg_enabled() && val) {
/* TCG only supports broken, allow other values and print a warning */
- error_setg(&local_err,
- "TCG doesn't support requested feature, cap-ibs=%s",
- cap_ibs_possible.vals[val]);
+ warn_report("TCG doesn't support requested feature, cap-ibs=%s",
+ cap_ibs_possible.vals[val]);
} else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe indirect branch capability level not supported by kvm,"
" try appending -machine cap-ibs=%s",
cap_ibs_possible.vals[kvm_val]);
}
-
- if (local_err != NULL) {
- warn_report_err(local_err);
- }
}
#define VALUE_DESC_TRISTATE " (broken, workaround, fixed)"
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 4318ed9638..731080d989 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -420,7 +420,7 @@ static void spapr_vio_busdev_reset(DeviceState *qdev)
}
/*
- * The register property of a VIO device is defined in livirt using
+ * The register property of a VIO device is defined in libvirt using
* 0x1000 as a base register number plus a 0x1000 increment. For the
* VIO tty device, the base number is changed to 0x30000000. QEMU uses
* a base register number of 0x71000000 and then a simple increment.
@@ -450,7 +450,7 @@ static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg)
} else if (reg >= 0x30000000) {
/*
- * VIO tty devices register values, when allocated by livirt,
+ * VIO tty devices register values, when allocated by libvirt,
* are mapped in range [0xf0 - 0xff], gives us a maximum of 16
* vtys.
*/
@@ -459,7 +459,7 @@ static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg)
} else {
/*
* Other VIO devices register values, when allocated by
- * livirt, should be mapped in range [0x00 - 0xef]. Conflicts
+ * libvirt, should be mapped in range [0x00 - 0xef]. Conflicts
* will be detected when IRQ is claimed.
*/
irq = (reg >> 12) & 0xff;
diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h
index b7b32e2b16..bb75a7b1e3 100644
--- a/include/hw/input/adb.h
+++ b/include/hw/input/adb.h
@@ -39,6 +39,8 @@ typedef struct ADBDevice ADBDevice;
typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
const uint8_t *buf, int len);
+typedef bool ADBDeviceHasData(ADBDevice *d);
+
#define TYPE_ADB_DEVICE "adb-device"
#define ADB_DEVICE(obj) OBJECT_CHECK(ADBDevice, (obj), TYPE_ADB_DEVICE)
@@ -49,7 +51,6 @@ struct ADBDevice {
int devaddr;
int handler;
- bool disable_direct_reg3_writes;
};
#define ADB_DEVICE_CLASS(cls) \
@@ -63,25 +64,48 @@ typedef struct ADBDeviceClass {
/*< public >*/
ADBDeviceRequest *devreq;
+ ADBDeviceHasData *devhasdata;
} ADBDeviceClass;
#define TYPE_ADB_BUS "apple-desktop-bus"
#define ADB_BUS(obj) OBJECT_CHECK(ADBBusState, (obj), TYPE_ADB_BUS)
+#define ADB_STATUS_BUSTIMEOUT 0x1
+#define ADB_STATUS_POLLREPLY 0x2
+
struct ADBBusState {
/*< private >*/
BusState parent_obj;
/*< public >*/
ADBDevice *devices[MAX_ADB_DEVICES];
+ uint16_t pending;
int nb_devices;
int poll_index;
+ uint8_t status;
+
+ QEMUTimer *autopoll_timer;
+ bool autopoll_enabled;
+ bool autopoll_blocked;
+ uint8_t autopoll_rate_ms;
+ uint16_t autopoll_mask;
+ void (*autopoll_cb)(void *opaque);
+ void *autopoll_cb_opaque;
};
int adb_request(ADBBusState *s, uint8_t *buf_out,
const uint8_t *buf, int len);
int adb_poll(ADBBusState *s, uint8_t *buf_out, uint16_t poll_mask);
+void adb_autopoll_block(ADBBusState *s);
+void adb_autopoll_unblock(ADBBusState *s);
+
+void adb_set_autopoll_enabled(ADBBusState *s, bool enabled);
+void adb_set_autopoll_rate_ms(ADBBusState *s, int rate_ms);
+void adb_set_autopoll_mask(ADBBusState *s, uint16_t mask);
+void adb_register_autopoll_callback(ADBBusState *s, void (*cb)(void *opaque),
+ void *opaque);
+
#define TYPE_ADB_KEYBOARD "adb-keyboard"
#define TYPE_ADB_MOUSE "adb-mouse"
diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h
index e74f85be0f..0be05d649b 100644
--- a/include/hw/misc/mac_via.h
+++ b/include/hw/misc/mac_via.h
@@ -106,13 +106,13 @@ typedef struct MacVIAState {
/* ADB */
ADBBusState adb_bus;
- QEMUTimer *adb_poll_timer;
qemu_irq adb_data_ready;
int adb_data_in_size;
int adb_data_in_index;
int adb_data_out_index;
uint8_t adb_data_in[128];
uint8_t adb_data_out[16];
+ uint8_t adb_autopoll_cmd;
} MacVIAState;
#endif
diff --git a/include/hw/misc/macio/cuda.h b/include/hw/misc/macio/cuda.h
index 5768075ac5..a8cf0be1ec 100644
--- a/include/hw/misc/macio/cuda.h
+++ b/include/hw/misc/macio/cuda.h
@@ -95,12 +95,8 @@ typedef struct CUDAState {
int data_out_index;
qemu_irq irq;
- uint16_t adb_poll_mask;
- uint8_t autopoll_rate_ms;
- uint8_t autopoll;
uint8_t data_in[128];
uint8_t data_out[16];
- QEMUTimer *adb_poll_timer;
} CUDAState;
#endif /* CUDA_H */
diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h
index 7ef83dee4c..72f75612b6 100644
--- a/include/hw/misc/macio/pmu.h
+++ b/include/hw/misc/macio/pmu.h
@@ -218,10 +218,6 @@ typedef struct PMUState {
/* ADB */
bool has_adb;
ADBBusState adb_bus;
- uint16_t adb_poll_mask;
- uint8_t autopoll_rate_ms;
- uint8_t autopoll_mask;
- QEMUTimer *adb_poll_timer;
uint8_t adb_reply_size;
uint8_t adb_reply[ADB_MAX_OUT_LEN];
diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h
index 09f243600c..7879692825 100644
--- a/include/hw/ppc/xive_regs.h
+++ b/include/hw/ppc/xive_regs.h
@@ -71,7 +71,7 @@
* QW word 2 contains the valid bit at the top and other fields
* depending on the QW.
*/
-#define TM_WORD2 0x8
+#define TM_WORD2 0x8
#define TM_QW0W2_VU PPC_BIT32(0)
#define TM_QW0W2_LOGIC_SERV PPC_BITMASK32(1, 31) /* XX 2,31 ? */
#define TM_QW1W2_VO PPC_BIT32(0)
diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h
index bd6b0468e1..1da148552f 100644
--- a/include/qemu/coroutine_int.h
+++ b/include/qemu/coroutine_int.h
@@ -28,6 +28,11 @@
#include "qemu/queue.h"
#include "qemu/coroutine.h"
+#ifdef CONFIG_SAFESTACK
+/* Pointer to the unsafe stack, defined by the compiler */
+extern __thread void *__safestack_unsafe_stack_ptr;
+#endif
+
#define COROUTINE_STACK_SIZE (1 << 20)
typedef enum {
diff --git a/scripts/minikconf.py b/scripts/minikconf.py
index 90b99517c1..bcd91015d3 100755
--- a/scripts/minikconf.py
+++ b/scripts/minikconf.py
@@ -402,7 +402,7 @@ class KconfigParser:
if incl_abs_fname in self.data.previously_included:
return
try:
- fp = open(incl_abs_fname, 'r')
+ fp = open(incl_abs_fname, 'rt', encoding='utf-8')
except IOError as e:
raise KconfigParserError(self,
'%s: %s' % (e.strerror, include))
@@ -696,7 +696,7 @@ if __name__ == '__main__':
parser.do_assignment(name, value == 'y')
external_vars.add(name[7:])
else:
- fp = open(arg, 'r')
+ fp = open(arg, 'rt', encoding='utf-8')
parser.parse_file(fp)
fp.close()
@@ -705,7 +705,7 @@ if __name__ == '__main__':
if key not in external_vars and config[key]:
print ('CONFIG_%s=y' % key)
- deps = open(argv[2], 'w')
+ deps = open(argv[2], 'wt', encoding='utf-8')
for fname in data.previously_included:
print ('%s: %s' % (argv[1], fname), file=deps)
deps.close()
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index a40888411c..49212bfd90 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -9086,11 +9086,6 @@ static void init_proc_POWER10(CPUPPCState *env)
gen_spr_power8_rpr(env);
gen_spr_power9_mmu(env);
- /* POWER9 Specific registers */
- spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL,
- spr_read_generic, spr_write_generic,
- KVM_REG_PPC_TIDR, 0);
-
/* FIXME: Filter fields properly based on privilege level */
spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL,
spr_read_generic, spr_write_generic,
diff --git a/tests/check-block.sh b/tests/check-block.sh
index ad320c21ba..8e29c868e5 100755
--- a/tests/check-block.sh
+++ b/tests/check-block.sh
@@ -21,7 +21,17 @@ if grep -q "CONFIG_GPROF=y" config-host.mak 2>/dev/null ; then
exit 0
fi
-if grep -q "CFLAGS.*-fsanitize" config-host.mak 2>/dev/null ; then
+# Disable tests with any sanitizer except for SafeStack
+CFLAGS=$( grep "CFLAGS.*-fsanitize" config-host.mak 2>/dev/null )
+SANITIZE_FLAGS=""
+#Remove all occurrencies of -fsanitize=safe-stack
+for i in ${CFLAGS}; do
+ if [ "${i}" != "-fsanitize=safe-stack" ]; then
+ SANITIZE_FLAGS="${SANITIZE_FLAGS} ${i}"
+ fi
+done
+if echo ${SANITIZE_FLAGS} | grep -q "\-fsanitize" 2>/dev/null; then
+ # Have a sanitize flag that is not allowed, stop
echo "Sanitizers are enabled ==> Not running the qemu-iotests."
exit 0
fi
diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c
index f6fc49a0e5..aade82afb8 100644
--- a/util/coroutine-sigaltstack.c
+++ b/util/coroutine-sigaltstack.c
@@ -30,6 +30,10 @@
#include "qemu-common.h"
#include "qemu/coroutine_int.h"
+#ifdef CONFIG_SAFESTACK
+#error "SafeStack is not compatible with code run in alternate signal stacks"
+#endif
+
typedef struct {
Coroutine base;
void *stack;
diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c
index 613f4c118e..f0b66320e1 100644
--- a/util/coroutine-ucontext.c
+++ b/util/coroutine-ucontext.c
@@ -45,6 +45,11 @@ typedef struct {
Coroutine base;
void *stack;
size_t stack_size;
+#ifdef CONFIG_SAFESTACK
+ /* Need an unsafe stack for each coroutine */
+ void *unsafe_stack;
+ size_t unsafe_stack_size;
+#endif
sigjmp_buf env;
void *tsan_co_fiber;
@@ -179,6 +184,10 @@ Coroutine *qemu_coroutine_new(void)
co = g_malloc0(sizeof(*co));
co->stack_size = COROUTINE_STACK_SIZE;
co->stack = qemu_alloc_stack(&co->stack_size);
+#ifdef CONFIG_SAFESTACK
+ co->unsafe_stack_size = COROUTINE_STACK_SIZE;
+ co->unsafe_stack = qemu_alloc_stack(&co->unsafe_stack_size);
+#endif
co->base.entry_arg = &old_env; /* stash away our jmp_buf */
uc.uc_link = &old_uc;
@@ -203,6 +212,22 @@ Coroutine *qemu_coroutine_new(void)
COROUTINE_YIELD,
&fake_stack_save,
co->stack, co->stack_size, co->tsan_co_fiber);
+
+#ifdef CONFIG_SAFESTACK
+ /*
+ * Before we swap the context, set the new unsafe stack
+ * The unsafe stack grows just like the normal stack, so start from
+ * the last usable location of the memory area.
+ * NOTE: we don't have to re-set the usp afterwards because we are
+ * coming back to this context through a siglongjmp.
+ * The compiler already wrapped the corresponding sigsetjmp call with
+ * code that saves the usp on the (safe) stack before the call, and
+ * restores it right after (which is where we return with siglongjmp).
+ */
+ void *usp = co->unsafe_stack + co->unsafe_stack_size;
+ __safestack_unsafe_stack_ptr = usp;
+#endif
+
swapcontext(&old_uc, &uc);
}
@@ -235,6 +260,9 @@ void qemu_coroutine_delete(Coroutine *co_)
#endif
qemu_free_stack(co->stack, co->stack_size);
+#ifdef CONFIG_SAFESTACK
+ qemu_free_stack(co->unsafe_stack, co->unsafe_stack_size);
+#endif
g_free(co);
}