smmu_v3.cc revision 14039:4991b2a345a1
18012Ssaidi@eecs.umich.edu/*
28029Snate@binkert.org * Copyright (c) 2013, 2018-2019 ARM Limited
38029Snate@binkert.org * All rights reserved
48013Sbinkertn@umich.edu *
58029Snate@binkert.org * The license below extends only to copyright in the software and shall
68029Snate@binkert.org * not be construed as granting a license to any other intellectual
78029Snate@binkert.org * property including but not limited to intellectual property relating
88029Snate@binkert.org * to a hardware implementation of the functionality of the software
98029Snate@binkert.org * licensed hereunder.  You may use the software subject to the license
108029Snate@binkert.org * terms below provided that you ensure that this notice is replicated
118029Snate@binkert.org * unmodified and in its entirety in all distributions of the software,
128029Snate@binkert.org * modified or unmodified, in source code or in binary form.
138029Snate@binkert.org *
148029Snate@binkert.org * Redistribution and use in source and binary forms, with or without
158013Sbinkertn@umich.edu * modification, are permitted provided that the following conditions are
168029Snate@binkert.org * met: redistributions of source code must retain the above copyright
178029Snate@binkert.org * notice, this list of conditions and the following disclaimer;
188029Snate@binkert.org * redistributions in binary form must reproduce the above copyright
198029Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
208029Snate@binkert.org * documentation and/or other materials provided with the distribution;
218029Snate@binkert.org * neither the name of the copyright holders nor the names of its
228029Snate@binkert.org * contributors may be used to endorse or promote products derived from
238029Snate@binkert.org * this software without specific prior written permission.
248029Snate@binkert.org *
258029Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
268029Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
278013Sbinkertn@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
288012Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
297997Ssaidi@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
307997Ssaidi@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
317997Ssaidi@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
327997Ssaidi@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
337997Ssaidi@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
347997Ssaidi@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
358013Sbinkertn@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
368013Sbinkertn@umich.edu *
378013Sbinkertn@umich.edu * Authors: Stan Czerniawski
388013Sbinkertn@umich.edu */
398013Sbinkertn@umich.edu
408013Sbinkertn@umich.edu#include "dev/arm/smmu_v3.hh"
418013Sbinkertn@umich.edu
427997Ssaidi@eecs.umich.edu#include <cstddef>
437997Ssaidi@eecs.umich.edu#include <cstring>
447997Ssaidi@eecs.umich.edu
457997Ssaidi@eecs.umich.edu#include "base/bitfield.hh"
467997Ssaidi@eecs.umich.edu#include "base/cast.hh"
477997Ssaidi@eecs.umich.edu#include "base/logging.hh"
487997Ssaidi@eecs.umich.edu#include "base/trace.hh"
497997Ssaidi@eecs.umich.edu#include "base/types.hh"
507997Ssaidi@eecs.umich.edu#include "debug/Checkpoint.hh"
517997Ssaidi@eecs.umich.edu#include "debug/SMMUv3.hh"
527997Ssaidi@eecs.umich.edu#include "dev/arm/smmu_v3_transl.hh"
537997Ssaidi@eecs.umich.edu#include "mem/packet_access.hh"
547997Ssaidi@eecs.umich.edu#include "sim/system.hh"
557997Ssaidi@eecs.umich.edu
567997Ssaidi@eecs.umich.eduSMMUv3::SMMUv3(SMMUv3Params *params) :
577997Ssaidi@eecs.umich.edu    MemObject(params),
587997Ssaidi@eecs.umich.edu    system(*params->system),
597997Ssaidi@eecs.umich.edu    masterId(params->system->getMasterId(this)),
607997Ssaidi@eecs.umich.edu    masterPort(name() + ".master", *this),
617997Ssaidi@eecs.umich.edu    masterTableWalkPort(name() + ".master_walker", *this),
627997Ssaidi@eecs.umich.edu    controlPort(name() + ".control", *this, params->reg_map),
637997Ssaidi@eecs.umich.edu    tlb(params->tlb_entries, params->tlb_assoc, params->tlb_policy),
648013Sbinkertn@umich.edu    configCache(params->cfg_entries, params->cfg_assoc, params->cfg_policy),
658013Sbinkertn@umich.edu    ipaCache(params->ipa_entries, params->ipa_assoc, params->ipa_policy),
668013Sbinkertn@umich.edu    walkCache({ { params->walk_S1L0, params->walk_S1L1,
678013Sbinkertn@umich.edu                  params->walk_S1L2, params->walk_S1L3,
688013Sbinkertn@umich.edu                  params->walk_S2L0, params->walk_S2L1,
698013Sbinkertn@umich.edu                  params->walk_S2L2, params->walk_S2L3 } },
708013Sbinkertn@umich.edu              params->walk_assoc, params->walk_policy),
718013Sbinkertn@umich.edu    tlbEnable(params->tlb_enable),
728013Sbinkertn@umich.edu    configCacheEnable(params->cfg_enable),
738013Sbinkertn@umich.edu    ipaCacheEnable(params->ipa_enable),
748013Sbinkertn@umich.edu    walkCacheEnable(params->walk_enable),
758013Sbinkertn@umich.edu    tableWalkPortEnable(false),
768013Sbinkertn@umich.edu    walkCacheNonfinalEnable(params->wc_nonfinal_enable),
778013Sbinkertn@umich.edu    walkCacheS1Levels(params->wc_s1_levels),
788013Sbinkertn@umich.edu    walkCacheS2Levels(params->wc_s2_levels),
798013Sbinkertn@umich.edu    masterPortWidth(params->master_port_width),
808013Sbinkertn@umich.edu    tlbSem(params->tlb_slots),
818013Sbinkertn@umich.edu    ifcSmmuSem(1),
828013Sbinkertn@umich.edu    smmuIfcSem(1),
838013Sbinkertn@umich.edu    configSem(params->cfg_slots),
848013Sbinkertn@umich.edu    ipaSem(params->ipa_slots),
857997Ssaidi@eecs.umich.edu    walkSem(params->walk_slots),
867997Ssaidi@eecs.umich.edu    masterPortSem(1),
877997Ssaidi@eecs.umich.edu    transSem(params->xlate_slots),
887997Ssaidi@eecs.umich.edu    ptwSem(params->ptw_slots),
897997Ssaidi@eecs.umich.edu    cycleSem(1),
907997Ssaidi@eecs.umich.edu    tlbLat(params->tlb_lat),
917997Ssaidi@eecs.umich.edu    ifcSmmuLat(params->ifc_smmu_lat),
927997Ssaidi@eecs.umich.edu    smmuIfcLat(params->smmu_ifc_lat),
937997Ssaidi@eecs.umich.edu    configLat(params->cfg_lat),
947997Ssaidi@eecs.umich.edu    ipaLat(params->ipa_lat),
957997Ssaidi@eecs.umich.edu    walkLat(params->walk_lat),
967997Ssaidi@eecs.umich.edu    slaveInterfaces(params->slave_interfaces),
977997Ssaidi@eecs.umich.edu    commandExecutor(name() + ".cmd_exec", *this),
987997Ssaidi@eecs.umich.edu    regsMap(params->reg_map),
997997Ssaidi@eecs.umich.edu    processCommandsEvent(this)
1007997Ssaidi@eecs.umich.edu{
1017997Ssaidi@eecs.umich.edu    fatal_if(regsMap.size() != SMMU_REG_SIZE,
1027997Ssaidi@eecs.umich.edu        "Invalid register map size: %#x different than SMMU_REG_SIZE = %#x\n",
1037997Ssaidi@eecs.umich.edu        regsMap.size(), SMMU_REG_SIZE);
1047997Ssaidi@eecs.umich.edu
1057997Ssaidi@eecs.umich.edu    // Init smmu registers to 0
1067997Ssaidi@eecs.umich.edu    memset(&regs, 0, sizeof(regs));
1077997Ssaidi@eecs.umich.edu
1087997Ssaidi@eecs.umich.edu    // Setup RO ID registers
1097997Ssaidi@eecs.umich.edu    regs.idr0 = params->smmu_idr0;
1107997Ssaidi@eecs.umich.edu    regs.idr1 = params->smmu_idr1;
1117997Ssaidi@eecs.umich.edu    regs.idr2 = params->smmu_idr2;
1127997Ssaidi@eecs.umich.edu    regs.idr3 = params->smmu_idr3;
1138013Sbinkertn@umich.edu    regs.idr4 = params->smmu_idr4;
1148013Sbinkertn@umich.edu    regs.idr5 = params->smmu_idr5;
1158013Sbinkertn@umich.edu    regs.iidr = params->smmu_iidr;
1168013Sbinkertn@umich.edu    regs.aidr = params->smmu_aidr;
1178013Sbinkertn@umich.edu
1188013Sbinkertn@umich.edu    // TODO: At the moment it possible to set the ID registers to hold
1198013Sbinkertn@umich.edu    // any possible value. It would be nice to have a sanity check here
1208013Sbinkertn@umich.edu    // at construction time in case some idx registers are programmed to
1218013Sbinkertn@umich.edu    // store an unallowed values or if the are configuration conflicts.
1228013Sbinkertn@umich.edu    warn("SMMUv3 IDx register values unchecked\n");
1237997Ssaidi@eecs.umich.edu
1247997Ssaidi@eecs.umich.edu    for (auto ifc : slaveInterfaces)
1257997Ssaidi@eecs.umich.edu        ifc->setSMMU(this);
1267997Ssaidi@eecs.umich.edu}
1277997Ssaidi@eecs.umich.edu
1287997Ssaidi@eecs.umich.edubool
1297997Ssaidi@eecs.umich.eduSMMUv3::masterRecvTimingResp(PacketPtr pkt)
1307997Ssaidi@eecs.umich.edu{
1317997Ssaidi@eecs.umich.edu    DPRINTF(SMMUv3, "[t] master resp addr=%#x size=%#x\n",
1327997Ssaidi@eecs.umich.edu        pkt->getAddr(), pkt->getSize());
1337997Ssaidi@eecs.umich.edu
1347997Ssaidi@eecs.umich.edu    // @todo: We need to pay for this and not just zero it out
1357997Ssaidi@eecs.umich.edu    pkt->headerDelay = pkt->payloadDelay = 0;
1367997Ssaidi@eecs.umich.edu
1377997Ssaidi@eecs.umich.edu    SMMUProcess *proc =
1387997Ssaidi@eecs.umich.edu        safe_cast<SMMUProcess *>(pkt->popSenderState());
1397997Ssaidi@eecs.umich.edu
1407997Ssaidi@eecs.umich.edu    runProcessTiming(proc, pkt);
1417997Ssaidi@eecs.umich.edu
1427997Ssaidi@eecs.umich.edu    return true;
1437997Ssaidi@eecs.umich.edu}
1447997Ssaidi@eecs.umich.edu
1457997Ssaidi@eecs.umich.eduvoid
1467997Ssaidi@eecs.umich.eduSMMUv3::masterRecvReqRetry()
1477997Ssaidi@eecs.umich.edu{
1487997Ssaidi@eecs.umich.edu    assert(!packetsToRetry.empty());
1497997Ssaidi@eecs.umich.edu
1507997Ssaidi@eecs.umich.edu    while (!packetsToRetry.empty()) {
1517997Ssaidi@eecs.umich.edu        SMMUAction a = packetsToRetry.front();
1527997Ssaidi@eecs.umich.edu
153        assert(a.type==ACTION_SEND_REQ || a.type==ACTION_SEND_REQ_FINAL);
154
155        DPRINTF(SMMUv3, "[t] master retr addr=%#x size=%#x\n",
156            a.pkt->getAddr(), a.pkt->getSize());
157
158        if (!masterPort.sendTimingReq(a.pkt))
159            break;
160
161        packetsToRetry.pop();
162
163        /*
164         * ACTION_SEND_REQ_FINAL means that we have just forwarded the packet
165         * on the master interface; this means that we no longer hold on to
166         * that transaction and therefore can accept a new one.
167         * If the slave port was stalled then unstall it (send retry).
168         */
169        if (a.type == ACTION_SEND_REQ_FINAL)
170            scheduleSlaveRetries();
171    }
172}
173
174bool
175SMMUv3::masterTableWalkRecvTimingResp(PacketPtr pkt)
176{
177    DPRINTF(SMMUv3, "[t] master HWTW resp addr=%#x size=%#x\n",
178        pkt->getAddr(), pkt->getSize());
179
180    // @todo: We need to pay for this and not just zero it out
181    pkt->headerDelay = pkt->payloadDelay = 0;
182
183    SMMUProcess *proc =
184        safe_cast<SMMUProcess *>(pkt->popSenderState());
185
186    runProcessTiming(proc, pkt);
187
188    return true;
189}
190
191void
192SMMUv3::masterTableWalkRecvReqRetry()
193{
194    assert(tableWalkPortEnable);
195    assert(!packetsTableWalkToRetry.empty());
196
197    while (!packetsTableWalkToRetry.empty()) {
198        SMMUAction a = packetsTableWalkToRetry.front();
199
200        assert(a.type==ACTION_SEND_REQ);
201
202        DPRINTF(SMMUv3, "[t] master HWTW retr addr=%#x size=%#x\n",
203            a.pkt->getAddr(), a.pkt->getSize());
204
205        if (!masterTableWalkPort.sendTimingReq(a.pkt))
206            break;
207
208        packetsTableWalkToRetry.pop();
209    }
210}
211
212void
213SMMUv3::scheduleSlaveRetries()
214{
215    for (auto ifc : slaveInterfaces) {
216        ifc->scheduleDeviceRetry();
217    }
218}
219
220SMMUAction
221SMMUv3::runProcess(SMMUProcess *proc, PacketPtr pkt)
222{
223    if (system.isAtomicMode()) {
224        return runProcessAtomic(proc, pkt);
225    } else if (system.isTimingMode()) {
226        return runProcessTiming(proc, pkt);
227    } else {
228        panic("Not in timing or atomic mode!");
229    }
230}
231
232SMMUAction
233SMMUv3::runProcessAtomic(SMMUProcess *proc, PacketPtr pkt)
234{
235    SMMUAction action;
236    Tick delay = 0;
237    bool finished = false;
238
239    do {
240        action = proc->run(pkt);
241
242        switch (action.type) {
243            case ACTION_SEND_REQ:
244                // Send an MMU initiated request on the table walk port if it is
245                // enabled. Otherwise, fall through and handle same as the final
246                // ACTION_SEND_REQ_FINAL request.
247                if (tableWalkPortEnable) {
248                    delay += masterTableWalkPort.sendAtomic(action.pkt);
249                    pkt = action.pkt;
250                    break;
251                }
252                M5_FALLTHROUGH;
253            case ACTION_SEND_REQ_FINAL:
254                delay += masterPort.sendAtomic(action.pkt);
255                pkt = action.pkt;
256                break;
257
258            case ACTION_SEND_RESP:
259            case ACTION_SEND_RESP_ATS:
260            case ACTION_SLEEP:
261                finished = true;
262                break;
263
264            case ACTION_DELAY:
265                delay += action.delay;
266                break;
267
268            case ACTION_TERMINATE:
269                panic("ACTION_TERMINATE in atomic mode\n");
270
271            default:
272                panic("Unknown action\n");
273        }
274    } while (!finished);
275
276    action.delay = delay;
277
278    return action;
279}
280
281SMMUAction
282SMMUv3::runProcessTiming(SMMUProcess *proc, PacketPtr pkt)
283{
284    SMMUAction action = proc->run(pkt);
285
286    switch (action.type) {
287        case ACTION_SEND_REQ:
288            // Send an MMU initiated request on the table walk port if it is
289            // enabled. Otherwise, fall through and handle same as the final
290            // ACTION_SEND_REQ_FINAL request.
291            if (tableWalkPortEnable) {
292                action.pkt->pushSenderState(proc);
293
294                DPRINTF(SMMUv3, "[t] master HWTW req  addr=%#x size=%#x\n",
295                        action.pkt->getAddr(), action.pkt->getSize());
296
297                if (packetsTableWalkToRetry.empty()
298                        && masterTableWalkPort.sendTimingReq(action.pkt)) {
299                    scheduleSlaveRetries();
300                } else {
301                    DPRINTF(SMMUv3, "[t] master HWTW req  needs retry,"
302                            " qlen=%d\n", packetsTableWalkToRetry.size());
303                    packetsTableWalkToRetry.push(action);
304                }
305
306                break;
307            }
308            M5_FALLTHROUGH;
309        case ACTION_SEND_REQ_FINAL:
310            action.pkt->pushSenderState(proc);
311
312            DPRINTF(SMMUv3, "[t] master req  addr=%#x size=%#x\n",
313                    action.pkt->getAddr(), action.pkt->getSize());
314
315            if (packetsToRetry.empty() && masterPort.sendTimingReq(action.pkt)) {
316                scheduleSlaveRetries();
317            } else {
318                DPRINTF(SMMUv3, "[t] master req  needs retry, qlen=%d\n",
319                        packetsToRetry.size());
320                packetsToRetry.push(action);
321            }
322
323            break;
324
325        case ACTION_SEND_RESP:
326            // @todo: We need to pay for this and not just zero it out
327            action.pkt->headerDelay = action.pkt->payloadDelay = 0;
328
329            DPRINTF(SMMUv3, "[t] slave resp addr=%#x size=%#x\n",
330                    action.pkt->getAddr(),
331                    action.pkt->getSize());
332
333            assert(action.ifc);
334            action.ifc->schedTimingResp(action.pkt);
335
336            delete proc;
337            break;
338
339        case ACTION_SEND_RESP_ATS:
340            // @todo: We need to pay for this and not just zero it out
341            action.pkt->headerDelay = action.pkt->payloadDelay = 0;
342
343            DPRINTF(SMMUv3, "[t] ATS slave resp addr=%#x size=%#x\n",
344                    action.pkt->getAddr(), action.pkt->getSize());
345
346            assert(action.ifc);
347            action.ifc->schedAtsTimingResp(action.pkt);
348
349            delete proc;
350            break;
351
352        case ACTION_DELAY:
353        case ACTION_SLEEP:
354            break;
355
356        case ACTION_TERMINATE:
357            delete proc;
358            break;
359
360        default:
361            panic("Unknown action\n");
362    }
363
364    return action;
365}
366
367void
368SMMUv3::processCommands()
369{
370    DPRINTF(SMMUv3, "processCommands()\n");
371
372    if (system.isAtomicMode()) {
373        SMMUAction a = runProcessAtomic(&commandExecutor, NULL);
374        (void) a;
375    } else if (system.isTimingMode()) {
376        if (!commandExecutor.isBusy())
377            runProcessTiming(&commandExecutor, NULL);
378    } else {
379        panic("Not in timing or atomic mode!");
380    }
381}
382
383void
384SMMUv3::processCommand(const SMMUCommand &cmd)
385{
386    switch (cmd.type) {
387        case CMD_PRF_CONFIG:
388            DPRINTF(SMMUv3, "CMD_PREFETCH_CONFIG - ignored\n");
389            break;
390
391        case CMD_PRF_ADDR:
392            DPRINTF(SMMUv3, "CMD_PREFETCH_ADDR - ignored\n");
393            break;
394
395        case CMD_INV_STE:
396            DPRINTF(SMMUv3, "CMD_INV_STE sid=%#x\n", cmd.data[0]);
397            configCache.invalidateSID(cmd.data[0]);
398            break;
399
400        case CMD_INV_CD:
401            DPRINTF(SMMUv3, "CMD_INV_CD sid=%#x ssid=%#x\n",
402                cmd.data[0], cmd.data[1]);
403            configCache.invalidateSSID(cmd.data[0], cmd.data[1]);
404            break;
405
406        case CMD_INV_CD_ALL:
407            DPRINTF(SMMUv3, "CMD_INV_CD_ALL sid=%#x\n", cmd.data[0]);
408            configCache.invalidateSID(cmd.data[0]);
409            break;
410
411        case CMD_INV_ALL:
412            DPRINTF(SMMUv3, "CMD_INV_ALL\n");
413            configCache.invalidateAll();
414            break;
415
416        case CMD_TLBI_ALL:
417            DPRINTF(SMMUv3, "CMD_TLBI_ALL\n");
418            for (auto slave_interface : slaveInterfaces) {
419                slave_interface->microTLB->invalidateAll();
420                slave_interface->mainTLB->invalidateAll();
421            }
422            tlb.invalidateAll();
423            ipaCache.invalidateAll();
424            walkCache.invalidateAll();
425            break;
426
427        case CMD_TLBI_ASID:
428            DPRINTF(SMMUv3, "CMD_TLBI_ASID asid=%#x vmid=%#x\n",
429                cmd.data[0], cmd.data[1]);
430            for (auto slave_interface : slaveInterfaces) {
431                slave_interface->microTLB->invalidateASID(
432                    cmd.data[0], cmd.data[1]);
433                slave_interface->mainTLB->invalidateASID(
434                    cmd.data[0], cmd.data[1]);
435            }
436            tlb.invalidateASID(cmd.data[0], cmd.data[1]);
437            walkCache.invalidateASID(cmd.data[0], cmd.data[1]);
438            break;
439
440        case CMD_TLBI_VAAL:
441            DPRINTF(SMMUv3, "CMD_TLBI_VAAL va=%#08x vmid=%#x\n",
442                cmd.data[0], cmd.data[1]);
443            for (auto slave_interface : slaveInterfaces) {
444                slave_interface->microTLB->invalidateVAA(
445                    cmd.data[0], cmd.data[1]);
446                slave_interface->mainTLB->invalidateVAA(
447                    cmd.data[0], cmd.data[1]);
448            }
449            tlb.invalidateVAA(cmd.data[0], cmd.data[1]);
450            break;
451
452        case CMD_TLBI_VAA:
453            DPRINTF(SMMUv3, "CMD_TLBI_VAA va=%#08x vmid=%#x\n",
454                cmd.data[0], cmd.data[1]);
455            for (auto slave_interface : slaveInterfaces) {
456                slave_interface->microTLB->invalidateVAA(
457                    cmd.data[0], cmd.data[1]);
458                slave_interface->mainTLB->invalidateVAA(
459                    cmd.data[0], cmd.data[1]);
460            }
461            tlb.invalidateVAA(cmd.data[0], cmd.data[1]);
462            walkCache.invalidateVAA(cmd.data[0], cmd.data[1]);
463            break;
464
465        case CMD_TLBI_VAL:
466            DPRINTF(SMMUv3, "CMD_TLBI_VAL va=%#08x asid=%#x vmid=%#x\n",
467                cmd.data[0], cmd.data[1], cmd.data[2]);
468            for (auto slave_interface : slaveInterfaces) {
469                slave_interface->microTLB->invalidateVA(
470                    cmd.data[0], cmd.data[1], cmd.data[2]);
471                slave_interface->mainTLB->invalidateVA(
472                    cmd.data[0], cmd.data[1], cmd.data[2]);
473            }
474            tlb.invalidateVA(cmd.data[0], cmd.data[1], cmd.data[2]);
475            break;
476
477        case CMD_TLBI_VA:
478            DPRINTF(SMMUv3, "CMD_TLBI_VA va=%#08x asid=%#x vmid=%#x\n",
479                cmd.data[0], cmd.data[1], cmd.data[2]);
480            for (auto slave_interface : slaveInterfaces) {
481                slave_interface->microTLB->invalidateVA(
482                    cmd.data[0], cmd.data[1], cmd.data[2]);
483                slave_interface->mainTLB->invalidateVA(
484                    cmd.data[0], cmd.data[1], cmd.data[2]);
485            }
486            tlb.invalidateVA(cmd.data[0], cmd.data[1], cmd.data[2]);
487            walkCache.invalidateVA(cmd.data[0], cmd.data[1], cmd.data[2]);
488            break;
489
490        case CMD_TLBI_VM_IPAL:
491            DPRINTF(SMMUv3, "CMD_TLBI_VM_IPAL ipa=%#08x vmid=%#x\n",
492                cmd.data[0], cmd.data[1]);
493            // This does not invalidate TLBs containing
494            // combined Stage1 + Stage2 translations, as per the spec.
495            ipaCache.invalidateIPA(cmd.data[0], cmd.data[1]);
496            walkCache.invalidateVMID(cmd.data[1]);
497            break;
498
499        case CMD_TLBI_VM_IPA:
500            DPRINTF(SMMUv3, "CMD_TLBI_VM_IPA ipa=%#08x vmid=%#x\n",
501                cmd.data[0], cmd.data[1]);
502            // This does not invalidate TLBs containing
503            // combined Stage1 + Stage2 translations, as per the spec.
504            ipaCache.invalidateIPA(cmd.data[0], cmd.data[1]);
505            walkCache.invalidateVMID(cmd.data[1]);
506            break;
507
508        case CMD_TLBI_VM_S12:
509            DPRINTF(SMMUv3, "CMD_TLBI_VM_S12 vmid=%#x\n", cmd.data[0]);
510            for (auto slave_interface : slaveInterfaces) {
511                slave_interface->microTLB->invalidateVMID(cmd.data[0]);
512                slave_interface->mainTLB->invalidateVMID(cmd.data[0]);
513            }
514            tlb.invalidateVMID(cmd.data[0]);
515            ipaCache.invalidateVMID(cmd.data[0]);
516            walkCache.invalidateVMID(cmd.data[0]);
517            break;
518
519        case CMD_RESUME_S:
520            DPRINTF(SMMUv3, "CMD_RESUME_S\n");
521            panic("resume unimplemented");
522            break;
523
524        default:
525            warn("Unimplemented command %#x\n", cmd.type);
526            break;
527    }
528}
529
530const PageTableOps*
531SMMUv3::getPageTableOps(uint8_t trans_granule)
532{
533    static V8PageTableOps4k  ptOps4k;
534    static V8PageTableOps64k ptOps64k;
535
536    switch (trans_granule) {
537    case TRANS_GRANULE_4K:  return &ptOps4k;
538    case TRANS_GRANULE_64K: return &ptOps64k;
539    default:
540        panic("Unknown translation granule size %d", trans_granule);
541    }
542}
543
544Tick
545SMMUv3::readControl(PacketPtr pkt)
546{
547    DPRINTF(SMMUv3, "readControl:  addr=%08x size=%d\n",
548            pkt->getAddr(), pkt->getSize());
549
550    int offset = pkt->getAddr() - regsMap.start();
551    assert(offset >= 0 && offset < SMMU_REG_SIZE);
552
553    if (inSecureBlock(offset)) {
554        warn("smmu: secure registers (0x%x) are not implemented\n",
555             offset);
556    }
557
558    auto reg_ptr = regs.data + offset;
559
560    switch (pkt->getSize()) {
561      case sizeof(uint32_t):
562        pkt->setLE<uint32_t>(*reinterpret_cast<uint32_t *>(reg_ptr));
563        break;
564      case sizeof(uint64_t):
565        pkt->setLE<uint64_t>(*reinterpret_cast<uint64_t *>(reg_ptr));
566        break;
567      default:
568        panic("smmu: unallowed access size: %d bytes\n", pkt->getSize());
569        break;
570    }
571
572    pkt->makeAtomicResponse();
573
574    return 0;
575}
576
577Tick
578SMMUv3::writeControl(PacketPtr pkt)
579{
580    int offset = pkt->getAddr() - regsMap.start();
581    assert(offset >= 0 && offset < SMMU_REG_SIZE);
582
583    DPRINTF(SMMUv3, "writeControl: addr=%08x size=%d data=%16x\n",
584            pkt->getAddr(), pkt->getSize(),
585            pkt->getSize() == sizeof(uint64_t) ?
586            pkt->getLE<uint64_t>() : pkt->getLE<uint32_t>());
587
588    switch (offset) {
589        case offsetof(SMMURegs, cr0):
590            assert(pkt->getSize() == sizeof(uint32_t));
591            regs.cr0 = regs.cr0ack = pkt->getLE<uint32_t>();
592            break;
593
594        case offsetof(SMMURegs, cr1):
595        case offsetof(SMMURegs, cr2):
596        case offsetof(SMMURegs, strtab_base_cfg):
597        case offsetof(SMMURegs, eventq_cons):
598        case offsetof(SMMURegs, eventq_irq_cfg1):
599        case offsetof(SMMURegs, priq_cons):
600            assert(pkt->getSize() == sizeof(uint32_t));
601            *reinterpret_cast<uint32_t *>(regs.data + offset) =
602                pkt->getLE<uint32_t>();
603            break;
604
605        case offsetof(SMMURegs, cmdq_prod):
606            assert(pkt->getSize() == sizeof(uint32_t));
607            *reinterpret_cast<uint32_t *>(regs.data + offset) =
608                pkt->getLE<uint32_t>();
609            schedule(processCommandsEvent, nextCycle());
610            break;
611
612        case offsetof(SMMURegs, strtab_base):
613        case offsetof(SMMURegs, eventq_irq_cfg0):
614            assert(pkt->getSize() == sizeof(uint64_t));
615            *reinterpret_cast<uint64_t *>(regs.data + offset) =
616                pkt->getLE<uint64_t>();
617            break;
618
619        case offsetof(SMMURegs, cmdq_base):
620            assert(pkt->getSize() == sizeof(uint64_t));
621            *reinterpret_cast<uint64_t *>(regs.data + offset) =
622                pkt->getLE<uint64_t>();
623            regs.cmdq_cons = 0;
624            regs.cmdq_prod = 0;
625            break;
626
627
628        case offsetof(SMMURegs, eventq_base):
629            assert(pkt->getSize() == sizeof(uint64_t));
630            *reinterpret_cast<uint64_t *>(regs.data + offset) =
631                pkt->getLE<uint64_t>();
632            regs.eventq_cons = 0;
633            regs.eventq_prod = 0;
634            break;
635
636        case offsetof(SMMURegs, priq_base):
637            assert(pkt->getSize() == sizeof(uint64_t));
638            *reinterpret_cast<uint64_t *>(regs.data + offset) =
639                pkt->getLE<uint64_t>();
640            regs.priq_cons = 0;
641            regs.priq_prod = 0;
642            break;
643
644        default:
645            if (inSecureBlock(offset)) {
646                warn("smmu: secure registers (0x%x) are not implemented\n",
647                     offset);
648            } else {
649                warn("smmu: write to read-only/undefined register at 0x%x\n",
650                     offset);
651            }
652    }
653
654    pkt->makeAtomicResponse();
655
656    return 0;
657}
658
659bool
660SMMUv3::inSecureBlock(uint32_t offs) const
661{
662    if (offs >= offsetof(SMMURegs, _secure_regs) && offs < SMMU_SECURE_SZ)
663        return true;
664    else
665        return false;
666}
667
668void
669SMMUv3::init()
670{
671    // make sure both sides are connected and have the same block size
672    if (!masterPort.isConnected())
673        fatal("Master port is not connected.\n");
674
675    // If the second master port is connected for the table walks, enable
676    // the mode to send table walks through this port instead
677    if (masterTableWalkPort.isConnected())
678        tableWalkPortEnable = true;
679
680    // notify the master side  of our address ranges
681    for (auto ifc : slaveInterfaces) {
682        ifc->sendRange();
683    }
684
685    if (controlPort.isConnected())
686        controlPort.sendRangeChange();
687}
688
689void
690SMMUv3::regStats()
691{
692    MemObject::regStats();
693
694    using namespace Stats;
695
696    for (size_t i = 0; i < slaveInterfaces.size(); i++) {
697        slaveInterfaces[i]->microTLB->regStats(
698            csprintf("%s.utlb%d", name(), i));
699        slaveInterfaces[i]->mainTLB->regStats(
700            csprintf("%s.maintlb%d", name(), i));
701    }
702
703    tlb.regStats(name() + ".tlb");
704    configCache.regStats(name() + ".cfg");
705    ipaCache.regStats(name() + ".ipa");
706    walkCache.regStats(name() + ".walk");
707
708    steL1Fetches
709        .name(name() + ".steL1Fetches")
710        .desc("STE L1 fetches")
711        .flags(pdf);
712
713    steFetches
714        .name(name() + ".steFetches")
715        .desc("STE fetches")
716        .flags(pdf);
717
718    cdL1Fetches
719        .name(name() + ".cdL1Fetches")
720        .desc("CD L1 fetches")
721        .flags(pdf);
722
723    cdFetches
724        .name(name() + ".cdFetches")
725        .desc("CD fetches")
726        .flags(pdf);
727
728    translationTimeDist
729        .init(0, 2000000, 2000)
730        .name(name() + ".translationTimeDist")
731        .desc("Time to translate address")
732        .flags(pdf);
733
734    ptwTimeDist
735        .init(0, 2000000, 2000)
736        .name(name() + ".ptwTimeDist")
737        .desc("Time to walk page tables")
738        .flags(pdf);
739}
740
741DrainState
742SMMUv3::drain()
743{
744    panic("SMMUv3 doesn't support draining\n");
745}
746
747void
748SMMUv3::serialize(CheckpointOut &cp) const
749{
750    DPRINTF(Checkpoint, "Serializing SMMUv3\n");
751
752    SERIALIZE_ARRAY(regs.data, sizeof(regs.data) / sizeof(regs.data[0]));
753}
754
755void
756SMMUv3::unserialize(CheckpointIn &cp)
757{
758    DPRINTF(Checkpoint, "Unserializing SMMUv3\n");
759
760    UNSERIALIZE_ARRAY(regs.data, sizeof(regs.data) / sizeof(regs.data[0]));
761}
762
763Port&
764SMMUv3::getPort(const std::string &name, PortID id)
765{
766    if (name == "master") {
767        return masterPort;
768    } else if (name == "master_walker") {
769        return masterTableWalkPort;
770    } else if (name == "control") {
771        return controlPort;
772    } else {
773        return MemObject::getPort(name, id);
774    }
775}
776
777SMMUv3*
778SMMUv3Params::create()
779{
780    return new SMMUv3(this);
781}
782