diff --git a/Makefile.objs b/Makefile.objs
index 9d8851e..cba6c2b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -11,6 +11,12 @@ oslib-obj-$(CONFIG_WIN32) += oslib-win32.o
 oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
 
 #######################################################################
+# coroutines
+coroutine-obj-y = qemu-coroutine.o
+coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
+coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
+
+#######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
 block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o
@@ -67,6 +73,7 @@ common-obj-y += readline.o console.o cursor.o qemu-error.o
 common-obj-y += $(oslib-obj-y)
 common-obj-$(CONFIG_WIN32) += os-win32.o
 common-obj-$(CONFIG_POSIX) += os-posix.o
+common-obj-y += $(coroutine-obj-y)
 
 common-obj-y += tcg-runtime.o host-utils.o
 common-obj-y += irq.o ioport.o input.o
diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c
new file mode 100644
index 0000000..97f2b35
--- /dev/null
+++ b/coroutine-ucontext.c
@@ -0,0 +1,73 @@
+/*
+ * ucontext coroutine initialization code
+ *
+ * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2011  Kevin Wolf <kwolf@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
+#ifdef _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+#include <setjmp.h>
+#include <stdint.h>
+#include <ucontext.h>
+#include "qemu-coroutine-int.h"
+
+static Coroutine *new_coroutine;
+
+static void continuation_trampoline(void)
+{
+    Coroutine *co = new_coroutine;
+
+    /* Initialize longjmp environment and switch back to
+     * qemu_coroutine_init_env() in the old ucontext. */
+    if (!setjmp(co->env)) {
+        return;
+    }
+
+    while (true) {
+        co->entry(co->data);
+        if (!setjmp(co->env)) {
+            longjmp(co->caller->env, COROUTINE_TERMINATE);
+        }
+    }
+}
+
+int qemu_coroutine_init_env(Coroutine *co)
+{
+    ucontext_t old_uc, uc;
+
+    /* Create a new ucontext for switching to the coroutine stack and setting
+     * up a longjmp environment. */
+    if (getcontext(&uc) == -1) {
+        return -errno;
+    }
+
+    uc.uc_link = &old_uc;
+    uc.uc_stack.ss_sp = co->stack;
+    uc.uc_stack.ss_size = co->stack_size;
+    uc.uc_stack.ss_flags = 0;
+
+    new_coroutine = co;
+    makecontext(&uc, (void *)continuation_trampoline, 0);
+
+    /* Initialize the longjmp environment */
+    swapcontext(&old_uc, &uc);
+
+    return 0;
+}
diff --git a/coroutine-win32.c b/coroutine-win32.c
new file mode 100644
index 0000000..f4521c3
--- /dev/null
+++ b/coroutine-win32.c
@@ -0,0 +1,57 @@
+/*
+ * Win32 coroutine initialization code
+ *
+ * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.com>
+ *
+ * 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.
+ */
+
+#include "qemu-coroutine-int.h"
+
+static void __attribute__((used)) trampoline(Coroutine *co)
+{
+    if (!setjmp(co->env)) {
+        return;
+    }
+
+    while (true) {
+        co->entry(co->data);
+        if (!setjmp(co->env)) {
+            longjmp(co->caller->env, COROUTINE_TERMINATE);
+        }
+    }
+}
+
+int qemu_coroutine_init_env(Coroutine *co)
+{
+#ifdef __i386__
+    asm volatile(
+        "mov %%esp, %%ebx;"
+        "mov %0, %%esp;"
+        "pushl %1;"
+        "call _trampoline;"
+        "mov %%ebx, %%esp;"
+        : : "r" (co->stack + co->stack_size), "r" (co) : "ebx"
+    );
+#else
+    #error This host architecture is not supported for win32
+#endif
+
+    return 0;
+}
diff --git a/qemu-coroutine-int.h b/qemu-coroutine-int.h
new file mode 100644
index 0000000..c86dcc1
--- /dev/null
+++ b/qemu-coroutine-int.h
@@ -0,0 +1,57 @@
+/*
+ * Coroutine internals
+ *
+ * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.com>
+ *
+ * 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 QEMU_COROUTINE_INT_H
+#define QEMU_COROUTINE_INT_H
+
+#include <setjmp.h>
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "qemu-coroutine.h"
+
+enum {
+    /* setjmp() return values */
+    COROUTINE_YIELD = 1,
+    COROUTINE_TERMINATE = 2,
+};
+
+struct Coroutine {
+    bool initialized;
+    struct Coroutine *caller;
+
+    size_t stack_size;
+    char *stack;
+
+    /* Used to pass arguments/return values for coroutines */
+    void *data;
+    CoroutineEntry *entry;
+
+    QLIST_ENTRY(Coroutine) pool_next;
+
+    jmp_buf env;
+};
+
+int qemu_coroutine_init_env(Coroutine *co);
+
+#endif
diff --git a/qemu-coroutine.c b/qemu-coroutine.c
new file mode 100644
index 0000000..0927f58
--- /dev/null
+++ b/qemu-coroutine.c
@@ -0,0 +1,132 @@
+/*
+ * QEMU coroutines
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *  Kevin Wolf         <kwolf@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
+#ifdef _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+#include <setjmp.h>
+
+#include "trace.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-coroutine.h"
+#include "qemu-coroutine-int.h"
+
+static QLIST_HEAD(, Coroutine) pool = QLIST_HEAD_INITIALIZER(&pool);
+static __thread Coroutine leader;
+static __thread Coroutine *current;
+
+static void qemu_coroutine_terminate(Coroutine *coroutine)
+{
+    trace_qemu_coroutine_terminate(coroutine);
+    QLIST_INSERT_HEAD(&pool, coroutine, pool_next);
+    coroutine->caller = NULL;
+}
+
+static int coroutine_init(Coroutine *co)
+{
+    if (!co->initialized) {
+        co->initialized = true;
+        co->stack_size = 4 << 20;
+        co->stack = qemu_malloc(co->stack_size);
+    }
+
+    return qemu_coroutine_init_env(co);
+}
+
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
+{
+    Coroutine *coroutine;
+
+    coroutine = QLIST_FIRST(&pool);
+
+    if (coroutine) {
+        QLIST_REMOVE(coroutine, pool_next);
+    } else {
+        coroutine = qemu_mallocz(sizeof(*coroutine));
+    }
+
+    coroutine_init(coroutine);
+    coroutine->entry = entry;
+
+    return coroutine;
+}
+
+Coroutine * coroutine_fn qemu_coroutine_self(void)
+{
+    if (current == NULL) {
+        current = &leader;
+    }
+
+    return current;
+}
+
+bool qemu_in_coroutine(void)
+{
+    return (qemu_coroutine_self() != &leader);
+}
+
+static void *coroutine_swap(Coroutine *from, Coroutine *to, void *opaque)
+{
+    int ret;
+
+    to->data = opaque;
+
+    ret = setjmp(from->env);
+    switch (ret) {
+    case COROUTINE_YIELD:
+        return from->data;
+    case COROUTINE_TERMINATE:
+        current = to->caller;
+        qemu_coroutine_terminate(to);
+        return to->data;
+    default:
+        /* Switch to called coroutine */
+        current = to;
+        longjmp(to->env, COROUTINE_YIELD);
+        return NULL;
+    }
+}
+
+void qemu_coroutine_enter(Coroutine *coroutine, void *opaque)
+{
+    Coroutine *self = qemu_coroutine_self();
+
+    trace_qemu_coroutine_enter(self, coroutine, opaque);
+
+    if (coroutine->caller) {
+        fprintf(stderr, "Co-routine re-entered recursively\n");
+        abort();
+    }
+
+    coroutine->caller = self;
+    coroutine_swap(self, coroutine, opaque);
+}
+
+void * coroutine_fn qemu_coroutine_yield(void)
+{
+    Coroutine *self = qemu_coroutine_self();
+    Coroutine *to = self->caller;
+
+    trace_qemu_coroutine_yield(self, self->caller);
+
+    if (!to) {
+        fprintf(stderr, "Co-routine is yielding to no one\n");
+        abort();
+    }
+
+    self->caller = NULL;
+    return coroutine_swap(self, to, NULL);
+}
diff --git a/qemu-coroutine.h b/qemu-coroutine.h
new file mode 100644
index 0000000..1a19d18
--- /dev/null
+++ b/qemu-coroutine.h
@@ -0,0 +1,82 @@
+/*
+ * QEMU coroutine implementation
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COROUTINE_H
+#define QEMU_COROUTINE_H
+
+#include <stdbool.h>
+
+/**
+ * Mark a function that executes in coroutine context
+ *
+ * Functions that execute in coroutine context cannot be called directly from
+ * normal functions.  In the future it would be nice to enable compiler or
+ * static checker support for catching such errors.  This annotation might make
+ * it possible and in the meantime it serves as documentation.
+ *
+ * For example:
+ *
+ *   static void coroutine_fn foo(void) {
+ *       ....
+ *   }
+ */
+#define coroutine_fn
+
+typedef struct Coroutine Coroutine;
+
+/**
+ * Coroutine entry point
+ *
+ * When the coroutine is entered for the first time, opaque is passed in as an
+ * argument.
+ *
+ * When this function returns, the coroutine is destroyed automatically and
+ * execution continues in the caller who last entered the coroutine.
+ */
+typedef void coroutine_fn CoroutineEntry(void *opaque);
+
+/**
+ * Create a new coroutine
+ *
+ * Use qemu_coroutine_enter() to actually transfer control to the coroutine.
+ */
+Coroutine *qemu_coroutine_create(CoroutineEntry *entry);
+
+/**
+ * Transfer control to a coroutine
+ *
+ * The opaque argument is made available to the coroutine either as the entry
+ * function argument if this is the first time a new coroutine is entered, or
+ * as the return value from qemu_coroutine_yield().
+ */
+void qemu_coroutine_enter(Coroutine *coroutine, void *opaque);
+
+/**
+ * Transfer control back to a coroutine's caller
+ *
+ * The return value is the argument passed back in from the next
+ * qemu_coroutine_enter().
+ */
+void * coroutine_fn qemu_coroutine_yield(void);
+
+/**
+ * Get the currently executing coroutine
+ */
+Coroutine * coroutine_fn qemu_coroutine_self(void);
+
+/**
+ * Return whether or not currently inside a coroutine
+ */
+bool qemu_in_coroutine(void);
+
+#endif /* QEMU_COROUTINE_H */
diff --git a/trace-events b/trace-events
index 4f965e2..2d4db05 100644
--- a/trace-events
+++ b/trace-events
@@ -361,3 +361,8 @@ disable milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX"
 # hw/milkymist-vgafb.c
 disable milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
 disable milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
+
+# qemu-coroutine.c
+qemu_coroutine_enter(void *from, void *to, void *opaque) "from %p to %p opaque %p"
+qemu_coroutine_yield(void *from, void *to) "from %p to %p"
+qemu_coroutine_terminate(void *co) "self %p"
