pagetable_walker.cc revision 7087:fb8d5786ff30
1/*
2 * Copyright (c) 2007 The Hewlett-Packard Development Company
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Gabe Black
38 */
39
40#include "arch/x86/pagetable.hh"
41#include "arch/x86/pagetable_walker.hh"
42#include "arch/x86/tlb.hh"
43#include "base/bitfield.hh"
44#include "cpu/thread_context.hh"
45#include "cpu/base.hh"
46#include "mem/packet_access.hh"
47#include "mem/request.hh"
48#include "sim/system.hh"
49
50namespace X86ISA {
51
52// Unfortunately, the placement of the base field in a page table entry is
53// very erratic and would make a mess here. It might be moved here at some
54// point in the future.
55BitUnion64(PageTableEntry)
56    Bitfield<63> nx;
57    Bitfield<11, 9> avl;
58    Bitfield<8> g;
59    Bitfield<7> ps;
60    Bitfield<6> d;
61    Bitfield<5> a;
62    Bitfield<4> pcd;
63    Bitfield<3> pwt;
64    Bitfield<2> u;
65    Bitfield<1> w;
66    Bitfield<0> p;
67EndBitUnion(PageTableEntry)
68
69Fault
70Walker::doNext(PacketPtr &write)
71{
72    assert(state != Ready && state != Waiting);
73    write = NULL;
74    PageTableEntry pte;
75    if (size == 8)
76        pte = read->get<uint64_t>();
77    else
78        pte = read->get<uint32_t>();
79    VAddr vaddr = entry.vaddr;
80    bool uncacheable = pte.pcd;
81    Addr nextRead = 0;
82    bool doWrite = false;
83    bool badNX = pte.nx && mode == BaseTLB::Execute && enableNX;
84    switch(state) {
85      case LongPML4:
86        DPRINTF(PageTableWalker,
87                "Got long mode PML4 entry %#016x.\n", (uint64_t)pte);
88        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl3 * size;
89        doWrite = !pte.a;
90        pte.a = 1;
91        entry.writable = pte.w;
92        entry.user = pte.u;
93        if (badNX || !pte.p) {
94            stop();
95            return pageFault(pte.p);
96        }
97        entry.noExec = pte.nx;
98        nextState = LongPDP;
99        break;
100      case LongPDP:
101        DPRINTF(PageTableWalker,
102                "Got long mode PDP entry %#016x.\n", (uint64_t)pte);
103        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl2 * size;
104        doWrite = !pte.a;
105        pte.a = 1;
106        entry.writable = entry.writable && pte.w;
107        entry.user = entry.user && pte.u;
108        if (badNX || !pte.p) {
109            stop();
110            return pageFault(pte.p);
111        }
112        nextState = LongPD;
113        break;
114      case LongPD:
115        DPRINTF(PageTableWalker,
116                "Got long mode PD entry %#016x.\n", (uint64_t)pte);
117        doWrite = !pte.a;
118        pte.a = 1;
119        entry.writable = entry.writable && pte.w;
120        entry.user = entry.user && pte.u;
121        if (badNX || !pte.p) {
122            stop();
123            return pageFault(pte.p);
124        }
125        if (!pte.ps) {
126            // 4 KB page
127            entry.size = 4 * (1 << 10);
128            nextRead =
129                ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl1 * size;
130            nextState = LongPTE;
131            break;
132        } else {
133            // 2 MB page
134            entry.size = 2 * (1 << 20);
135            entry.paddr = (uint64_t)pte & (mask(31) << 21);
136            entry.uncacheable = uncacheable;
137            entry.global = pte.g;
138            entry.patBit = bits(pte, 12);
139            entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
140            tlb->insert(entry.vaddr, entry);
141            stop();
142            return NoFault;
143        }
144      case LongPTE:
145        DPRINTF(PageTableWalker,
146                "Got long mode PTE entry %#016x.\n", (uint64_t)pte);
147        doWrite = !pte.a;
148        pte.a = 1;
149        entry.writable = entry.writable && pte.w;
150        entry.user = entry.user && pte.u;
151        if (badNX || !pte.p) {
152            stop();
153            return pageFault(pte.p);
154        }
155        entry.paddr = (uint64_t)pte & (mask(40) << 12);
156        entry.uncacheable = uncacheable;
157        entry.global = pte.g;
158        entry.patBit = bits(pte, 12);
159        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
160        tlb->insert(entry.vaddr, entry);
161        stop();
162        return NoFault;
163      case PAEPDP:
164        DPRINTF(PageTableWalker,
165                "Got legacy mode PAE PDP entry %#08x.\n", (uint32_t)pte);
166        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael2 * size;
167        if (!pte.p) {
168            stop();
169            return pageFault(pte.p);
170        }
171        nextState = PAEPD;
172        break;
173      case PAEPD:
174        DPRINTF(PageTableWalker,
175                "Got legacy mode PAE PD entry %#08x.\n", (uint32_t)pte);
176        doWrite = !pte.a;
177        pte.a = 1;
178        entry.writable = pte.w;
179        entry.user = pte.u;
180        if (badNX || !pte.p) {
181            stop();
182            return pageFault(pte.p);
183        }
184        if (!pte.ps) {
185            // 4 KB page
186            entry.size = 4 * (1 << 10);
187            nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael1 * size;
188            nextState = PAEPTE;
189            break;
190        } else {
191            // 2 MB page
192            entry.size = 2 * (1 << 20);
193            entry.paddr = (uint64_t)pte & (mask(31) << 21);
194            entry.uncacheable = uncacheable;
195            entry.global = pte.g;
196            entry.patBit = bits(pte, 12);
197            entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
198            tlb->insert(entry.vaddr, entry);
199            stop();
200            return NoFault;
201        }
202      case PAEPTE:
203        DPRINTF(PageTableWalker,
204                "Got legacy mode PAE PTE entry %#08x.\n", (uint32_t)pte);
205        doWrite = !pte.a;
206        pte.a = 1;
207        entry.writable = entry.writable && pte.w;
208        entry.user = entry.user && pte.u;
209        if (badNX || !pte.p) {
210            stop();
211            return pageFault(pte.p);
212        }
213        entry.paddr = (uint64_t)pte & (mask(40) << 12);
214        entry.uncacheable = uncacheable;
215        entry.global = pte.g;
216        entry.patBit = bits(pte, 7);
217        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
218        tlb->insert(entry.vaddr, entry);
219        stop();
220        return NoFault;
221      case PSEPD:
222        DPRINTF(PageTableWalker,
223                "Got legacy mode PSE PD entry %#08x.\n", (uint32_t)pte);
224        doWrite = !pte.a;
225        pte.a = 1;
226        entry.writable = pte.w;
227        entry.user = pte.u;
228        if (!pte.p) {
229            stop();
230            return pageFault(pte.p);
231        }
232        if (!pte.ps) {
233            // 4 KB page
234            entry.size = 4 * (1 << 10);
235            nextRead =
236                ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * size;
237            nextState = PTE;
238            break;
239        } else {
240            // 4 MB page
241            entry.size = 4 * (1 << 20);
242            entry.paddr = bits(pte, 20, 13) << 32 | bits(pte, 31, 22) << 22;
243            entry.uncacheable = uncacheable;
244            entry.global = pte.g;
245            entry.patBit = bits(pte, 12);
246            entry.vaddr = entry.vaddr & ~((4 * (1 << 20)) - 1);
247            tlb->insert(entry.vaddr, entry);
248            stop();
249            return NoFault;
250        }
251      case PD:
252        DPRINTF(PageTableWalker,
253                "Got legacy mode PD entry %#08x.\n", (uint32_t)pte);
254        doWrite = !pte.a;
255        pte.a = 1;
256        entry.writable = pte.w;
257        entry.user = pte.u;
258        if (!pte.p) {
259            stop();
260            return pageFault(pte.p);
261        }
262        // 4 KB page
263        entry.size = 4 * (1 << 10);
264        nextRead = ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * size;
265        nextState = PTE;
266        break;
267      case PTE:
268        DPRINTF(PageTableWalker,
269                "Got legacy mode PTE entry %#08x.\n", (uint32_t)pte);
270        doWrite = !pte.a;
271        pte.a = 1;
272        entry.writable = pte.w;
273        entry.user = pte.u;
274        if (!pte.p) {
275            stop();
276            return pageFault(pte.p);
277        }
278        entry.paddr = (uint64_t)pte & (mask(20) << 12);
279        entry.uncacheable = uncacheable;
280        entry.global = pte.g;
281        entry.patBit = bits(pte, 7);
282        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
283        tlb->insert(entry.vaddr, entry);
284        stop();
285        return NoFault;
286      default:
287        panic("Unknown page table walker state %d!\n");
288    }
289    PacketPtr oldRead = read;
290    //If we didn't return, we're setting up another read.
291    Request::Flags flags = oldRead->req->getFlags();
292    flags.set(Request::UNCACHEABLE, uncacheable);
293    RequestPtr request =
294        new Request(nextRead, oldRead->getSize(), flags);
295    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
296    read->allocate();
297    //If we need to write, adjust the read packet to write the modified value
298    //back to memory.
299    if (doWrite) {
300        write = oldRead;
301        write->set<uint64_t>(pte);
302        write->cmd = MemCmd::WriteReq;
303        write->setDest(Packet::Broadcast);
304    } else {
305        write = NULL;
306        delete oldRead->req;
307        delete oldRead;
308    }
309    return NoFault;
310}
311
312Fault
313Walker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
314              RequestPtr _req, BaseTLB::Mode _mode)
315{
316    assert(state == Ready);
317    tc = _tc;
318    req = _req;
319    Addr vaddr = req->getVaddr();
320    mode = _mode;
321    translation = _translation;
322
323    VAddr addr = vaddr;
324
325    //Figure out what we're doing.
326    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
327    Addr top = 0;
328    // Check if we're in long mode or not
329    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
330    size = 8;
331    if (efer.lma) {
332        // Do long mode.
333        state = LongPML4;
334        top = (cr3.longPdtb << 12) + addr.longl4 * size;
335        enableNX = efer.nxe;
336    } else {
337        // We're in some flavor of legacy mode.
338        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
339        if (cr4.pae) {
340            // Do legacy PAE.
341            state = PAEPDP;
342            top = (cr3.paePdtb << 5) + addr.pael3 * size;
343            enableNX = efer.nxe;
344        } else {
345            size = 4;
346            top = (cr3.pdtb << 12) + addr.norml2 * size;
347            if (cr4.pse) {
348                // Do legacy PSE.
349                state = PSEPD;
350            } else {
351                // Do legacy non PSE.
352                state = PD;
353            }
354            enableNX = false;
355        }
356    }
357
358    nextState = Ready;
359    entry.vaddr = vaddr;
360
361    Request::Flags flags = Request::PHYSICAL;
362    if (cr3.pcd)
363        flags.set(Request::UNCACHEABLE);
364    RequestPtr request = new Request(top, size, flags);
365    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
366    read->allocate();
367    Enums::MemoryMode memMode = sys->getMemoryMode();
368    if (memMode == Enums::timing) {
369        nextState = state;
370        state = Waiting;
371        timingFault = NoFault;
372        sendPackets();
373    } else if (memMode == Enums::atomic) {
374        Fault fault;
375        do {
376            port.sendAtomic(read);
377            PacketPtr write = NULL;
378            fault = doNext(write);
379            assert(fault == NoFault || read == NULL);
380            state = nextState;
381            nextState = Ready;
382            if (write)
383                port.sendAtomic(write);
384        } while(read);
385        state = Ready;
386        nextState = Waiting;
387        return fault;
388    } else {
389        panic("Unrecognized memory system mode.\n");
390    }
391    return NoFault;
392}
393
394bool
395Walker::WalkerPort::recvTiming(PacketPtr pkt)
396{
397    return walker->recvTiming(pkt);
398}
399
400bool
401Walker::recvTiming(PacketPtr pkt)
402{
403    if (pkt->isResponse() && !pkt->wasNacked()) {
404        assert(inflight);
405        assert(state == Waiting);
406        assert(!read);
407        inflight--;
408        if (pkt->isRead()) {
409            state = nextState;
410            nextState = Ready;
411            PacketPtr write = NULL;
412            read = pkt;
413            timingFault = doNext(write);
414            state = Waiting;
415            assert(timingFault == NoFault || read == NULL);
416            if (write) {
417                writes.push_back(write);
418            }
419            sendPackets();
420        } else {
421            sendPackets();
422        }
423        if (inflight == 0 && read == NULL && writes.size() == 0) {
424            state = Ready;
425            nextState = Waiting;
426            if (timingFault == NoFault) {
427                /*
428                 * Finish the translation. Now that we now the right entry is
429                 * in the TLB, this should work with no memory accesses.
430                 * There could be new faults unrelated to the table walk like
431                 * permissions violations, so we'll need the return value as
432                 * well.
433                 */
434                bool delayedResponse;
435                Fault fault = tlb->translate(req, tc, NULL, mode,
436                        delayedResponse, true);
437                assert(!delayedResponse);
438                // Let the CPU continue.
439                translation->finish(fault, req, tc, mode);
440            } else {
441                // There was a fault during the walk. Let the CPU know.
442                translation->finish(timingFault, req, tc, mode);
443            }
444        }
445    } else if (pkt->wasNacked()) {
446        pkt->reinitNacked();
447        if (!port.sendTiming(pkt)) {
448            inflight--;
449            retrying = true;
450            if (pkt->isWrite()) {
451                writes.push_back(pkt);
452            } else {
453                assert(!read);
454                read = pkt;
455            }
456        }
457    }
458    return true;
459}
460
461Tick
462Walker::WalkerPort::recvAtomic(PacketPtr pkt)
463{
464    return 0;
465}
466
467void
468Walker::WalkerPort::recvFunctional(PacketPtr pkt)
469{
470    return;
471}
472
473void
474Walker::WalkerPort::recvStatusChange(Status status)
475{
476    if (status == RangeChange) {
477        if (!snoopRangeSent) {
478            snoopRangeSent = true;
479            sendStatusChange(Port::RangeChange);
480        }
481        return;
482    }
483
484    panic("Unexpected recvStatusChange.\n");
485}
486
487void
488Walker::WalkerPort::recvRetry()
489{
490    walker->recvRetry();
491}
492
493void
494Walker::recvRetry()
495{
496    retrying = false;
497    sendPackets();
498}
499
500void
501Walker::sendPackets()
502{
503    //If we're already waiting for the port to become available, just return.
504    if (retrying)
505        return;
506
507    //Reads always have priority
508    if (read) {
509        PacketPtr pkt = read;
510        read = NULL;
511        inflight++;
512        if (!port.sendTiming(pkt)) {
513            retrying = true;
514            read = pkt;
515            inflight--;
516            return;
517        }
518    }
519    //Send off as many of the writes as we can.
520    while (writes.size()) {
521        PacketPtr write = writes.back();
522        writes.pop_back();
523        inflight++;
524        if (!port.sendTiming(write)) {
525            retrying = true;
526            writes.push_back(write);
527            inflight--;
528            return;
529        }
530    }
531}
532
533Port *
534Walker::getPort(const std::string &if_name, int idx)
535{
536    if (if_name == "port")
537        return &port;
538    else
539        panic("No page table walker port named %s!\n", if_name);
540}
541
542Fault
543Walker::pageFault(bool present)
544{
545    DPRINTF(PageTableWalker, "Raising page fault.\n");
546    HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
547    if (mode == BaseTLB::Execute && !enableNX)
548        mode = BaseTLB::Read;
549    return new PageFault(entry.vaddr, present, mode, m5reg.cpl == 3, false);
550}
551
552}
553
554X86ISA::Walker *
555X86PagetableWalkerParams::create()
556{
557    return new X86ISA::Walker(this);
558}
559