Source('fenv.c')
 if env['USE_PNG']:
     Source('pngwriter.cc')
+Source('fiber.cc')
+GTest('fibertest', 'fibertest.cc', 'fiber.cc')
 Source('framebuffer.cc')
 Source('hostinfo.cc')
 Source('inet.cc')
 
--- /dev/null
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#include "base/fiber.hh"
+
+#include <cerrno>
+#include <cstring>
+
+#include "base/logging.hh"
+
+using namespace std;
+
+namespace
+{
+
+/*
+ * The PrimaryFiber class is a special case that attaches to the currently
+ * executing context. That makes handling the "primary" fiber, aka the one
+ * which most of gem5 is running under, no different than other Fibers.
+ */
+class PrimaryFiber : public Fiber
+{
+  public:
+    PrimaryFiber() : Fiber(nullptr, 0) { setStarted(); }
+    void main() { panic("PrimaryFiber main executed.\n"); }
+};
+
+PrimaryFiber _primaryFiber;
+
+// A pointer to whatever the currently executing Fiber is.
+Fiber *_currentFiber = &_primaryFiber;
+
+// A pointer to the Fiber which is currently being started/initialized.
+Fiber *startingFiber = nullptr;
+
+} // anonymous namespace
+
+void
+Fiber::entryTrampoline()
+{
+    startingFiber->start();
+}
+
+Fiber::Fiber(size_t stack_size) :
+    link(primaryFiber()),
+    stack(stack_size ? new uint8_t[stack_size] : nullptr),
+    stackSize(stack_size), started(false), _finished(false)
+{}
+
+Fiber::Fiber(Fiber *link, size_t stack_size) :
+    link(link), stack(stack_size ? new uint8_t[stack_size] : nullptr),
+    stackSize(stack_size), started(false), _finished(false)
+{}
+
+Fiber::~Fiber()
+{
+    panic_if(stack && _currentFiber == this, "Fiber stack is in use.");
+    delete [] stack;
+}
+
+void
+Fiber::createContext()
+{
+    // Set up a context for the new fiber, starting it in the trampoline.
+    getcontext(&ctx);
+    ctx.uc_stack.ss_sp = stack;
+    ctx.uc_stack.ss_size = stackSize;
+    ctx.uc_link = nullptr;
+    makecontext(&ctx, &entryTrampoline, 0);
+
+    // Swap to the new context so it can enter its start() function. It
+    // will then swap itself back out and return here.
+    startingFiber = this;
+    panic_if(!_currentFiber, "No active Fiber object.");
+    swapcontext(&_currentFiber->ctx, &ctx);
+
+    // The new context is now ready and about to call main().
+}
+
+void
+Fiber::start()
+{
+    // Avoid a dangling pointer.
+    startingFiber = nullptr;
+
+    setStarted();
+
+    // Swap back to the parent context which is still considered "current",
+    // now that we're ready to go.
+    int ret M5_VAR_USED = swapcontext(&ctx, &_currentFiber->ctx);
+    panic_if(ret == -1, strerror(errno));
+
+    // Call main() when we're been reactivated for the first time.
+    main();
+
+    // main has returned, so this Fiber has finished. Switch to the "link"
+    // Fiber.
+    _finished = true;
+    link->run();
+}
+
+void
+Fiber::run()
+{
+    panic_if(_finished, "Fiber has already run to completion.");
+
+    // If we're already running this fiber, we're done.
+    if (_currentFiber == this)
+        return;
+
+    if (!started)
+        createContext();
+
+    // Switch out of the current Fiber's context and this one's in.
+    Fiber *prev = _currentFiber;
+    Fiber *next = this;
+    _currentFiber = next;
+    swapcontext(&prev->ctx, &next->ctx);
+}
+
+Fiber *Fiber::currentFiber() { return _currentFiber; }
+Fiber *Fiber::primaryFiber() { return &_primaryFiber; }
 
