110298Salexandru.dutu@amd.com/*
210298Salexandru.dutu@amd.com * Copyright (c) 2014 Advanced Micro Devices, Inc.
310298Salexandru.dutu@amd.com * All rights reserved.
410298Salexandru.dutu@amd.com *
510298Salexandru.dutu@amd.com * Redistribution and use in source and binary forms, with or without
610298Salexandru.dutu@amd.com * modification, are permitted provided that the following conditions are
710298Salexandru.dutu@amd.com * met: redistributions of source code must retain the above copyright
810298Salexandru.dutu@amd.com * notice, this list of conditions and the following disclaimer;
910298Salexandru.dutu@amd.com * redistributions in binary form must reproduce the above copyright
1010298Salexandru.dutu@amd.com * notice, this list of conditions and the following disclaimer in the
1110298Salexandru.dutu@amd.com * documentation and/or other materials provided with the distribution;
1210298Salexandru.dutu@amd.com * neither the name of the copyright holders nor the names of its
1310298Salexandru.dutu@amd.com * contributors may be used to endorse or promote products derived from
1410298Salexandru.dutu@amd.com * this software without specific prior written permission.
1510298Salexandru.dutu@amd.com *
1610298Salexandru.dutu@amd.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1710298Salexandru.dutu@amd.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1810298Salexandru.dutu@amd.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1910298Salexandru.dutu@amd.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2010298Salexandru.dutu@amd.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2110298Salexandru.dutu@amd.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2210298Salexandru.dutu@amd.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2310298Salexandru.dutu@amd.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2410298Salexandru.dutu@amd.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2510298Salexandru.dutu@amd.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2610298Salexandru.dutu@amd.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2710298Salexandru.dutu@amd.com *
2810298Salexandru.dutu@amd.com * Authors: Alexandru Dutu
2910298Salexandru.dutu@amd.com */
3010298Salexandru.dutu@amd.com
3110298Salexandru.dutu@amd.com/**
3210298Salexandru.dutu@amd.com * @file
3310298Salexandru.dutu@amd.com * Declaration of a multi-level page table.
3410298Salexandru.dutu@amd.com */
3510298Salexandru.dutu@amd.com
3610298Salexandru.dutu@amd.com#ifndef __MEM_MULTI_LEVEL_PAGE_TABLE_HH__
3710298Salexandru.dutu@amd.com#define __MEM_MULTI_LEVEL_PAGE_TABLE_HH__
3810298Salexandru.dutu@amd.com
3910298Salexandru.dutu@amd.com#include <string>
4010298Salexandru.dutu@amd.com
4110298Salexandru.dutu@amd.com#include "base/types.hh"
4210298Salexandru.dutu@amd.com#include "mem/page_table.hh"
4311800Sbrandon.potter@amd.com
4411800Sbrandon.potter@amd.comclass System;
4510298Salexandru.dutu@amd.com
4610298Salexandru.dutu@amd.com/**
4710298Salexandru.dutu@amd.com * This class implements an in-memory multi-level page table that can be
4810298Salexandru.dutu@amd.com * configured to follow ISA specifications. It can be used instead of the
4910298Salexandru.dutu@amd.com * PageTable class in SE mode to allow CPU models (e.g. X86KvmCPU)
5010298Salexandru.dutu@amd.com * to do a normal page table walk.
5110298Salexandru.dutu@amd.com *
5210298Salexandru.dutu@amd.com * To reduce memory required to store the page table, a multi-level page
5310298Salexandru.dutu@amd.com * table stores its translations similarly with a radix tree. Let n be
5410298Salexandru.dutu@amd.com * the number of levels and {Ln, Ln-1, ..., L1, L0} a set that specifies
5510298Salexandru.dutu@amd.com * the number of entries for each level as base 2 logarithm values. A
5610298Salexandru.dutu@amd.com * multi-level page table will store its translations at level 0 (the
5710298Salexandru.dutu@amd.com * leaves of the tree) and it will be layed out in memory in the
5810298Salexandru.dutu@amd.com * following way:
5910298Salexandru.dutu@amd.com *
6010298Salexandru.dutu@amd.com *                              +------------------------------+
6110298Salexandru.dutu@amd.com * level n                      |Ln-1_E0|Ln-1_E1|...|Ln-1_E2^Ln|
6210298Salexandru.dutu@amd.com *                              +------------------------------+
6310298Salexandru.dutu@amd.com *                                 /       \
6410298Salexandru.dutu@amd.com *            +------------------------+   +------------------------+
6510298Salexandru.dutu@amd.com * level n-1  |Ln-2_E0|...|Ln-2_E2^Ln-1|   |Ln-2_E0|...|Ln-2_E2^Ln-1|
6610298Salexandru.dutu@amd.com *            +------------------------+   +------------------------+
6710298Salexandru.dutu@amd.com *                /            \             /              \
6810298Salexandru.dutu@amd.com *                                  .
6910298Salexandru.dutu@amd.com *                                  .
7010298Salexandru.dutu@amd.com *                                  .
7110298Salexandru.dutu@amd.com *               /                      /               \
7210298Salexandru.dutu@amd.com *          +------------------+   +------------+     +------------+
7310298Salexandru.dutu@amd.com * level 1  |L0_E1|...|L0_E2^L1|   |...|L0_E2^L1| ... |...|L0_E2^L1|
7410298Salexandru.dutu@amd.com *          +------------------+   +------------+     +------------+
7510298Salexandru.dutu@amd.com * , where
7610298Salexandru.dutu@amd.com * +------------------------------+
7710298Salexandru.dutu@amd.com * |Lk-1_E0|Lk-1_E1|...|Lk-1_E2^Lk|
7810298Salexandru.dutu@amd.com * +------------------------------+
7910298Salexandru.dutu@amd.com * is a level k entry that holds 2^Lk entries in Lk-1 level.
8010298Salexandru.dutu@amd.com *
8110298Salexandru.dutu@amd.com * Essentially, a level n entry will contain 2^Ln level n-1 entries,
8210298Salexandru.dutu@amd.com * a level n-1 entry will hold 2^Ln-1 level n-2 entries etc.
8310298Salexandru.dutu@amd.com *
8410298Salexandru.dutu@amd.com * The virtual address is split into offsets that index into the
8510298Salexandru.dutu@amd.com * different levels of the page table.
8610298Salexandru.dutu@amd.com *
8710298Salexandru.dutu@amd.com * +--------------------------------+
8810298Salexandru.dutu@amd.com * |LnOffset|...|L1Offset|PageOffset|
8910298Salexandru.dutu@amd.com * +--------------------------------+
9010298Salexandru.dutu@amd.com *
9110298Salexandru.dutu@amd.com * For example L0Offset will be formed by the bits in range
9210298Salexandru.dutu@amd.com * [log2(PageOffset), log2(PageOffset)+L0].
9310298Salexandru.dutu@amd.com *
9410298Salexandru.dutu@amd.com * For every level of the page table, from n to 1, the base address
9510298Salexandru.dutu@amd.com * of the entry is loaded, the offset in the virtual address for
9610298Salexandru.dutu@amd.com * that particular level is used to index into the entry which
9710298Salexandru.dutu@amd.com * will reveal the memory address of the entry in the next level.
9810298Salexandru.dutu@amd.com *
9910298Salexandru.dutu@amd.com * @see MultiLevelPageTable
10010298Salexandru.dutu@amd.com */
10112460Sgabeblack@google.com
10212460Sgabeblack@google.comnamespace {
10312460Sgabeblack@google.com
10412460Sgabeblack@google.comtemplate <class First, class ...Rest>
10512460Sgabeblack@google.comAddr
10612460Sgabeblack@google.comprepTopTable(System *system, Addr pageSize)
10712460Sgabeblack@google.com{
10812460Sgabeblack@google.com    Addr addr = system->allocPhysPages(First::tableSize());
10912460Sgabeblack@google.com    PortProxy &p = system->physProxy;
11012460Sgabeblack@google.com    p.memsetBlob(addr, 0, First::tableSize() * pageSize);
11112460Sgabeblack@google.com    return addr;
11212460Sgabeblack@google.com}
11312460Sgabeblack@google.com
11412460Sgabeblack@google.comtemplate <class ...Types>
11512460Sgabeblack@google.comstruct LastType;
11612460Sgabeblack@google.com
11712460Sgabeblack@google.comtemplate <class First, class Second, class ...Rest>
11812460Sgabeblack@google.comstruct LastType<First, Second, Rest...>
11912460Sgabeblack@google.com{
12012460Sgabeblack@google.com    typedef typename LastType<Second, Rest...>::type type;
12112460Sgabeblack@google.com};
12212460Sgabeblack@google.com
12312460Sgabeblack@google.comtemplate <class Only>
12412460Sgabeblack@google.comstruct LastType<Only>
12512460Sgabeblack@google.com{
12612460Sgabeblack@google.com    typedef Only type;
12712460Sgabeblack@google.com};
12812460Sgabeblack@google.com
12912460Sgabeblack@google.com
13012460Sgabeblack@google.comtemplate <class ...Types>
13112460Sgabeblack@google.comstruct WalkWrapper;
13212460Sgabeblack@google.com
13312460Sgabeblack@google.comtemplate <class Final, class Only>
13412460Sgabeblack@google.comstruct WalkWrapper<Final, Only>
13512460Sgabeblack@google.com{
13612460Sgabeblack@google.com    static void
13712460Sgabeblack@google.com    walk(System *system, Addr pageSize, Addr table, Addr vaddr,
13812460Sgabeblack@google.com         bool allocate, Final *entry)
13912460Sgabeblack@google.com    {
14012460Sgabeblack@google.com        entry->read(system->physProxy, table, vaddr);
14112460Sgabeblack@google.com    }
14212460Sgabeblack@google.com};
14312460Sgabeblack@google.com
14412460Sgabeblack@google.comtemplate <class Final, class First, class Second, class ...Rest>
14512460Sgabeblack@google.comstruct WalkWrapper<Final, First, Second, Rest...>
14612460Sgabeblack@google.com{
14712460Sgabeblack@google.com    static void
14812460Sgabeblack@google.com    walk(System *system, Addr pageSize, Addr table, Addr vaddr,
14912460Sgabeblack@google.com         bool allocate, Final *entry)
15012460Sgabeblack@google.com    {
15112460Sgabeblack@google.com        First first;
15212460Sgabeblack@google.com        first.read(system->physProxy, table, vaddr);
15312460Sgabeblack@google.com
15412460Sgabeblack@google.com        Addr next;
15512460Sgabeblack@google.com        if (!first.present()) {
15612460Sgabeblack@google.com            fatal_if(!allocate,
15712460Sgabeblack@google.com                     "Page fault while walking the page table.");
15812460Sgabeblack@google.com            next = prepTopTable<Second>(system, pageSize);
15912460Sgabeblack@google.com            first.reset(next);
16012460Sgabeblack@google.com            first.write(system->physProxy);
16112460Sgabeblack@google.com        } else {
16212460Sgabeblack@google.com            next = first.paddr();
16312460Sgabeblack@google.com        }
16412460Sgabeblack@google.com        WalkWrapper<Final, Second, Rest...>::walk(
16512460Sgabeblack@google.com                system, pageSize, next, vaddr, allocate, entry);
16612460Sgabeblack@google.com    }
16712460Sgabeblack@google.com};
16812460Sgabeblack@google.com
16912460Sgabeblack@google.comtemplate <class ...EntryTypes>
17012460Sgabeblack@google.comvoid
17112460Sgabeblack@google.comwalk(System *system, Addr pageSize, Addr table, Addr vaddr,
17212460Sgabeblack@google.com     bool allocate, typename LastType<EntryTypes...>::type *entry)
17312460Sgabeblack@google.com{
17412460Sgabeblack@google.com    WalkWrapper<typename LastType<EntryTypes...>::type, EntryTypes...>::walk(
17512460Sgabeblack@google.com            system, pageSize, table, vaddr, allocate, entry);
17612460Sgabeblack@google.com}
17712460Sgabeblack@google.com
17812460Sgabeblack@google.com}
17912460Sgabeblack@google.com
18012460Sgabeblack@google.com
18112460Sgabeblack@google.comtemplate <class ...EntryTypes>
18212448Sgabeblack@google.comclass MultiLevelPageTable : public EmulationPageTable
18310298Salexandru.dutu@amd.com{
18412460Sgabeblack@google.com    typedef typename LastType<EntryTypes...>::type Final;
18510298Salexandru.dutu@amd.com
18610298Salexandru.dutu@amd.com    /**
18710298Salexandru.dutu@amd.com     * Pointer to System object
18810298Salexandru.dutu@amd.com     */
18910298Salexandru.dutu@amd.com    System *system;
19010298Salexandru.dutu@amd.com
19110298Salexandru.dutu@amd.com    /**
19210298Salexandru.dutu@amd.com     * Physical address to the last level of the page table
19310298Salexandru.dutu@amd.com     */
19412458Sgabeblack@google.com    Addr _basePtr;
19510298Salexandru.dutu@amd.com
19610298Salexandru.dutu@amd.compublic:
19710556Salexandru.dutu@amd.com    MultiLevelPageTable(const std::string &__name, uint64_t _pid,
19812460Sgabeblack@google.com                        System *_sys, Addr pageSize) :
19912460Sgabeblack@google.com            EmulationPageTable(__name, _pid, pageSize), system(_sys)
20012460Sgabeblack@google.com    {}
20110298Salexandru.dutu@amd.com
20212460Sgabeblack@google.com    ~MultiLevelPageTable() {}
20312460Sgabeblack@google.com
20412460Sgabeblack@google.com    void
20514138Sbrandon.potter@amd.com    initState() override
20612460Sgabeblack@google.com    {
20713867Salexandru.dutu@amd.com        if (shared)
20813867Salexandru.dutu@amd.com            return;
20913867Salexandru.dutu@amd.com
21012460Sgabeblack@google.com        _basePtr = prepTopTable<EntryTypes...>(system, pageSize);
21112460Sgabeblack@google.com    }
21210298Salexandru.dutu@amd.com
21312458Sgabeblack@google.com    Addr basePtr() { return _basePtr; }
21412458Sgabeblack@google.com
21512460Sgabeblack@google.com    void
21612460Sgabeblack@google.com    map(Addr vaddr, Addr paddr, int64_t size, uint64_t flags = 0) override
21712460Sgabeblack@google.com    {
21812460Sgabeblack@google.com        EmulationPageTable::map(vaddr, paddr, size, flags);
21912460Sgabeblack@google.com
22012460Sgabeblack@google.com        Final entry;
22112460Sgabeblack@google.com
22212460Sgabeblack@google.com        for (int64_t offset = 0; offset < size; offset += pageSize) {
22312460Sgabeblack@google.com            walk<EntryTypes...>(system, pageSize, _basePtr,
22412460Sgabeblack@google.com                                vaddr + offset, true, &entry);
22512460Sgabeblack@google.com
22612460Sgabeblack@google.com            entry.reset(paddr + offset, true, flags & Uncacheable,
22712460Sgabeblack@google.com                        flags & ReadOnly);
22812460Sgabeblack@google.com            entry.write(system->physProxy);
22912460Sgabeblack@google.com
23012460Sgabeblack@google.com            DPRINTF(MMU, "New mapping: %#x-%#x\n",
23112460Sgabeblack@google.com                    vaddr + offset, paddr + offset);
23212460Sgabeblack@google.com        }
23312460Sgabeblack@google.com    }
23412460Sgabeblack@google.com
23512460Sgabeblack@google.com    void
23612460Sgabeblack@google.com    remap(Addr vaddr, int64_t size, Addr new_vaddr) override
23712460Sgabeblack@google.com    {
23812460Sgabeblack@google.com        EmulationPageTable::remap(vaddr, size, new_vaddr);
23912460Sgabeblack@google.com
24012460Sgabeblack@google.com        Final old_entry, new_entry;
24112460Sgabeblack@google.com
24212460Sgabeblack@google.com        for (int64_t offset = 0; offset < size; offset += pageSize) {
24312460Sgabeblack@google.com            // Unmap the original mapping.
24412460Sgabeblack@google.com            walk<EntryTypes...>(system, pageSize, _basePtr, vaddr + offset,
24512460Sgabeblack@google.com                                false, &old_entry);
24612460Sgabeblack@google.com            old_entry.present(false);
24712460Sgabeblack@google.com            old_entry.write(system->physProxy);
24812460Sgabeblack@google.com
24912460Sgabeblack@google.com            // Map the new one.
25012460Sgabeblack@google.com            walk<EntryTypes...>(system, pageSize, _basePtr, new_vaddr + offset,
25112460Sgabeblack@google.com                                true, &new_entry);
25212460Sgabeblack@google.com            new_entry.reset(old_entry.paddr(), true, old_entry.uncacheable(),
25312460Sgabeblack@google.com                            old_entry.readonly());
25412460Sgabeblack@google.com            new_entry.write(system->physProxy);
25512460Sgabeblack@google.com        }
25612460Sgabeblack@google.com    }
25712460Sgabeblack@google.com
25812460Sgabeblack@google.com    void
25912460Sgabeblack@google.com    unmap(Addr vaddr, int64_t size) override
26012460Sgabeblack@google.com    {
26112460Sgabeblack@google.com        EmulationPageTable::unmap(vaddr, size);
26212460Sgabeblack@google.com
26312460Sgabeblack@google.com        Final entry;
26412460Sgabeblack@google.com
26512460Sgabeblack@google.com        for (int64_t offset = 0; offset < size; offset += pageSize) {
26612460Sgabeblack@google.com            walk<EntryTypes...>(system, pageSize, _basePtr,
26712460Sgabeblack@google.com                                vaddr + offset, false, &entry);
26812460Sgabeblack@google.com            fatal_if(!entry.present(),
26912460Sgabeblack@google.com                     "PageTable::unmap: Address %#x not mapped.", vaddr);
27012460Sgabeblack@google.com            entry.present(false);
27112460Sgabeblack@google.com            entry.write(system->physProxy);
27212460Sgabeblack@google.com            DPRINTF(MMU, "Unmapping: %#x\n", vaddr);
27312460Sgabeblack@google.com        }
27412460Sgabeblack@google.com    }
27512460Sgabeblack@google.com
27612460Sgabeblack@google.com    void
27712460Sgabeblack@google.com    serialize(CheckpointOut &cp) const override
27812460Sgabeblack@google.com    {
27912460Sgabeblack@google.com        EmulationPageTable::serialize(cp);
28012460Sgabeblack@google.com        /** Since, the page table is stored in system memory
28112460Sgabeblack@google.com         * which is serialized separately, we will serialize
28212460Sgabeblack@google.com         * just the base pointer
28312460Sgabeblack@google.com         */
28412460Sgabeblack@google.com        paramOut(cp, "ptable.pointer", _basePtr);
28512460Sgabeblack@google.com    }
28612460Sgabeblack@google.com
28712460Sgabeblack@google.com    void
28812460Sgabeblack@google.com    unserialize(CheckpointIn &cp) override
28912460Sgabeblack@google.com    {
29012460Sgabeblack@google.com        EmulationPageTable::unserialize(cp);
29112460Sgabeblack@google.com        paramIn(cp, "ptable.pointer", _basePtr);
29212460Sgabeblack@google.com    }
29310298Salexandru.dutu@amd.com};
29410298Salexandru.dutu@amd.com#endif // __MEM_MULTI_LEVEL_PAGE_TABLE_HH__
295