⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kern_invoke.cxx

📁 C++ 编写的EROS RTOS
💻 CXX
📖 第 1 页 / 共 4 页
字号:
/* * Copyright (C) 1998, 1999, 2001, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Driver for key invocation */#include <kerninc/kernel.hxx>#include <kerninc/Thread.hxx>#include <kerninc/Debug.hxx>#include <kerninc/Check.hxx>#include <kerninc/util.h>#include <kerninc/Invocation.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/StackTester.hxx>#include <eros/SysTraceKey.h>#include <arch-kerninc/Process.hxx>#include <eros/Invoke.h>#ifdef OPTION_DDB#include <eros/StdKeyType.h>#endif#include <kerninc/Machine.hxx>#if 0#include <machine/PTE.hxx>#endif/* #define GATEDEBUG 5 * #define KPRDEBUG *//* #define TESTING_INVOCATION */#ifdef TESTING_INVOCATIONconst dbg_wild_ptr = 1;#endif#ifdef OPTION_DDBuint32_t ddb_inv_flags = 0;#define DDB_STOP(x) (ddb_inv_flags & DDB_INV_##x)#endif#define __EROS_PRIMARY_KEYDEF(x) void x##Key (Invocation& msg);#include <eros/StdKeyType.h>void GateKey (Invocation& msg);static voidUnknownKey(Invocation& inv){  inv.key->Print();  fatal("Invocation of unknown primary key type 0x%x\n",		inv.key->GetType());}static voidUnimplementedKey(Invocation& inv){  fatal("Invocation of unimplemented primary key type 0x%x\n",	     inv.key->GetType());}KeyHandler keyHandler[PRIMARY_KEY_TYPES] = {  GateKey,			/* KT_Start */  GateKey,			/* KT_Resume */#ifdef KT_Wrapper  WrapperKey,			/* KT_Wrapper */#endif  NodeKey,			/* KT_Node */  SegmentKey,			/* KT_Segment */  ProcessKey,			/* KT_Process */  PageKey,			/* KT_Page */  DeviceKey,			/* KT_Device */  NumberKey,			/* KT_Number */  UnimplementedKey,		/* KT_Timer */  SchedKey,			/* KT_Sched */  RangeKey,			/* KT_Range */  RangeKey,			/* KT_PrimeRange */  RangeKey,			/* KT_PhysRange */  KeyBitsKey,			/* KT_KeyBits */  DiscrimKey,			/* KT_Discrim */  ReturnerKey,			/* KT_Returner */  ProcessToolKey,		/* KT_ProcessTools */  CheckpointKey,		/* KT_CheckpointKey */  VoidKey,			/* KT_VoidKey */  SleepKey,			/* KT_SleepKey */  ConsoleKey,			/* KT_ConsoleKey */  SchedCreatorKey,		/* KT_SchedCreator */  SysTraceKey,			/* KT_SysTrace */  DevicePrivsKey,		/* KT_DeviceProvs */  TimePageKey,			/* KT_TimePage */  TimeOfDayKey,			/* KT_TimeOfDay */#ifdef OPTION_KBD  KeyboardKey,			/* KT_Keyboard */#else  UnknownKey,			/* (unassigned) */#endif  UnknownKey,			/* (unassigned) */  UnknownKey,			/* (unassigned) */  UnknownKey,			/* (unassigned) */  UnknownKey,			/* (unassigned) */#ifndef KT_Wrapper  UnknownKey,			/* (unassigned) */#endif};#ifdef OPTION_KERN_TIMING_STATSuint64_t Invocation::KeyHandlerCycles[PRIMARY_KEY_TYPES][3];uint64_t Invocation::KeyHandlerCounts[PRIMARY_KEY_TYPES][3];voidInvocation::ZeroStats(){  for (int i = 0; i < PRIMARY_KEY_TYPES; i++) {    KeyHandlerCycles[i][IT_Call] = 0;    KeyHandlerCycles[i][IT_Reply] = 0;    KeyHandlerCycles[i][IT_Send] = 0;    KeyHandlerCounts[i][IT_Call] = 0;    KeyHandlerCounts[i][IT_Reply] = 0;    KeyHandlerCounts[i][IT_Send] = 0;  }}#endifvoidInvocation::BootInit(){#if defined(OPTION_KERN_TIMING_STATS)  ZeroStats();#endif}/* KEY INVOCATION *  * Logic in Principle: Entry, Action, Exit *  * Entry:  *  *    1. Validate that the invocation is well-formed, and that the *       data argument if any is present and validly mapped.   *  *    2. Validate all of the information associated with the *       invocation to a place of safety against the possibility that *       entry and exit arguments overlap. *  *    3. Determine who we will be exiting to (the invokee) -- this is *       often the invoker.  It can also be the domain named by the *       invoked gate key.  It can also be the domain named by the key *       in slot 4 if this is a kernel key (YUCK).  It can also be *       NULL if the key in slot 4 proves not to be a resume key. *  *       Invoked      Invocation    Slot 4     Invokee *       Key Type     Type          Key Type *  *       Gate Key     ANY           *          Named by gate key *       Kernel Key   CALL          *          Same as invoker *       Kernel Key   FORK,RETURN   Resume     Named by resume key *       Kernel Key   FORK,RETURN   !Resume    NONE *  *       Note that the case of CALL on a kernel key is in some sense *       the same as FORK/RETURN on a kernel key -- the resume key was *       generated by the call.  I may in fact implement the first *       version this way. *  *    4. Determine if the invokee will receive a resume key to the *       invoker.  (yes if the invocation is a CALL). *  *    5. Determine if a resume key will be consumed by the invocation *       (yes if the invokee was named by a resume key, including the *       case of CALL on kernel key). *  *    6. Determine if the invokee is in the proper state: *  *       Invoked      Invocation    Invokee *       Key Type     Type          State *  *       Start Key    ANY           AVAILABLE *       Resume Key   ANY           WAITING *       Kernel Key   CALL          RUNNING (really waiting per note above) *       Kernel Key   FORK,RETURN   WAITING *  *    7. Determine if a new thread is to be created. *  *    8. Construct a receive buffer mapping, splicing in the *       kernel-reserved void page where no mapping is present. *  *    NONE OF THE ABOVE CAN MODIFY THE PROGRAM as the action may put *    the program to sleep, causing us to need to restart the entire *    invocation. *  * Action: *    Whatever the key is specified to do.  This may require stalling *    the application.  The interesting case is the gate key *    implementation.  Given that the above actions have already been *    performed, the gate key action *  * Exit: *    If a resume key is to be copied *      If already consumed copy a null key *      else construct resume key *    Migrate the outbound thread to the invokee. *  *  * NOTE DELICATE ISSUE *  *   Red seg invocation is the only place where a resume key can *   become prepared without it's containing object being dirty (the *   other case -- key registers -- guarantees that the object is *   already dirty.  Preparing a resume key without guaranteeing dirty *   object causes resume key rescind to violate the checkpoint *   constraint by creating a situation in which the resume key may *   get turned into void before the containing node is stabilized. *   The code here is careful to check -- if the keeper gate key is a *   resume key, we mark the containing node dirty. *  *   In practice, if you use a resume key in a keeper slot you are a *   bogon anyway, so I'm not real worried about it... */Invocation inv;  #ifndef NDEBUGbool InvocationCommitted = false;#endif  voidInvocation::Commit(){  MaybeDecommit();  #ifndef NDEBUG  InvocationCommitted = true;#endif  if (Thread::Current()->context) {    /* This is bogus, because this gets called from GateKey(), which     * can in turn be called from InvokeMyKeeper, within which     * CurContext is decidedly NOT runnable.     * assert ( Thread::CurContext()->IsRunnable() );     */    ((Process *) Thread::Current()->context)->SetPC(nextPC);  }}boolInvocation::IsInvocationKey(const Key *pKey){  /* because this gets set up early in the keeper invocation   * path, /inv.key/ may not be set yet, so check this early.   */  if (pKey == &redNodeKey)    return true;    if (key == 0)    return false;    if (pKey == key)    return true;  #if 0  if (pKey == &resumeKey)    return true;#endif    if (pKey == &scratchKey)    return true;  #if 0  if (pKey == &exit.key[0] ||      pKey == &exit.key[1] ||      pKey == &exit.key[2] ||      pKey == &exit.key[3])    return true;#endif      return false;}Invocation::Invocation(): entry(), exit(){  entry.key[0] = 0;  entry.key[1] = 0;  entry.key[2] = 0;  entry.key[3] = 0;  exit.len = 0;  exit.w1 = 0;  exit.w2 = 0;  exit.w3 = 0;  /* exit.code is always set explicitly, so don't bother. */  key = 0;#ifndef NDEBUG  InvocationCommitted = false;#endif  invokee = 0;  flags = 0;}voidInvocation::Cleanup(){  key = 0;  #ifndef NDEBUG  InvocationCommitted = false;#endif    /* exit register values are set up in PopulateExitBlock, where they   * are only done on one path.  Don't change them here.   */  #ifndef NDEBUG  /* Leaving the key pointers dangling causes no harm */  entry.key[0] = 0;  entry.key[1] = 0;  entry.key[2] = 0;  entry.key[3] = 0;#endif  /* NH_Unprepare is expensive.  Avoid it if possible: */  if (flags & INV_SCRATCHKEY)    scratchKey.NH_VoidKey();  if (flags & INV_REDNODEKEY)    redNodeKey.NH_VoidKey();#if 0  if (flags & INV_RESUMEKEY)    resumeKey.NH_VoidKey();#endif#if 0  if (flags & INV_EXITKEY0)    exit.key[0].NH_VoidKey();  if (flags & INV_EXITKEY3)    exit.key[3].INH_VoidKey();  if (flags & INV_EXITKEYOTHER) {    exit.key[1].NH_VoidKey();    exit.key[2].NH_VoidKey();  }#endif    flags = 0;}voidInvocation::RetryInvocation(){  Cleanup();  Thread::CurContext()->runState = RS_Running;  Thread::Current()->Yield();}#ifndef NDEBUGboolInvocation::IsCorrupted(){  for (int i = 0; i < 4; i++) {    if (entry.key[i])      return true;#if 0    if (exit.key[i].IsPrepared())      return true;#endif  }  return false;}#endifvoidProcess::BuildResumeKey(Key& resumeKey){  /* Fabricate a prepared resume key.  The mere existence of this key is   * technically an error, but we will blow it away at the end of the   * invocation, and nothing we call from here will notice it's   * presence.   */#ifndef NDEBUG  if (!kr.IsValid(this))    fatal("Key ring screwed up\n");#endif  /* not hazarded because it is an invocation key */  resumeKey.NH_Unprepare();    resumeKey.InitType(KT_Resume);  resumeKey.SetPrepared();  resumeKey.gk.pContext = this;  resumeKey.gk.next = &kr;  resumeKey.gk.prev = kr.prev;  kr.prev = (KeyRing*) &resumeKey;  resumeKey.gk.prev->next = (KeyRing *) &resumeKey;#if 0  printf("Dumping key ring (context=0x%08x, resumeKey=0x%08x):\n"		 "kr.prev=0x%08x kr.next=0x%08x\n",		 this, &resumeKey, kr.prev, kr.next);  KeyRing      *pkr=kr.prev;  int		count=0;  while (pkr!=&kr)    {      printf("pkr->prev=0x%08x pkr->next=0x%08x\n",		     pkr->prev, pkr->next);      ((Key *)pkr)->Print();      pkr=pkr->prev;      count++;    }  if (count>1)    dprintf(true, "Multiple keys in key ring\n");#endif#ifdef GATEDEBUG  dprintf(GATEDEBUG>2, "Crafted resume key\n");#endif}extern "C" {  uint64_t rdtsc();};/* This is a carefully coded routine, probably less than clear.  The * version that handles all of the necessary cases is * DoGeneralKeyInvocation; this version is trying to cherry pick the * high flyers. *  * The common cases are, in approximate order: *  *  1. CALL   on a gate key *  2. RETURN on a gate key    (someday: via returner) *  3. CALL   on a kernel key *  4. Anything else *  * This version also assumes that the string arguments are valid, and * does only the fast version of the string test. */voidProcess::DoKeyInvocation(){#ifdef OPTION_KERN_TIMING_STATS  uint64_t pre_handler;  uint64_t top_time = rdtsc();#ifdef OPTION_KERN_EVENT_TRACING  uint64_t top_cnt0 = Machine::ReadCounter(0);  uint64_t top_cnt1 = Machine::ReadCounter(1);#endif#endif  KernStats.nInvoke++;#ifndef NDEBUG  InvocationCommitted = false;#endif  inv.nextPC = CalcPostInvocationPC();  /* in case we must restart */  AdjustInvocationPC();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -