1/* 2 * Copyright 2018 Google, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer; 8 * redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution; 11 * neither the name of the copyright holders nor the names of its 12 * contributors may be used to endorse or promote products derived from 13 * this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * Authors: Gabe Black 28 */ 29 30#include "base/fiber.hh" 31 32#if HAVE_VALGRIND 33#include <valgrind/valgrind.h> 34#endif 35 36// Mac OS requires _DARWIN_C_SOURCE if _POSIX_C_SOURCE is defined, 37// otherwise it will mask the definition of MAP_ANONYMOUS. 38// _POSIX_C_SOURCE is already defined by including <ucontext.h> in 39// base/fiber.hh 40#if defined(__APPLE__) && defined(__MACH__) 41#define _DARWIN_C_SOURCE 42#endif 43 44#include <sys/mman.h> 45#include <unistd.h> 46 47#include <cerrno> 48#include <cstdio> 49#include <cstring> 50 51#include "base/logging.hh" 52 53using namespace std; 54 55namespace 56{ 57 58/* 59 * The PrimaryFiber class is a special case that attaches to the currently 60 * executing context. That makes handling the "primary" fiber, aka the one 61 * which most of gem5 is running under, no different than other Fibers. 62 */ 63class PrimaryFiber : public Fiber 64{ 65 public: 66 PrimaryFiber() : Fiber(nullptr, 0) { setStarted(); } 67 void main() { panic("PrimaryFiber main executed.\n"); } 68}; 69 70PrimaryFiber _primaryFiber; 71 72// A pointer to whatever the currently executing Fiber is. 73Fiber *_currentFiber = &_primaryFiber; 74 75// A pointer to the Fiber which is currently being started/initialized. 76Fiber *startingFiber = nullptr; 77 78} // anonymous namespace 79 80void 81Fiber::entryTrampoline() 82{ 83 startingFiber->start(); 84} 85 86Fiber::Fiber(size_t stack_size) : Fiber(primaryFiber(), stack_size) 87{} 88 89Fiber::Fiber(Fiber *link, size_t stack_size) : 90 link(link), stack(nullptr), stackSize(stack_size), guardPage(nullptr), 91 guardPageSize(sysconf(_SC_PAGE_SIZE)), _started(false), _finished(false) 92{ 93 if (stack_size) { 94 guardPage = mmap(nullptr, guardPageSize + stack_size, 95 PROT_READ | PROT_WRITE, 96 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 97 if (guardPage == (void *)MAP_FAILED) { 98 perror("mmap"); 99 fatal("Could not mmap %d byte fiber stack.\n", stack_size); 100 } 101 stack = (void *)((uint8_t *)guardPage + guardPageSize); 102 if (mprotect(guardPage, guardPageSize, PROT_NONE)) { 103 perror("mprotect"); 104 fatal("Could not forbid access to fiber stack guard page."); 105 } 106 } 107#if HAVE_VALGRIND 108 valgrindStackId = VALGRIND_STACK_REGISTER( 109 stack, (uint8_t *)stack + stack_size); 110#endif 111} 112 113Fiber::~Fiber() 114{ 115 panic_if(stack && _currentFiber == this, "Fiber stack is in use."); 116#if HAVE_VALGRIND 117 VALGRIND_STACK_DEREGISTER(valgrindStackId); 118#endif 119 if (guardPage) 120 munmap(guardPage, guardPageSize + stackSize); 121} 122 123void 124Fiber::createContext() 125{ 126 // Set up a context for the new fiber, starting it in the trampoline. 127 getcontext(&ctx); 128 ctx.uc_stack.ss_sp = stack; 129 ctx.uc_stack.ss_size = stackSize; 130 ctx.uc_link = nullptr; 131 makecontext(&ctx, &entryTrampoline, 0); 132 133 // Swap to the new context so it can enter its start() function. It 134 // will then swap itself back out and return here. 135 startingFiber = this; 136 panic_if(!_currentFiber, "No active Fiber object."); 137 swapcontext(&_currentFiber->ctx, &ctx); 138 139 // The new context is now ready and about to call main(). 140} 141 142void 143Fiber::start() 144{ 145 // Avoid a dangling pointer. 146 startingFiber = nullptr; 147 148 setStarted(); 149 150 // Swap back to the parent context which is still considered "current", 151 // now that we're ready to go. 152 int ret M5_VAR_USED = swapcontext(&ctx, &_currentFiber->ctx); 153 panic_if(ret == -1, strerror(errno)); 154 155 // Call main() when we're been reactivated for the first time. 156 main(); 157 158 // main has returned, so this Fiber has finished. Switch to the "link" 159 // Fiber. 160 _finished = true; 161 link->run(); 162} 163 164void 165Fiber::run() 166{ 167 panic_if(_finished, "Fiber has already run to completion."); 168 169 // If we're already running this fiber, we're done. 170 if (_currentFiber == this) 171 return; 172 173 if (!_started) 174 createContext(); 175 176 // Switch out of the current Fiber's context and this one's in. 177 Fiber *prev = _currentFiber; 178 Fiber *next = this; 179 _currentFiber = next; 180 swapcontext(&prev->ctx, &next->ctx); 181} 182 183Fiber *Fiber::currentFiber() { return _currentFiber; } 184Fiber *Fiber::primaryFiber() { return &_primaryFiber; } 185