📄 pk_nodekey.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. */#include <kerninc/kernel.hxx>#include <kerninc/Key.hxx>#include <kerninc/Invocation.hxx>#include <kerninc/Node.hxx>#include <kerninc/Thread.hxx>#include <kerninc/Process.hxx>#include <kerninc/SegWalk.hxx>#include <eros/Invoke.h>#include <eros/StdKeyType.h>#include <eros/NodeKey.h>#include <eros/NumberKey.h>#include <eros/memory.h>/* See pk_ProcessKey.cxx */extern voidMaybeRepairCpuReserveLinkages(Node *theNode, CpuReserve* oldReserve, const Key &newKey);/* #define NODEKEYDEBUG *//* Node key implements node keys, RO node keys, and sense keys. * N.B. that the node we point to may NOT be prepared as a node! *//* DANGER -- there is a potential gotcha in the node key * implementation. Suppose that the node key happens to name the * caller's domain root. The operations (swap, zero, WriteNumbers) * either alter the register values of the domain or cause the domain * to become malformed. The swap operation may, in addition, cause * the key registers node to be changed, which has the effect of * altering the return key slots. * * KeyKOS made this all transparent. It would be nice to make all of * this transparent, but if you are holding a node key in the first * place then you have sufficient authority to obtain a domain key to * yourself, by which any alteration that would leave your domain * running can be accomplished. If you don't want your domain * running, be a sport and return to the null key. * * Given this, I just made dorking the returnee generate a suitable * return code. * */voidDesensitize(Key *k){ if (k == 0) return; switch (k->GetType()) { case KT_Node: case KT_Segment:#ifdef KT_Wrapper case KT_Wrapper:#endif k->SetReadOnly(); k->SetNoCall(); k->SetWeak(); break; case KT_Page: k->SetReadOnly(); break; case KT_Number: break; case KT_Discrim: case KT_Returner: break; default: /* Safe because only called on invocation block keys: */ assert( k->IsHazard() == false ); k->NH_VoidKey(); break; }}voidNodeKey(Invocation& inv){ bool isSense = inv.key->IsWeak(); bool isFetch = inv.key->IsReadOnly(); Node *theNode = (Node *) inv.key->GetObjectPtr(); if (inv.invokee && theNode == inv.invokee->procRoot && OC_Node_Swap == inv.entry.code) dprintf(true, "Modifying invokee domain root\n"); if (inv.invokee && theNode == inv.invokee->keysNode && OC_Node_Swap == inv.entry.code) dprintf(true, "Modifying invokee keys node\n"); switch (inv.entry.code) { case OC_KeyType: { COMMIT_POINT(); inv.exit.code = RC_OK; inv.exit.w1 = AKT_Node; inv.exit.w2 = inv.key->keyData; return; } case OC_Node_Copy: { uint32_t slot = inv.entry.w1; COMMIT_POINT(); if (slot >= EROS_NODE_SIZE) { inv.exit.code = RC_RequestError; return; } /* All of these complete ok. */ inv.exit.code = RC_OK; if ( (*theNode)[slot].IsRdHazard() ) theNode->ClearHazard(slot); /* Does not copy hazard bits, but preserves preparation: */ inv.SetExitKey(0, theNode->slot[slot]);#ifdef NODEKEYDEBUG dprintf(true, "Copied key to exit slot %d\n", slot);#endif if (isSense) Desensitize(inv.exit.pKey[0]); Thread::Current()->Prepare(); return; } case OC_Node_Swap: { if (theNode == Thread::CurContext()->keysNode) dprintf(true, "Swap involving sender keys\n"); if (isFetch) { COMMIT_POINT(); inv.exit.code = RC_NoAccess; return; } uint32_t slot = inv.entry.w1; if (slot >= EROS_NODE_SIZE) { COMMIT_POINT(); inv.exit.code = RC_RequestError; return; } /* All of these complete ok. */ inv.exit.code = RC_OK; theNode->MakeObjectDirty(); COMMIT_POINT(); /* advance the PC! */ /* Following will not cause dirty node because we forced it * dirty above the commit point. */ theNode->ClearHazard(slot); /* Tread carefully, because we need to rearrange the CPU * reserve linkages. */ CpuReserve *oldReserve = 0; if ( slot == ProcSched && theNode->slot[slot].IsType(KT_Sched) ) oldReserve = &CpuReserve::GetReserve(theNode->slot[slot]); { Key k; /* temporary in case send and receive */ /* slots are the same. */ k.IKS_Set(theNode->slot[slot]); (*theNode)[slot].NH_Set(*inv.entry.key[0]); inv.SetExitKey(0, k); /* Unchain, but do not unprepare -- the objects do not have * on-disk keys. */ k.NH_Unchain(); } if (oldReserve) MaybeRepairCpuReserveLinkages(theNode, oldReserve, theNode->slot[slot]);#ifdef NODEKEYDEBUG dprintf(true, "Swapped key to slot %d\n", slot);#endif Thread::Current()->Prepare(); inv.nextPC = ((Process *) Thread::CurContext())->GetPC(); return; } case OC_Node_Extended_Copy: case OC_Node_Extended_Swap: { SegWalk wi; bool result; wi.faultCode = FC_NoFault; wi.traverseCount = 0; wi.segObj = 0; wi.vaddr = inv.entry.w1; wi.frameBits = EROS_NODE_LGSIZE; wi.writeAccess = (inv.entry.code == OC_Node_Extended_Swap) ? true : false; wi.invokeKeeperOK = true; /* seg keeper can be invoked */ wi.invokeProcessKeeperOK = false; /* but not process keeper */ wi.wantLeafNode = true; segwalk_init(wi, inv.key); /* Begin the traversal... */ result = proc_WalkSeg(Thread::CurContext(), wi, EROS_PAGE_BLSS, 0, 0, false); /* If this is a write operation, we need to mark the node dirty. */ if (wi.writeAccess) theNode->MakeObjectDirty(); COMMIT_POINT(); if ( result == false ) { fatal("Node tree traversal fails without keeper.!\n"); inv.exit.code = RC_NoAccess; inv.exit.w1 = wi.faultCode; inv.exit.w2 = wi.vaddr; return; } assert(wi.segObj); assert(wi.segObj->obType <= ObType::NtLAST_NODE_TYPE); uint32_t slot = inv.entry.w1 & EROS_NODE_SLOT_MASK; Node *theNode = (Node *) wi.segObj; COMMIT_POINT(); if (inv.entry.code == OC_Node_Extended_Swap) { assert(wi.canWrite); CpuReserve *oldReserve = 0; if ( slot == ProcSched && theNode->slot[slot].IsType(KT_Sched) ) oldReserve = &CpuReserve::GetReserve(theNode->slot[slot]); Key k; /* temporary in case send and receive */ /* slots are the same. */ k.IKS_Set(theNode->slot[slot]); (*theNode)[slot].NH_Set(*inv.entry.key[0]); inv.SetExitKey(0, k); /* Unchain, but do not unprepare -- the objects do not have * on-disk keys. */ k.NH_Unchain(); if (oldReserve) MaybeRepairCpuReserveLinkages(theNode, oldReserve, theNode->slot[slot]); /* There is no need to check this for sense key status, as the * segment walker would have generated an exception if the * tree was not writable, and any writable tree is also * readable in full power form. */ } else { inv.SetExitKey(0, theNode->slot[slot]); if (wi.canFullFetch == false) Desensitize(inv.exit.pKey[0]); } inv.exit.code = RC_OK; return; } case OC_Node_MakeNodeKey: case OC_Node_MakeSegmentKey:#ifdef KT_Wrapper case OC_Node_MakeWrapperKey:#endif { COMMIT_POINT(); uint32_t w = inv.entry.w1; if ( (w > EROS_KEYDATA_MAX) ) { inv.exit.code = RC_RequestError; dprintf(true, "Value 0x%08x is out of range\n", w); return; } w |= (inv.key->keyData & SEGMODE_ATTRIBUTE_MASK); inv.exit.code = RC_OK; inv.SetExitKey(0, *inv.key); if (inv.exit.pKey[0]) { inv.exit.pKey[0]->keyData = w; switch(inv.entry.code) { case OC_Node_MakeNodeKey: break; case OC_Node_MakeSegmentKey: inv.exit.pKey[0]->SetType(KT_Segment); break;#ifdef KT_Wrapper case OC_Node_MakeWrapperKey: inv.exit.pKey[0]->SetType(KT_Wrapper); break;#endif } } return; } case OC_Node_CompareKey: { inv.entry.key[0]->Prepare(); COMMIT_POINT(); inv.exit.code = RC_OK; inv.exit.w1 = 0; /* until proven otherwise */ switch (inv.entry.key[0]->GetType()) { case KT_Node:#ifdef KT_Wrapper case KT_Wrapper:#endif case KT_Segment: /* It's possible - check further below */ break; default: return; } if (inv.entry.key[0]->GetKeyOid() != inv.key->GetKeyOid()) return; if (inv.entry.key[0]->GetAllocCount() != inv.key->GetAllocCount()) return; inv.exit.w1 = 1; return; } case OC_Node_Clear: { if (isFetch) { inv.exit.code = RC_NoAccess; return; } theNode->MakeObjectDirty(); /* If we zero it, we're going to nail all of it's dependencies * anyway: */ COMMIT_POINT(); theNode->ClearThisNode(); Thread::Current()->Prepare(); return; } case OC_Node_KeyDataField: { COMMIT_POINT(); inv.exit.code = RC_OK; inv.exit.w1 = inv.key->keyData; return; } case OC_Node_WriteNumber: { if (isFetch) { COMMIT_POINT(); inv.exit.code = RC_NoAccess; return; } uint32_t slot = inv.entry.w1; if (slot >= EROS_NODE_SIZE || inv.entry.len != sizeof(nk_value)) { COMMIT_POINT(); inv.exit.code = RC_RequestError; return; }#ifndef OPTION_PURE_ENTRY_STRINGS Thread::CurContext()->SetupEntryString(inv);#endif /* If we overwrite it, we're going to nail all of it's * dependencies anyway: */ inv.exit.code = RC_OK; CpuReserve *oldReserve = 0; if (slot == ProcSched && theNode->slot[slot].IsType(KT_Sched) ) oldReserve = &CpuReserve::GetReserve(theNode->slot[slot]); theNode->MakeObjectDirty(); COMMIT_POINT(); nk_value nkv; inv.CopyIn(inv.entry.len, &nkv); theNode->ClearHazard(slot); assert( (*theNode)[slot].IsUnprepared() ); (*theNode)[slot].KS_SetNumberKey(nkv.value[2], nkv.value[1], nkv.value[0]); if (oldReserve) { Key voidKey; /* default constructor == void */ /* any type other than KT_Sched will do */ MaybeRepairCpuReserveLinkages(theNode, oldReserve, voidKey); } Thread::Current()->Prepare(); inv.nextPC = ((Process *) Thread::CurContext())->GetPC(); return; } case OC_Node_Clone: { /* copy content of node key in arg0 to current node */ if (isFetch) { inv.exit.code = RC_NoAccess; return; } /* Mark the object dirty. */ theNode->MakeObjectDirty(); inv.entry.key[0]->Prepare(); assert(inv.key->IsPrepared()); COMMIT_POINT(); if (inv.entry.key[0]->GetType() != KT_Node) { inv.exit.code = RC_RequestError; return; } bool isWeak = inv.entry.key[0]->IsWeak(); Node *fromNode = (Node *) inv.entry.key[0]->GetObjectPtr(); for (unsigned slot = 0; slot < EROS_NODE_SIZE; slot++) { theNode->ClearHazard(slot); if (fromNode->slot[slot].IsRdHazard()) fromNode->ClearHazard(slot); } /* The following will do the right thing if the two nodes are * identical, because the NH_Set code checks for this case. */ for (unsigned slot = 0; slot < EROS_NODE_SIZE; slot++) { /* hazards cleared above */ theNode->slot[slot].NH_Set( (*fromNode)[slot] ); if (isWeak) Desensitize(&theNode->slot[slot]); } return; }#if 0 /* Removed because keeping the reserves straight is a pain in the ass. */ case OC_Node_WriteNumbers: { if (isFetch) { inv.exit.code = RC_NoAccess; return; } inv.exit.code = RC_RequestError; struct inMsg { Word start; Word end; Word numBuf[EROS_NODE_SIZEx][3]; /* number key data fields */ } req; /* If we overwrite it, we're going to nail all of it's * dependencies anyway: */ theNode->Unprepare(true); theNode->MakeObjectDirty();#ifndef OPTION_PURE_ENTRY_STRINGS Thread::CurContext()->SetupEntryString(inv);#endif COMMIT_POINT(); bzero(&req, sizeof(req)); inv.CopyIn(sizeof(req), &req); if (req.start >= EROS_NODE_SIZEx || req.end >= EROS_NODE_SIZEx || req.start > req.end) return; for (uint32_t k = req.start; k < req.end; k++) { (*theNode)[k].KS_SetNumberKey(req.numBuf[(k-req.start)][2], req.numBuf[(k-req.start)][1], req.numBuf[(k-req.start)][0] ); } return; }#endif default: COMMIT_POINT(); inv.exit.code = RC_UnknownRequest; return; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -