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