--- /dev/null
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __BASE_FIBER_HH__
+#define __BASE_FIBER_HH__
+
+#include <ucontext.h>
+
+#include <cstddef>
+#include <cstdint>
+
+/**
+ * This class represents a fiber, which is a light weight sort of thread which
+ * is cooperatively scheduled and runs sequentially with other fibers, swapping
+ * in and out of a single actual thread of execution.
+ *
+ * To define your own threads, create a subclass of Fiber and override its
+ * main() function to do what you want your fiber to do. You can start it by
+ * calling its run() method which will stop your execution and start the other
+ * fiber in your place.
+ *
+ * If your main() function ends, that fiber will automatically switch to either
+ * the primary fiber, or to a particular fiber you specified at construction
+ * time, and your fiber is considered finished.
+ */
+
+class Fiber
+{
+  public:
+    const static size_t DefaultStackSize = 0x50000;
+
+    /// stack_size is the size of the stack available to this fiber.
+    /// link points to another fiber which will start executing when this
+    /// fiber's main function returns.
+    Fiber(size_t stack_size=DefaultStackSize);
+    Fiber(Fiber *link, size_t stack_size=DefaultStackSize);
+
+    virtual ~Fiber();
+
+    /// Start executing the fiber represented by this object. This function
+    /// will "return" when the current fiber is switched back to later on.
+    void run();
+
+    /// Returns whether the "main" function of this fiber has finished.
+    ///
+    bool finished() const { return _finished; };
+
+    /// Get a pointer to the current running Fiber.
+    ///
+    static Fiber *currentFiber();
+    /// Get a pointer to the primary Fiber.
+    /// This Fiber represents the thread of execution started by the OS, and
+    /// which has a Fiber attached to it after the fact.
+    static Fiber *primaryFiber();
+
+  protected:
+    /// This method is called when this fiber is first run. Override it to
+    /// give your fiber something to do. When main returns, the fiber will
+    /// mark itself as finished and switch to its link fiber.
+    virtual void main() = 0;
+
+    void setStarted() { started = true; }
+
+  private:
+    static void entryTrampoline();
+    void start();
+
+    ucontext_t ctx;
+    Fiber *link;
+
+    // The stack for this context, or a nullptr if allocated elsewhere.
+    uint8_t *stack;
+    size_t stackSize;
+
+    bool started;
+    bool _finished;
+    void createContext();
+};
+
+#endif // __BASE_FIBER_HH__
 
--- /dev/null
+/*
+ * Copyright 2018 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#include <gtest/gtest.h>
+
+#include <initializer_list>
+#include <iostream>
+#include <vector>
+
+#include "base/fiber.hh"
+
+class TestFiber : public Fiber
+{
+  public:
+    const char *name;
+    std::vector<Fiber *> next;
+
+    TestFiber(const char *name, std::initializer_list<Fiber *> l);
+
+    void checkExpected();
+    void main();
+};
+
+extern TestFiber a;
+extern TestFiber b;
+extern TestFiber c;
+
+TestFiber a("A", { &b, &a, Fiber::primaryFiber(), &b, &c });
+TestFiber b("B", { &a, &c });
+TestFiber c("C", { &a, Fiber::primaryFiber(), Fiber::primaryFiber() });
+
+std::vector<TestFiber *>::iterator expectedIt;
+std::vector<TestFiber *> expected({
+    &a, &b, &a, &a, /* main Fiber, */
+    &a, &b, &c, &a, &c,
+    /* main Fiber, */ &c, &c
+});
+
+TestFiber::TestFiber(
+        const char *name, std::initializer_list<Fiber *> l) :
+    name(name), next(l)
+{}
+
+void
+TestFiber::checkExpected()
+{
+    ASSERT_NE(expectedIt, expected.end());
+    TestFiber *e = *expectedIt++;
+    EXPECT_EQ(e, this) << "Expected " << e->name << ", got " << name;
+}
+
+void
+TestFiber::main()
+{
+    checkExpected();
+    for (auto &n : next) {
+        n->run();
+        checkExpected();
+    }
+}
+
+TEST(Fiber, Switching)
+{
+    expectedIt = expected.begin();
+
+    a.run();
+    EXPECT_EQ(expectedIt - expected.begin(), 4);
+
+    a.run();
+    EXPECT_EQ(expectedIt - expected.begin(), 9);
+
+    c.run();
+    EXPECT_EQ(expectedIt - expected.begin(), 10);
+
+    EXPECT_FALSE(a.finished());
+    EXPECT_FALSE(b.finished());
+    EXPECT_FALSE(c.finished());
+
+    c.run();
+    EXPECT_EQ(expected.end(), expectedIt) <<
+        "Didn't exactly use up the expected Fiber sequence";
+
+    EXPECT_TRUE(c.finished());
+}
+
+int currentIndex = 0;
+
+class LinkedFiber : public Fiber
+{
+  public:
+    const int index;
+    LinkedFiber(Fiber *link, int index) : Fiber(link), index(index) {}
+
+    void
+    main()
+    {
+        EXPECT_EQ(currentIndex, index);
+        currentIndex++;
+    }
+};
+
+TEST(Fiber, Linked)
+{
+    currentIndex = 0;
+
+    LinkedFiber lf3(Fiber::primaryFiber(), 3);
+    LinkedFiber lf2(&lf3, 2);
+    LinkedFiber lf1(&lf2, 1);
+    LinkedFiber lf0(&lf1, 0);
+
+    lf0.run();
+
+    EXPECT_EQ(currentIndex, 4);
+}