/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Thomas Grocutt
 */

#include "arch/arm/faults.hh"
#include "arch/arm/stage2_mmu.hh"
#include "arch/arm/system.hh"
#include "arch/arm/tlb.hh"
#include "cpu/base.hh"
#include "cpu/thread_context.hh"
#include "debug/Checkpoint.hh"
#include "debug/TLB.hh"
#include "debug/TLBVerbose.hh"

using namespace ArmISA;

Stage2MMU::Stage2MMU(const Params *p)
    : SimObject(p), _stage1Tlb(p->tlb), _stage2Tlb(p->stage2_tlb)
{
    stage1Tlb()->setMMU(this);
    stage2Tlb()->setMMU(this);
}

Fault
Stage2MMU::readDataUntimed(ThreadContext *tc, Addr oVAddr, Addr descAddr,
    uint8_t *data, int numBytes, Request::Flags flags, int masterId,
    bool isFunctional)
{
    Fault fault;

    // translate to physical address using the second stage MMU
    Request req = Request();
    req.setVirt(0, descAddr, numBytes, flags | Request::PT_WALK, masterId, 0);
    if (isFunctional) {
        fault = stage2Tlb()->translateFunctional(&req, tc, BaseTLB::Read);
    } else {
        fault = stage2Tlb()->translateAtomic(&req, tc, BaseTLB::Read);
    }

    // Now do the access.
    if (fault == NoFault && !req.getFlags().isSet(Request::NO_ACCESS)) {
        Packet pkt = Packet(&req, MemCmd::ReadReq);
        pkt.dataStatic(data);
        if (isFunctional) {
            stage1Tlb()->getWalkerPort().sendFunctional(&pkt);
        } else {
            stage1Tlb()->getWalkerPort().sendAtomic(&pkt);
        }
        assert(!pkt.isError());
    }

    // If there was a fault annotate it with the flag saying the foult occured
    // while doing a translation for a stage 1 page table walk.
    if (fault != NoFault) {
        ArmFault *armFault = reinterpret_cast<ArmFault *>(fault.get());
        armFault->annotate(ArmFault::S1PTW, true);
        armFault->annotate(ArmFault::OVA, oVAddr);
    }
    return fault;
}

Fault
Stage2MMU::readDataTimed(ThreadContext *tc, Addr descAddr,
    Stage2Translation *translation, int numBytes, Request::Flags flags,
    int masterId)
{
    Fault fault;
    // translate to physical address using the second stage MMU
    translation->setVirt(descAddr, numBytes, flags | Request::PT_WALK, masterId);
    fault = translation->translateTiming(tc);
    return fault;
}

Stage2MMU::Stage2Translation::Stage2Translation(Stage2MMU &_parent,
        uint8_t *_data, Event *_event, Addr _oVAddr)
    : data(_data), event(_event), parent(_parent), oVAddr(_oVAddr),
    fault(NoFault)
{
}

void
Stage2MMU::Stage2Translation::finish(Fault _fault, RequestPtr req, ThreadContext *tc,
    BaseTLB::Mode mode)
{
    fault = _fault;

    // If there was a fault annotate it with the flag saying the foult occured
    // while doing a translation for a stage 1 page table walk.
    if (fault != NoFault) {
        ArmFault *armFault = reinterpret_cast<ArmFault *>(fault.get());
        armFault->annotate(ArmFault::S1PTW, true);
        armFault->annotate(ArmFault::OVA, oVAddr);
    }

    if (_fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
        DmaPort& port = parent.stage1Tlb()->getWalkerPort();
        port.dmaAction(MemCmd::ReadReq, req->getPaddr(), numBytes,
                       event, data, tc->getCpuPtr()->clockPeriod(),
                       req->getFlags());
    } else {
        // We can't do the DMA access as there's been a problem, so tell the
        // event we're done
        event->process();
    }
}

ArmISA::Stage2MMU *
ArmStage2MMUParams::create()
{
    return new ArmISA::Stage2MMU(this);
}
