gem5  v22.1.0.0
pagetable_walker.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 ARM Limited
3  * Copyright (c) 2020 Barkhausen Institut
4  * All rights reserved.
5  *
6  * The license below extends only to copyright in the software and shall
7  * not be construed as granting a license to any other intellectual
8  * property including but not limited to intellectual property relating
9  * to a hardware implementation of the functionality of the software
10  * licensed hereunder. You may use the software subject to the license
11  * terms below provided that you ensure that this notice is replicated
12  * unmodified and in its entirety in all distributions of the software,
13  * modified or unmodified, in source code or in binary form.
14  *
15  * Copyright (c) 2007 The Hewlett-Packard Development Company
16  * All rights reserved.
17  *
18  * The license below extends only to copyright in the software and shall
19  * not be construed as granting a license to any other intellectual
20  * property including but not limited to intellectual property relating
21  * to a hardware implementation of the functionality of the software
22  * licensed hereunder. You may use the software subject to the license
23  * terms below provided that you ensure that this notice is replicated
24  * unmodified and in its entirety in all distributions of the software,
25  * modified or unmodified, in source code or in binary form.
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions are
29  * met: redistributions of source code must retain the above copyright
30  * notice, this list of conditions and the following disclaimer;
31  * redistributions in binary form must reproduce the above copyright
32  * notice, this list of conditions and the following disclaimer in the
33  * documentation and/or other materials provided with the distribution;
34  * neither the name of the copyright holders nor the names of its
35  * contributors may be used to endorse or promote products derived from
36  * this software without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
41  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
42  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49  */
50 
52 
53 #include <memory>
54 
55 #include "arch/riscv/faults.hh"
56 #include "arch/riscv/page_size.hh"
57 #include "arch/riscv/pagetable.hh"
58 #include "arch/riscv/tlb.hh"
59 #include "base/bitfield.hh"
60 #include "base/trie.hh"
61 #include "cpu/base.hh"
62 #include "cpu/thread_context.hh"
63 #include "debug/PageTableWalker.hh"
64 #include "mem/packet_access.hh"
65 #include "mem/request.hh"
66 
67 namespace gem5
68 {
69 
70 namespace RiscvISA {
71 
72 Fault
74  const RequestPtr &_req, BaseMMU::Mode _mode)
75 {
76  // TODO: in timing mode, instead of blocking when there are other
77  // outstanding requests, see if this request can be coalesced with
78  // another one (i.e. either coalesce or start walk)
79  WalkerState * newState = new WalkerState(this, _translation, _req);
80  newState->initState(_tc, _mode, sys->isTimingMode());
81  if (currStates.size()) {
82  assert(newState->isTiming());
83  DPRINTF(PageTableWalker, "Walks in progress: %d\n", currStates.size());
84  currStates.push_back(newState);
85  return NoFault;
86  } else {
87  currStates.push_back(newState);
88  Fault fault = newState->startWalk();
89  if (!newState->isTiming()) {
90  currStates.pop_front();
91  delete newState;
92  }
93  return fault;
94  }
95 }
96 
97 Fault
98 Walker::startFunctional(ThreadContext * _tc, Addr &addr, unsigned &logBytes,
99  BaseMMU::Mode _mode)
100 {
101  funcState.initState(_tc, _mode);
102  return funcState.startFunctional(addr, logBytes);
103 }
104 
105 bool
107 {
108  return walker->recvTimingResp(pkt);
109 }
110 
111 bool
113 {
114  WalkerSenderState * senderState =
115  dynamic_cast<WalkerSenderState *>(pkt->popSenderState());
116  WalkerState * senderWalk = senderState->senderWalk;
117  bool walkComplete = senderWalk->recvPacket(pkt);
118  delete senderState;
119  if (walkComplete) {
121  for (iter = currStates.begin(); iter != currStates.end(); iter++) {
122  WalkerState * walkerState = *(iter);
123  if (walkerState == senderWalk) {
124  iter = currStates.erase(iter);
125  break;
126  }
127  }
128  delete senderWalk;
129  // Since we block requests when another is outstanding, we
130  // need to check if there is a waiting request to be serviced
131  if (currStates.size() && !startWalkWrapperEvent.scheduled())
132  // delay sending any new requests until we are finished
133  // with the responses
135  }
136  return true;
137 }
138 
139 void
141 {
142  walker->recvReqRetry();
143 }
144 
145 void
147 {
149  for (iter = currStates.begin(); iter != currStates.end(); iter++) {
150  WalkerState * walkerState = *(iter);
151  if (walkerState->isRetrying()) {
152  walkerState->retry();
153  }
154  }
155 }
156 
157 bool Walker::sendTiming(WalkerState* sendingState, PacketPtr pkt)
158 {
159  WalkerSenderState* walker_state = new WalkerSenderState(sendingState);
160  pkt->pushSenderState(walker_state);
161  if (port.sendTimingReq(pkt)) {
162  return true;
163  } else {
164  // undo the adding of the sender state and delete it, as we
165  // will do it again the next time we attempt to send it
166  pkt->popSenderState();
167  delete walker_state;
168  return false;
169  }
170 
171 }
172 
173 Port &
174 Walker::getPort(const std::string &if_name, PortID idx)
175 {
176  if (if_name == "port")
177  return port;
178  else
179  return ClockedObject::getPort(if_name, idx);
180 }
181 
182 void
184  BaseMMU::Mode _mode, bool _isTiming)
185 {
186  assert(state == Ready);
187  started = false;
188  tc = _tc;
189  mode = _mode;
190  timing = _isTiming;
191  // fetch these now in case they change during the walk
192  status = tc->readMiscReg(MISCREG_STATUS);
193  pmode = walker->tlb->getMemPriv(tc, mode);
194  satp = tc->readMiscReg(MISCREG_SATP);
195  assert(satp.mode == AddrXlateMode::SV39);
196 }
197 
198 void
200 {
201  unsigned num_squashed = 0;
202  WalkerState *currState = currStates.front();
203  while ((num_squashed < numSquashable) && currState &&
204  currState->translation->squashed()) {
205  currStates.pop_front();
206  num_squashed++;
207 
208  DPRINTF(PageTableWalker, "Squashing table walk for address %#x\n",
209  currState->req->getVaddr());
210 
211  // finish the translation which will delete the translation object
212  currState->translation->finish(
213  std::make_shared<UnimpFault>("Squashed Inst"),
214  currState->req, currState->tc, currState->mode);
215 
216  // delete the current request if there are no inflight packets.
217  // if there is something in flight, delete when the packets are
218  // received and inflight is zero.
219  if (currState->numInflight() == 0) {
220  delete currState;
221  } else {
222  currState->squash();
223  }
224 
225  // check the next translation request, if it exists
226  if (currStates.size())
227  currState = currStates.front();
228  else
229  currState = NULL;
230  }
231  if (currState && !currState->wasStarted())
232  currState->startWalk();
233 }
234 
235 Fault
237 {
238  Fault fault = NoFault;
239  assert(!started);
240  started = true;
241  setupWalk(req->getVaddr());
242  if (timing) {
243  nextState = state;
244  state = Waiting;
245  timingFault = NoFault;
246  sendPackets();
247  } else {
248  do {
249  walker->port.sendAtomic(read);
250  PacketPtr write = NULL;
251  fault = stepWalk(write);
252  assert(fault == NoFault || read == NULL);
253  state = nextState;
254  nextState = Ready;
255  if (write)
256  walker->port.sendAtomic(write);
257  } while (read);
258  state = Ready;
259  nextState = Waiting;
260  }
261  return fault;
262 }
263 
264 Fault
266 {
267  Fault fault = NoFault;
268  assert(!started);
269  started = true;
270  setupWalk(addr);
271 
272  do {
273  walker->port.sendFunctional(read);
274  // On a functional access (page table lookup), writes should
275  // not happen so this pointer is ignored after stepWalk
276  PacketPtr write = NULL;
277  fault = stepWalk(write);
278  assert(fault == NoFault || read == NULL);
279  state = nextState;
280  nextState = Ready;
281  } while (read);
282  logBytes = entry.logBytes;
283  addr = entry.paddr << PageShift;
284 
285  return fault;
286 }
287 
288 Fault
290 {
291  assert(state != Ready && state != Waiting);
292  Fault fault = NoFault;
293  write = NULL;
294  PTESv39 pte = read->getLE<uint64_t>();
295  Addr nextRead = 0;
296  bool doWrite = false;
297  bool doTLBInsert = false;
298  bool doEndWalk = false;
299 
300  DPRINTF(PageTableWalker, "Got level%d PTE: %#x\n", level, pte);
301 
302  // step 2:
303  // Performing PMA/PMP checks on physical address of PTE
304 
305  walker->pma->check(read->req);
306  // Effective privilege mode for pmp checks for page table
307  // walks is S mode according to specs
308  fault = walker->pmp->pmpCheck(read->req, BaseMMU::Read,
309  RiscvISA::PrivilegeMode::PRV_S, tc, entry.vaddr);
310 
311  if (fault == NoFault) {
312  // step 3:
313  if (!pte.v || (!pte.r && pte.w)) {
314  doEndWalk = true;
315  DPRINTF(PageTableWalker, "PTE invalid, raising PF\n");
316  fault = pageFault(pte.v);
317  }
318  else {
319  // step 4:
320  if (pte.r || pte.x) {
321  // step 5: leaf PTE
322  doEndWalk = true;
323  fault = walker->tlb->checkPermissions(status, pmode,
324  entry.vaddr, mode, pte);
325 
326  // step 6
327  if (fault == NoFault) {
328  if (level >= 1 && pte.ppn0 != 0) {
329  DPRINTF(PageTableWalker,
330  "PTE has misaligned PPN, raising PF\n");
331  fault = pageFault(true);
332  }
333  else if (level == 2 && pte.ppn1 != 0) {
334  DPRINTF(PageTableWalker,
335  "PTE has misaligned PPN, raising PF\n");
336  fault = pageFault(true);
337  }
338  }
339 
340  if (fault == NoFault) {
341  // step 7
342  if (!pte.a) {
343  pte.a = 1;
344  doWrite = true;
345  }
346  if (!pte.d && mode == BaseMMU::Write) {
347  pte.d = 1;
348  doWrite = true;
349  }
350  // Performing PMA/PMP checks
351 
352  if (doWrite) {
353 
354  // this read will eventually become write
355  // if doWrite is True
356 
357  walker->pma->check(read->req);
358 
359  fault = walker->pmp->pmpCheck(read->req,
360  BaseMMU::Write, pmode, tc, entry.vaddr);
361 
362  }
363  // perform step 8 only if pmp checks pass
364  if (fault == NoFault) {
365 
366  // step 8
367  entry.logBytes = PageShift + (level * LEVEL_BITS);
368  entry.paddr = pte.ppn;
369  entry.vaddr &= ~((1 << entry.logBytes) - 1);
370  entry.pte = pte;
371  // put it non-writable into the TLB to detect
372  // writes and redo the page table walk in order
373  // to update the dirty flag.
374  if (!pte.d && mode != BaseMMU::Write)
375  entry.pte.w = 0;
376  doTLBInsert = true;
377  }
378  }
379  } else {
380  level--;
381  if (level < 0) {
382  DPRINTF(PageTableWalker, "No leaf PTE found,"
383  "raising PF\n");
384  doEndWalk = true;
385  fault = pageFault(true);
386  } else {
388  Addr idx = (entry.vaddr >> shift) & LEVEL_MASK;
389  nextRead = (pte.ppn << PageShift) + (idx * sizeof(pte));
390  nextState = Translate;
391  }
392  }
393  }
394  } else {
395  doEndWalk = true;
396  }
397  PacketPtr oldRead = read;
398  Request::Flags flags = oldRead->req->getFlags();
399 
400  if (doEndWalk) {
401  // If we need to write, adjust the read packet to write the modified
402  // value back to memory.
403  if (!functional && doWrite) {
404  DPRINTF(PageTableWalker, "Writing level%d PTE to %#x: %#x\n",
405  level, oldRead->getAddr(), pte);
406  write = oldRead;
407  write->setLE<uint64_t>(pte);
408  write->cmd = MemCmd::WriteReq;
409  read = NULL;
410  } else {
411  write = NULL;
412  }
413 
414  if (doTLBInsert) {
415  if (!functional)
416  walker->tlb->insert(entry.vaddr, entry);
417  else {
418  DPRINTF(PageTableWalker, "Translated %#x -> %#x\n",
419  entry.vaddr, entry.paddr << PageShift |
420  (entry.vaddr & mask(entry.logBytes)));
421  }
422  }
423  endWalk();
424  }
425  else {
426  //If we didn't return, we're setting up another read.
427  RequestPtr request = std::make_shared<Request>(
428  nextRead, oldRead->getSize(), flags, walker->requestorId);
429 
430  delete oldRead;
431  oldRead = nullptr;
432 
433  read = new Packet(request, MemCmd::ReadReq);
434  read->allocate();
435 
436  DPRINTF(PageTableWalker,
437  "Loading level%d PTE from %#x\n", level, nextRead);
438  }
439 
440  return fault;
441 }
442 
443 void
445 {
446  nextState = Ready;
447  delete read;
448  read = NULL;
449 }
450 
451 void
453 {
454  vaddr = Addr(sext<VADDR_BITS>(vaddr));
455 
456  Addr shift = PageShift + LEVEL_BITS * 2;
457  Addr idx = (vaddr >> shift) & LEVEL_MASK;
458  Addr topAddr = (satp.ppn << PageShift) + (idx * sizeof(PTESv39));
459  level = 2;
460 
461  DPRINTF(PageTableWalker, "Performing table walk for address %#x\n", vaddr);
462  DPRINTF(PageTableWalker, "Loading level%d PTE from %#x\n", level, topAddr);
463 
464  state = Translate;
465  nextState = Ready;
466  entry.vaddr = vaddr;
467  entry.asid = satp.asid;
468 
470  RequestPtr request = std::make_shared<Request>(
471  topAddr, sizeof(PTESv39), flags, walker->requestorId);
472 
473  read = new Packet(request, MemCmd::ReadReq);
474  read->allocate();
475 }
476 
477 bool
479 {
480  assert(pkt->isResponse());
481  assert(inflight);
482  assert(state == Waiting);
483  inflight--;
484  if (squashed) {
485  // if were were squashed, return true once inflight is zero and
486  // this WalkerState will be freed there.
487  return (inflight == 0);
488  }
489  if (pkt->isRead()) {
490  // should not have a pending read it we also had one outstanding
491  assert(!read);
492 
493  // @todo someone should pay for this
494  pkt->headerDelay = pkt->payloadDelay = 0;
495 
496  state = nextState;
497  nextState = Ready;
498  PacketPtr write = NULL;
499  read = pkt;
500  timingFault = stepWalk(write);
501  state = Waiting;
502  assert(timingFault == NoFault || read == NULL);
503  if (write) {
504  writes.push_back(write);
505  }
506  sendPackets();
507  } else {
508  delete pkt;
509 
510  sendPackets();
511  }
512  if (inflight == 0 && read == NULL && writes.size() == 0) {
513  state = Ready;
514  nextState = Waiting;
515  if (timingFault == NoFault) {
516  /*
517  * Finish the translation. Now that we know the right entry is
518  * in the TLB, this should work with no memory accesses.
519  * There could be new faults unrelated to the table walk like
520  * permissions violations, so we'll need the return value as
521  * well.
522  */
523  Addr vaddr = req->getVaddr();
524  vaddr = Addr(sext<VADDR_BITS>(vaddr));
525  Addr paddr = walker->tlb->translateWithTLB(vaddr, satp.asid, mode);
526  req->setPaddr(paddr);
527  walker->pma->check(req);
528 
529  // do pmp check if any checking condition is met.
530  // timingFault will be NoFault if pmp checks are
531  // passed, otherwise an address fault will be returned.
532  timingFault = walker->pmp->pmpCheck(req, mode, pmode, tc);
533 
534  // Let the CPU continue.
535  translation->finish(timingFault, req, tc, mode);
536  } else {
537  // There was a fault during the walk. Let the CPU know.
538  translation->finish(timingFault, req, tc, mode);
539  }
540  return true;
541  }
542 
543  return false;
544 }
545 
546 void
548 {
549  //If we're already waiting for the port to become available, just return.
550  if (retrying)
551  return;
552 
553  //Reads always have priority
554  if (read) {
555  PacketPtr pkt = read;
556  read = NULL;
557  inflight++;
558  if (!walker->sendTiming(this, pkt)) {
559  retrying = true;
560  read = pkt;
561  inflight--;
562  return;
563  }
564  }
565  //Send off as many of the writes as we can.
566  while (writes.size()) {
567  PacketPtr write = writes.back();
568  writes.pop_back();
569  inflight++;
570  if (!walker->sendTiming(this, write)) {
571  retrying = true;
572  writes.push_back(write);
573  inflight--;
574  return;
575  }
576  }
577 }
578 
579 unsigned
581 {
582  return inflight;
583 }
584 
585 bool
587 {
588  return retrying;
589 }
590 
591 bool
593 {
594  return timing;
595 }
596 
597 bool
599 {
600  return started;
601 }
602 
603 void
605 {
606  squashed = true;
607 }
608 
609 void
611 {
612  retrying = false;
613  sendPackets();
614 }
615 
616 Fault
618 {
619  DPRINTF(PageTableWalker, "Raising page fault.\n");
620  return walker->tlb->createPagefault(entry.vaddr, mode);
621 }
622 
623 } // namespace RiscvISA
624 } // namespace gem5
#define DPRINTF(x,...)
Definition: trace.hh:186
virtual bool squashed() const
This function is used by the page table walker to determine if it should translate the a pending requ...
Definition: mmu.hh:84
virtual void finish(const Fault &fault, const RequestPtr &req, ThreadContext *tc, BaseMMU::Mode mode)=0
Tick clockEdge(Cycles cycles=Cycles(0)) const
Determine the tick when a cycle begins, by default the current one, but the argument also enables the...
A Packet is used to encapsulate a transfer between two objects in the memory system (e....
Definition: packet.hh:294
bool isRead() const
Definition: packet.hh:592
Addr getAddr() const
Definition: packet.hh:805
void setLE(T v)
Set the value in the data pointer to v as little endian.
bool isResponse() const
Definition: packet.hh:597
uint32_t payloadDelay
The extra pipelining delay from seeing the packet until the end of payload is transmitted by the comp...
Definition: packet.hh:448
uint32_t headerDelay
The extra delay from seeing the packet until the header is transmitted.
Definition: packet.hh:430
void pushSenderState(SenderState *sender_state)
Push a new sender state to the packet and make the current sender state the predecessor of the new on...
Definition: packet.cc:334
SenderState * popSenderState()
Pop the top of the state stack and return a pointer to it.
Definition: packet.cc:342
RequestPtr req
A pointer to the original request.
Definition: packet.hh:376
unsigned getSize() const
Definition: packet.hh:815
MemCmd cmd
The command field of the packet.
Definition: packet.hh:371
T getLE() const
Get the data in the packet byte swapped from little endian to host endian.
Ports are used to interface objects to each other.
Definition: port.hh:62
bool sendTimingReq(PacketPtr pkt)
Attempt to send a timing request to the responder port by calling its corresponding receive function.
Definition: port.hh:495
@ PHYSICAL
The virtual address is also the physical address.
Definition: request.hh:117
void recvReqRetry()
Called by the peer if sendTimingReq was called on this peer (causing recvTimingReq to be called on th...
bool recvTimingResp(PacketPtr pkt)
Receive a timing response from the peer.
Fault startFunctional(Addr &addr, unsigned &logBytes)
void initState(ThreadContext *_tc, BaseMMU::Mode _mode, bool _isTiming=false)
Fault startFunctional(ThreadContext *_tc, Addr &addr, unsigned &logBytes, BaseMMU::Mode mode)
EventFunctionWrapper startWalkWrapperEvent
Event used to call startWalkWrapper.
Port & getPort(const std::string &if_name, PortID idx=InvalidPortID) override
Get a port with a given name and index.
bool sendTiming(WalkerState *sendingState, PacketPtr pkt)
bool recvTimingResp(PacketPtr pkt)
std::list< WalkerState * > currStates
Fault start(ThreadContext *_tc, BaseMMU::Translation *translation, const RequestPtr &req, BaseMMU::Mode mode)
bool isTimingMode() const
Is the system in timing mode?
Definition: system.hh:273
ThreadContext is the external interface to all thread state for anything outside of the CPU.
STL list class.
Definition: stl.hh:51
bool scheduled() const
Determine if the current event is scheduled.
Definition: eventq.hh:465
void schedule(Event &event, Tick when)
Definition: eventq.hh:1019
virtual Port & getPort(const std::string &if_name, PortID idx=InvalidPortID)
Get a port with a given name and index.
Definition: sim_object.cc:126
atomic_var_t state
Definition: helpers.cc:188
uint8_t flags
Definition: helpers.cc:66
Bitfield< 5, 0 > status
Definition: misc_types.hh:429
Bitfield< 6, 5 > shift
Definition: types.hh:117
const Addr LEVEL_BITS
Definition: pagetable.hh:60
const Addr PageShift
Definition: page_size.hh:53
const Addr LEVEL_MASK
Definition: pagetable.hh:61
@ MISCREG_STATUS
Definition: misc.hh:73
@ MISCREG_SATP
Definition: misc.hh:182
Bitfield< 20 > level
Definition: intmessage.hh:51
Bitfield< 7 > present
Definition: misc.hh:999
Bitfield< 3 > addr
Definition: types.hh:84
ProbePointArg< PacketInfo > Packet
Packet probe point.
Definition: mem.hh:109
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
std::shared_ptr< FaultBase > Fault
Definition: types.hh:248
std::shared_ptr< Request > RequestPtr
Definition: request.hh:92
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
int16_t PortID
Port index/ID type, and a symbolic name for an invalid port id.
Definition: types.hh:245
constexpr decltype(nullptr) NoFault
Definition: types.hh:253
Declaration of a request, the overall memory request consisting of the parts of the request that are ...

Generated on Wed Dec 21 2022 10:22:24 for gem5 by doxygen 1.9.1