📄 kern_invoke.cxx
字号:
/* * 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 + -