gic_v3_its.cc revision 13996:8a567118e670
1/*
2 * Copyright (c) 2019 ARM Limited
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: Giacomo Travaglini
38 */
39
40#include "dev/arm/gic_v3_its.hh"
41
42#include "debug/AddrRanges.hh"
43#include "debug/Drain.hh"
44#include "debug/GIC.hh"
45#include "debug/ITS.hh"
46#include "dev/arm/gic_v3.hh"
47#include "dev/arm/gic_v3_distributor.hh"
48#include "dev/arm/gic_v3_redistributor.hh"
49#include "mem/packet_access.hh"
50
51#define COMMAND(x, method) { x, DispatchEntry(#x, method) }
52
53const AddrRange Gicv3Its::GITS_BASER(0x0100, 0x0138);
54
55ItsProcess::ItsProcess(Gicv3Its &_its)
56  : its(_its), coroutine(nullptr)
57{
58}
59
60ItsProcess::~ItsProcess()
61{
62}
63
64void
65ItsProcess::reinit()
66{
67    coroutine.reset(new Coroutine(
68        std::bind(&ItsProcess::main, this, std::placeholders::_1)));
69}
70
71const std::string
72ItsProcess::name() const
73{
74    return its.name();
75}
76
77ItsAction
78ItsProcess::run(PacketPtr pkt)
79{
80    assert(coroutine != nullptr);
81    assert(*coroutine);
82    return (*coroutine)(pkt).get();
83}
84
85void
86ItsProcess::doRead(Yield &yield, Addr addr, void *ptr, size_t size)
87{
88    ItsAction a;
89    a.type = ItsActionType::SEND_REQ;
90
91    RequestPtr req = std::make_shared<Request>(
92        addr, size, 0, its.masterId);
93
94    req->taskId(ContextSwitchTaskId::DMA);
95
96    a.pkt = new Packet(req, MemCmd::ReadReq);
97    a.pkt->dataStatic(ptr);
98
99    a.delay = 0;
100
101    PacketPtr pkt = yield(a).get();
102
103    assert(pkt);
104    assert(pkt->getSize() >= size);
105
106    delete pkt;
107}
108
109void
110ItsProcess::doWrite(Yield &yield, Addr addr, void *ptr, size_t size)
111{
112    ItsAction a;
113    a.type = ItsActionType::SEND_REQ;
114
115    RequestPtr req = std::make_shared<Request>(
116        addr, size, 0, its.masterId);
117
118    req->taskId(ContextSwitchTaskId::DMA);
119
120    a.pkt = new Packet(req, MemCmd::WriteReq);
121    a.pkt->dataStatic(ptr);
122
123    a.delay = 0;
124
125    PacketPtr pkt = yield(a).get();
126
127    assert(pkt);
128    assert(pkt->getSize() >= size);
129
130    delete pkt;
131}
132
133void
134ItsProcess::terminate(Yield &yield)
135{
136    ItsAction a;
137    a.type = ItsActionType::TERMINATE;
138    a.pkt = NULL;
139    a.delay = 0;
140    yield(a);
141}
142
143void
144ItsProcess::writeDeviceTable(Yield &yield, uint32_t device_id, DTE dte)
145{
146    const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE);
147    const Addr address = base + device_id;
148
149    DPRINTF(ITS, "Writing DTE at address %#x: %#x\n", address, dte);
150
151    doWrite(yield, address, &dte, sizeof(dte));
152}
153
154void
155ItsProcess::writeIrqTranslationTable(
156    Yield &yield, const Addr itt_base, uint32_t event_id, ITTE itte)
157{
158    const Addr address = itt_base + event_id;
159
160    doWrite(yield, address, &itte, sizeof(itte));
161
162    DPRINTF(ITS, "Writing ITTE at address %#x: %#x\n", address, itte);
163}
164
165void
166ItsProcess::writeIrqCollectionTable(
167    Yield &yield, uint32_t collection_id, CTE cte)
168{
169    const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE);
170    const Addr address = base + collection_id;
171
172    doWrite(yield, address, &cte, sizeof(cte));
173
174    DPRINTF(ITS, "Writing CTE at address %#x: %#x\n", address, cte);
175}
176
177uint64_t
178ItsProcess::readDeviceTable(Yield &yield, uint32_t device_id)
179{
180    const Addr base = its.pageAddress(Gicv3Its::DEVICE_TABLE);
181    const Addr address = base + device_id;
182
183    uint64_t dte;
184    doRead(yield, address, &dte, sizeof(dte));
185
186    DPRINTF(ITS, "Reading DTE at address %#x: %#x\n", address, dte);
187    return dte;
188}
189
190uint64_t
191ItsProcess::readIrqTranslationTable(
192    Yield &yield, const Addr itt_base, uint32_t event_id)
193{
194    const Addr address = itt_base + event_id;
195
196    uint64_t itte;
197    doRead(yield, address, &itte, sizeof(itte));
198
199    DPRINTF(ITS, "Reading ITTE at address %#x: %#x\n", address, itte);
200    return itte;
201}
202
203uint64_t
204ItsProcess::readIrqCollectionTable(Yield &yield, uint32_t collection_id)
205{
206    const Addr base = its.pageAddress(Gicv3Its::COLLECTION_TABLE);
207    const Addr address = base + collection_id;
208
209    uint64_t cte;
210    doRead(yield, address, &cte, sizeof(cte));
211
212    DPRINTF(ITS, "Reading CTE at address %#x: %#x\n", address, cte);
213    return cte;
214}
215
216ItsTranslation::ItsTranslation(Gicv3Its &_its)
217  : ItsProcess(_its)
218{
219    reinit();
220    its.pendingTranslations++;
221}
222
223ItsTranslation::~ItsTranslation()
224{
225    assert(its.pendingTranslations >= 1);
226    its.pendingTranslations--;
227}
228
229void
230ItsTranslation::main(Yield &yield)
231{
232    PacketPtr pkt = yield.get();
233
234    const uint32_t device_id = pkt->req->streamId();
235    const uint32_t event_id = pkt->getLE<uint32_t>();
236
237    auto result = translateLPI(yield, device_id, event_id);
238
239    uint32_t intid = result.first;
240    Gicv3Redistributor *redist = result.second;
241
242    // Set the LPI in the redistributor
243    redist->setClrLPI(intid, true);
244
245    // Update the value in GITS_TRANSLATER only once we know
246    // there was no error in the tranlation process (before
247    // terminating the translation
248    its.gitsTranslater = event_id;
249
250    terminate(yield);
251}
252
253std::pair<uint32_t, Gicv3Redistributor *>
254ItsTranslation::translateLPI(Yield &yield, uint32_t device_id,
255                             uint32_t event_id)
256{
257    if (its.deviceOutOfRange(device_id)) {
258        terminate(yield);
259    }
260
261    DTE dte = readDeviceTable(yield, device_id);
262
263    if (!dte.valid || its.idOutOfRange(event_id, dte.ittRange)) {
264        terminate(yield);
265    }
266
267    ITTE itte = readIrqTranslationTable(yield, dte.ittAddress, event_id);
268    const auto collection_id = itte.icid;
269
270    if (!itte.valid || its.collectionOutOfRange(collection_id)) {
271        terminate(yield);
272    }
273
274    CTE cte = readIrqCollectionTable(yield, collection_id);
275
276    if (!cte.valid) {
277        terminate(yield);
278    }
279
280    // Returning the INTID and the target Redistributor
281    return std::make_pair(itte.intNum, its.getRedistributor(cte));
282}
283
284ItsCommand::DispatchTable ItsCommand::cmdDispatcher =
285{
286    COMMAND(CLEAR, &ItsCommand::clear),
287    COMMAND(DISCARD, &ItsCommand::discard),
288    COMMAND(INT, &ItsCommand::doInt),
289    COMMAND(INV, &ItsCommand::inv),
290    COMMAND(INVALL, &ItsCommand::invall),
291    COMMAND(MAPC, &ItsCommand::mapc),
292    COMMAND(MAPD, &ItsCommand::mapd),
293    COMMAND(MAPI, &ItsCommand::mapi),
294    COMMAND(MAPTI, &ItsCommand::mapti),
295    COMMAND(MOVALL, &ItsCommand::movall),
296    COMMAND(MOVI, &ItsCommand::movi),
297    COMMAND(SYNC, &ItsCommand::sync),
298    COMMAND(VINVALL, &ItsCommand::vinvall),
299    COMMAND(VMAPI, &ItsCommand::vmapi),
300    COMMAND(VMAPP, &ItsCommand::vmapp),
301    COMMAND(VMAPTI, &ItsCommand::vmapti),
302    COMMAND(VMOVI, &ItsCommand::vmovi),
303    COMMAND(VMOVP, &ItsCommand::vmovp),
304    COMMAND(VSYNC, &ItsCommand::vsync),
305};
306
307ItsCommand::ItsCommand(Gicv3Its &_its)
308  : ItsProcess(_its)
309{
310    reinit();
311    its.pendingCommands = true;
312}
313
314ItsCommand::~ItsCommand()
315{
316    its.pendingCommands = false;
317}
318
319std::string
320ItsCommand::commandName(uint32_t cmd)
321{
322    const auto entry = cmdDispatcher.find(cmd);
323    return entry != cmdDispatcher.end() ? entry->second.name : "INVALID";
324}
325
326void
327ItsCommand::main(Yield &yield)
328{
329    ItsAction a;
330    a.type = ItsActionType::INITIAL_NOP;
331    a.pkt = nullptr;
332    a.delay = 0;
333    yield(a);
334
335    while (its.gitsCwriter.offset != its.gitsCreadr.offset) {
336        CommandEntry command;
337
338        // Reading the command from CMDQ
339        readCommand(yield, command);
340
341        processCommand(yield, command);
342
343        its.incrementReadPointer();
344    }
345
346    terminate(yield);
347}
348
349void
350ItsCommand::readCommand(Yield &yield, CommandEntry &command)
351{
352    // read the command pointed by GITS_CREADR
353    const Addr cmd_addr =
354        (its.gitsCbaser.physAddr << 12) + (its.gitsCreadr.offset << 5);
355
356    doRead(yield, cmd_addr, &command, sizeof(command));
357
358    DPRINTF(ITS, "Command %s read from queue at address: %#x\n",
359            commandName(command.type), cmd_addr);
360    DPRINTF(ITS, "dw0: %#x dw1: %#x dw2: %#x dw3: %#x\n",
361            command.raw[0], command.raw[1], command.raw[2], command.raw[3]);
362}
363
364void
365ItsCommand::processCommand(Yield &yield, CommandEntry &command)
366{
367    const auto entry = cmdDispatcher.find(command.type);
368
369    if (entry != cmdDispatcher.end()) {
370        // Execute the command
371        entry->second.exec(this, yield, command);
372    } else {
373        panic("Unrecognized command type: %u", command.type);
374    }
375}
376
377void
378ItsCommand::clear(Yield &yield, CommandEntry &command)
379{
380    if (deviceOutOfRange(command)) {
381        its.incrementReadPointer();
382        terminate(yield);
383    }
384
385    DTE dte = readDeviceTable(yield, command.deviceId);
386
387    if (!dte.valid || idOutOfRange(command, dte)) {
388        its.incrementReadPointer();
389        terminate(yield);
390    }
391
392    ITTE itte = readIrqTranslationTable(
393        yield, dte.ittAddress, command.eventId);
394
395    if (!itte.valid) {
396        its.incrementReadPointer();
397        terminate(yield);
398    }
399
400    const auto collection_id = itte.icid;
401    CTE cte = readIrqCollectionTable(yield, collection_id);
402
403    if (!cte.valid) {
404        its.incrementReadPointer();
405        terminate(yield);
406    }
407
408    // Clear the LPI in the redistributor
409    its.getRedistributor(cte)->setClrLPI(itte.intNum, false);
410}
411
412void
413ItsCommand::discard(Yield &yield, CommandEntry &command)
414{
415    if (deviceOutOfRange(command)) {
416        its.incrementReadPointer();
417        terminate(yield);
418    }
419
420    DTE dte = readDeviceTable(yield, command.deviceId);
421
422    if (!dte.valid || idOutOfRange(command, dte)) {
423        its.incrementReadPointer();
424        terminate(yield);
425    }
426
427    ITTE itte = readIrqTranslationTable(
428        yield, dte.ittAddress, command.eventId);
429
430    if (!itte.valid) {
431        its.incrementReadPointer();
432        terminate(yield);
433    }
434
435    const auto collection_id = itte.icid;
436    Gicv3Its::CTE cte = readIrqCollectionTable(yield, collection_id);
437
438    if (!cte.valid) {
439        its.incrementReadPointer();
440        terminate(yield);
441    }
442
443    its.getRedistributor(cte)->setClrLPI(itte.intNum, false);
444
445    // Then removes the mapping from the ITT (invalidating)
446    itte.valid = 0;
447    writeIrqTranslationTable(
448        yield, dte.ittAddress, command.eventId, itte);
449}
450
451void
452ItsCommand::doInt(Yield &yield, CommandEntry &command)
453{
454    if (deviceOutOfRange(command)) {
455        its.incrementReadPointer();
456        terminate(yield);
457    }
458
459    DTE dte = readDeviceTable(yield, command.deviceId);
460
461    if (!dte.valid || idOutOfRange(command, dte)) {
462        its.incrementReadPointer();
463        terminate(yield);
464    }
465
466    ITTE itte = readIrqTranslationTable(
467        yield, dte.ittAddress, command.eventId);
468
469    if (!itte.valid) {
470        its.incrementReadPointer();
471        terminate(yield);
472    }
473
474    const auto collection_id = itte.icid;
475    CTE cte = readIrqCollectionTable(yield, collection_id);
476
477    if (!cte.valid) {
478        its.incrementReadPointer();
479        terminate(yield);
480    }
481
482    // Set the LPI in the redistributor
483    its.getRedistributor(cte)->setClrLPI(itte.intNum, true);
484}
485
486void
487ItsCommand::inv(Yield &yield, CommandEntry &command)
488{
489    if (deviceOutOfRange(command)) {
490        its.incrementReadPointer();
491        terminate(yield);
492    }
493
494    DTE dte = readDeviceTable(yield, command.deviceId);
495
496    if (!dte.valid || idOutOfRange(command, dte)) {
497        its.incrementReadPointer();
498        terminate(yield);
499    }
500
501    ITTE itte = readIrqTranslationTable(
502        yield, dte.ittAddress, command.eventId);
503
504    if (!itte.valid) {
505        its.incrementReadPointer();
506        terminate(yield);
507    }
508
509    const auto collection_id = itte.icid;
510    CTE cte = readIrqCollectionTable(yield, collection_id);
511
512    if (!cte.valid) {
513        its.incrementReadPointer();
514        terminate(yield);
515    }
516    // Do nothing since caching is currently not supported in
517    // Redistributor
518}
519
520void
521ItsCommand::invall(Yield &yield, CommandEntry &command)
522{
523    if (collectionOutOfRange(command)) {
524        its.incrementReadPointer();
525        terminate(yield);
526    }
527
528    const auto icid = bits(command.raw[2], 15, 0);
529
530    CTE cte = readIrqCollectionTable(yield, icid);
531
532    if (!cte.valid) {
533        its.incrementReadPointer();
534        terminate(yield);
535    }
536    // Do nothing since caching is currently not supported in
537    // Redistributor
538}
539
540void
541ItsCommand::mapc(Yield &yield, CommandEntry &command)
542{
543    if (collectionOutOfRange(command)) {
544        its.incrementReadPointer();
545        terminate(yield);
546    }
547
548    CTE cte = 0;
549    cte.valid = bits(command.raw[2], 63);
550    cte.rdBase = bits(command.raw[2], 50, 16);
551
552    const auto icid = bits(command.raw[2], 15, 0);
553
554    writeIrqCollectionTable(yield, icid, cte);
555}
556
557void
558ItsCommand::mapd(Yield &yield, CommandEntry &command)
559{
560    if (deviceOutOfRange(command) || sizeOutOfRange(command)) {
561        its.incrementReadPointer();
562        terminate(yield);
563    }
564
565    DTE dte = 0;
566    dte.valid = bits(command.raw[2], 63);
567    dte.ittAddress = mbits(command.raw[2], 51, 8);
568    dte.ittRange = bits(command.raw[1], 4, 0);
569
570    writeDeviceTable(yield, command.deviceId, dte);
571}
572
573void
574ItsCommand::mapi(Yield &yield, CommandEntry &command)
575{
576    if (deviceOutOfRange(command)) {
577        its.incrementReadPointer();
578        terminate(yield);
579    }
580
581    if (collectionOutOfRange(command)) {
582        its.incrementReadPointer();
583        terminate(yield);
584    }
585
586    DTE dte = readDeviceTable(yield, command.deviceId);
587
588    if (!dte.valid || idOutOfRange(command, dte) ||
589        its.lpiOutOfRange(command.eventId)) {
590
591        its.incrementReadPointer();
592        terminate(yield);
593    }
594
595    Gicv3Its::ITTE itte = readIrqTranslationTable(
596        yield, dte.ittAddress, command.eventId);
597
598    itte.valid = 1;
599    itte.intType = Gicv3Its::PHYSICAL_INTERRUPT;
600    itte.intNum = command.eventId;
601    itte.icid = bits(command.raw[2], 15, 0);
602
603    writeIrqTranslationTable(
604        yield, dte.ittAddress, command.eventId, itte);
605}
606
607void
608ItsCommand::mapti(Yield &yield, CommandEntry &command)
609{
610    if (deviceOutOfRange(command)) {
611        its.incrementReadPointer();
612        terminate(yield);
613    }
614
615    if (collectionOutOfRange(command)) {
616        its.incrementReadPointer();
617        terminate(yield);
618    }
619
620    DTE dte = readDeviceTable(yield, command.deviceId);
621
622    const auto pintid = bits(command.raw[1], 63, 32);
623
624    if (!dte.valid || idOutOfRange(command, dte) ||
625        its.lpiOutOfRange(pintid)) {
626
627        its.incrementReadPointer();
628        terminate(yield);
629    }
630
631    ITTE itte = readIrqTranslationTable(
632        yield, dte.ittAddress, command.eventId);
633
634    itte.valid = 1;
635    itte.intType = Gicv3Its::PHYSICAL_INTERRUPT;
636    itte.intNum = pintid;
637    itte.icid = bits(command.raw[2], 15, 0);
638
639    writeIrqTranslationTable(
640        yield, dte.ittAddress, command.eventId, itte);
641}
642
643void
644ItsCommand::movall(Yield &yield, CommandEntry &command)
645{
646    const uint64_t rd1 = bits(command.raw[2], 50, 16);
647    const uint64_t rd2 = bits(command.raw[3], 50, 16);
648
649    if (rd1 != rd2) {
650        Gicv3Redistributor * redist1 = its.getRedistributor(rd1);
651        Gicv3Redistributor * redist2 = its.getRedistributor(rd2);
652
653        its.moveAllPendingState(redist1, redist2);
654    }
655}
656
657void
658ItsCommand::movi(Yield &yield, CommandEntry &command)
659{
660    if (deviceOutOfRange(command)) {
661        its.incrementReadPointer();
662        terminate(yield);
663    }
664
665    if (collectionOutOfRange(command)) {
666        its.incrementReadPointer();
667        terminate(yield);
668    }
669
670    DTE dte = readDeviceTable(yield, command.deviceId);
671
672    if (!dte.valid || idOutOfRange(command, dte)) {
673        its.incrementReadPointer();
674        terminate(yield);
675    }
676
677    ITTE itte = readIrqTranslationTable(
678        yield, dte.ittAddress, command.eventId);
679
680    if (!itte.valid || itte.intType == Gicv3Its::VIRTUAL_INTERRUPT) {
681        its.incrementReadPointer();
682        terminate(yield);
683    }
684
685    const auto collection_id1 = itte.icid;
686    CTE cte1 = readIrqCollectionTable(yield, collection_id1);
687
688    if (!cte1.valid) {
689        its.incrementReadPointer();
690        terminate(yield);
691    }
692
693    const auto collection_id2 = bits(command.raw[2], 15, 0);
694    CTE cte2 = readIrqCollectionTable(yield, collection_id2);
695
696    if (!cte2.valid) {
697        its.incrementReadPointer();
698        terminate(yield);
699    }
700
701    Gicv3Redistributor *first_redist = its.getRedistributor(cte1);
702    Gicv3Redistributor *second_redist = its.getRedistributor(cte2);
703
704    if (second_redist != first_redist) {
705        // move pending state of the interrupt from one redistributor
706        // to the other.
707        if (first_redist->isPendingLPI(itte.intNum)) {
708            first_redist->setClrLPI(itte.intNum, false);
709            second_redist->setClrLPI(itte.intNum, true);
710        }
711    }
712
713    itte.icid = collection_id2;
714    writeIrqTranslationTable(
715        yield, dte.ittAddress, command.eventId, itte);
716}
717
718void
719ItsCommand::sync(Yield &yield, CommandEntry &command)
720{
721    warn("ITS %s command unimplemented", __func__);
722}
723
724void
725ItsCommand::vinvall(Yield &yield, CommandEntry &command)
726{
727    panic("ITS %s command unimplemented", __func__);
728}
729
730void
731ItsCommand::vmapi(Yield &yield, CommandEntry &command)
732{
733    panic("ITS %s command unimplemented", __func__);
734}
735
736void
737ItsCommand::vmapp(Yield &yield, CommandEntry &command)
738{
739    panic("ITS %s command unimplemented", __func__);
740}
741
742void
743ItsCommand::vmapti(Yield &yield, CommandEntry &command)
744{
745    panic("ITS %s command unimplemented", __func__);
746}
747
748void
749ItsCommand::vmovi(Yield &yield, CommandEntry &command)
750{
751    panic("ITS %s command unimplemented", __func__);
752}
753
754void
755ItsCommand::vmovp(Yield &yield, CommandEntry &command)
756{
757    panic("ITS %s command unimplemented", __func__);
758}
759
760void
761ItsCommand::vsync(Yield &yield, CommandEntry &command)
762{
763    panic("ITS %s command unimplemented", __func__);
764}
765
766Gicv3Its::Gicv3Its(const Gicv3ItsParams *params)
767 : BasicPioDevice(params, params->pio_size),
768   dmaPort(name() + ".dma", *this),
769   gitsControl(0x1),
770   gitsTyper(params->gits_typer),
771   gitsCbaser(0), gitsCreadr(0),
772   gitsCwriter(0), gitsIidr(0),
773   masterId(params->system->getMasterId(this)),
774   gic(nullptr),
775   commandEvent([this] { checkCommandQueue(); }, name()),
776   pendingCommands(false),
777   pendingTranslations(0)
778{
779    for (auto idx = 0; idx < NUM_BASER_REGS; idx++) {
780        BASER gits_baser = 0;
781        gits_baser.type = idx;
782        gits_baser.entrySize = sizeof(uint64_t) - 1;
783        tableBases.push_back(gits_baser);
784    }
785}
786
787void
788Gicv3Its::setGIC(Gicv3 *_gic)
789{
790    assert(!gic);
791    gic = _gic;
792}
793
794AddrRangeList
795Gicv3Its::getAddrRanges() const
796{
797    assert(pioSize != 0);
798    AddrRangeList ranges;
799    DPRINTF(AddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
800    ranges.push_back(RangeSize(pioAddr, pioSize));
801    return ranges;
802}
803
804Tick
805Gicv3Its::read(PacketPtr pkt)
806{
807    const Addr addr = pkt->getAddr() - pioAddr;
808    uint64_t value = 0;
809
810    DPRINTF(GIC, "%s register at addr: %#x\n", __func__, addr);
811
812    switch (addr) {
813      case GITS_CTLR:
814        value = gitsControl;
815        break;
816
817      case GITS_IIDR:
818        value = gitsIidr;
819        break;
820
821      case GITS_TYPER:
822        value = gitsTyper;
823        break;
824
825      case GITS_CBASER:
826        value = gitsCbaser;
827        break;
828
829      case GITS_CWRITER:
830        value = gitsCwriter;
831        break;
832
833      case GITS_CREADR:
834        value = gitsCreadr;
835        break;
836
837      case GITS_TRANSLATER:
838        value = gitsTranslater;
839        break;
840
841      default:
842        if (GITS_BASER.contains(addr)) {
843            auto relative_addr = addr - GITS_BASER.start();
844            auto baser_index = relative_addr / sizeof(uint64_t);
845
846            value = tableBases[baser_index];
847            break;
848        } else {
849            panic("Unrecognized register access\n");
850        }
851    }
852
853    pkt->setUintX(value, LittleEndianByteOrder);
854    pkt->makeAtomicResponse();
855    return pioDelay;
856}
857
858Tick
859Gicv3Its::write(PacketPtr pkt)
860{
861    Addr addr = pkt->getAddr() - pioAddr;
862
863    DPRINTF(GIC, "%s register at addr: %#x\n", __func__, addr);
864
865    switch (addr) {
866      case GITS_CTLR:
867        assert(pkt->getSize() == sizeof(uint32_t));
868        gitsControl = pkt->getLE<uint32_t>();
869        break;
870
871      case GITS_IIDR:
872        panic("GITS_IIDR is Read Only\n");
873
874      case GITS_TYPER:
875        panic("GITS_TYPER is Read Only\n");
876
877      case GITS_CBASER:
878        assert(pkt->getSize() == sizeof(uint64_t));
879        gitsCbaser = pkt->getLE<uint64_t>();
880        gitsCreadr = 0; // Cleared when CBASER gets written
881
882        checkCommandQueue();
883        break;
884
885      case GITS_CWRITER:
886        assert(pkt->getSize() == sizeof(uint64_t));
887        gitsCwriter = pkt->getLE<uint64_t>();
888
889        checkCommandQueue();
890        break;
891
892      case GITS_CREADR:
893        panic("GITS_READR is Read Only\n");
894
895      case GITS_TRANSLATER:
896        if (gitsControl.enabled) {
897            translate(pkt);
898        }
899        break;
900
901      default:
902        if (GITS_BASER.contains(addr)) {
903            auto relative_addr = addr - GITS_BASER.start();
904            auto baser_index = relative_addr / sizeof(uint64_t);
905
906            BASER val = pkt->getLE<uint64_t>();
907
908            panic_if(val.indirect,
909                "We currently don't support two level ITS tables");
910
911            tableBases[baser_index] = val;
912            break;
913        } else {
914            panic("Unrecognized register access\n");
915        }
916    }
917
918    pkt->makeAtomicResponse();
919    return pioDelay;
920}
921
922bool
923Gicv3Its::idOutOfRange(uint32_t event_id, uint8_t itt_range) const
924{
925    const uint32_t id_bits = gitsTyper.idBits;
926    return event_id >= (1ULL << (id_bits + 1)) ||
927        event_id >= ((1ULL << itt_range) + 1);
928}
929
930bool
931Gicv3Its::deviceOutOfRange(uint32_t device_id) const
932{
933    return device_id >= (1ULL << (gitsTyper.devBits + 1));
934}
935
936bool
937Gicv3Its::sizeOutOfRange(uint32_t size) const
938{
939    return size > gitsTyper.idBits;
940}
941
942bool
943Gicv3Its::collectionOutOfRange(uint32_t collection_id) const
944{
945    // If GITS_TYPER.CIL == 0, ITS supports 16-bit CollectionID
946    // Otherwise, #bits is specified by GITS_TYPER.CIDbits
947    const auto cid_bits = gitsTyper.cil == 0 ?
948        16 : gitsTyper.cidBits + 1;
949
950    return collection_id >= (1ULL << cid_bits);
951}
952
953bool
954Gicv3Its::lpiOutOfRange(uint32_t intid) const
955{
956    return intid >= (1ULL << (Gicv3Distributor::IDBITS + 1)) ||
957           (intid < Gicv3Redistributor::SMALLEST_LPI_ID &&
958            intid != Gicv3::INTID_SPURIOUS);
959}
960
961DrainState
962Gicv3Its::drain()
963{
964    if (!pendingCommands && !pendingTranslations) {
965        return DrainState::Drained;
966    } else {
967        DPRINTF(Drain, "GICv3 ITS not drained\n");
968        return DrainState::Draining;
969    }
970}
971
972void
973Gicv3Its::serialize(CheckpointOut & cp) const
974{
975    SERIALIZE_SCALAR(gitsControl);
976    SERIALIZE_SCALAR(gitsTyper);
977    SERIALIZE_SCALAR(gitsCbaser);
978    SERIALIZE_SCALAR(gitsCreadr);
979    SERIALIZE_SCALAR(gitsCwriter);
980    SERIALIZE_SCALAR(gitsIidr);
981
982    SERIALIZE_CONTAINER(tableBases);
983}
984
985void
986Gicv3Its::unserialize(CheckpointIn & cp)
987{
988    UNSERIALIZE_SCALAR(gitsControl);
989    UNSERIALIZE_SCALAR(gitsTyper);
990    UNSERIALIZE_SCALAR(gitsCbaser);
991    UNSERIALIZE_SCALAR(gitsCreadr);
992    UNSERIALIZE_SCALAR(gitsCwriter);
993    UNSERIALIZE_SCALAR(gitsIidr);
994
995    UNSERIALIZE_CONTAINER(tableBases);
996}
997
998void
999Gicv3Its::incrementReadPointer()
1000{
1001    // Make the reader point to the next element
1002    gitsCreadr.offset = gitsCreadr.offset + 1;
1003
1004    // Check for wrapping
1005    auto queue_end = (4096 * (gitsCbaser.size + 1));
1006
1007    if (gitsCreadr.offset == queue_end) {
1008        gitsCreadr.offset = 0;
1009    }
1010}
1011
1012void
1013Gicv3Its::checkCommandQueue()
1014{
1015    if (!gitsControl.enabled || !gitsCbaser.valid)
1016        return;
1017
1018    if (gitsCwriter.offset != gitsCreadr.offset) {
1019        // writer and reader pointing to different command
1020        // entries: queue not empty.
1021        DPRINTF(ITS, "Reading command from queue\n");
1022
1023        if (!pendingCommands) {
1024            auto *cmd_proc = new ItsCommand(*this);
1025
1026            runProcess(cmd_proc, nullptr);
1027        } else {
1028            DPRINTF(ITS, "Waiting for pending command to finish\n");
1029        }
1030    }
1031}
1032
1033Port &
1034Gicv3Its::getPort(const std::string &if_name, PortID idx)
1035{
1036    if (if_name == "dma") {
1037        return dmaPort;
1038    }
1039    return BasicPioDevice::getPort(if_name, idx);
1040}
1041
1042void
1043Gicv3Its::recvReqRetry()
1044{
1045    assert(!packetsToRetry.empty());
1046
1047    while (!packetsToRetry.empty()) {
1048        ItsAction a = packetsToRetry.front();
1049
1050        assert(a.type == ItsActionType::SEND_REQ);
1051
1052        if (!dmaPort.sendTimingReq(a.pkt))
1053            break;
1054
1055        packetsToRetry.pop();
1056    }
1057}
1058
1059bool
1060Gicv3Its::recvTimingResp(PacketPtr pkt)
1061{
1062    // @todo: We need to pay for this and not just zero it out
1063    pkt->headerDelay = pkt->payloadDelay = 0;
1064
1065    ItsProcess *proc =
1066        safe_cast<ItsProcess *>(pkt->popSenderState());
1067
1068    runProcessTiming(proc, pkt);
1069
1070    return true;
1071}
1072
1073ItsAction
1074Gicv3Its::runProcess(ItsProcess *proc, PacketPtr pkt)
1075{
1076    if (sys->isAtomicMode()) {
1077        return runProcessAtomic(proc, pkt);
1078    } else if (sys->isTimingMode()) {
1079        return runProcessTiming(proc, pkt);
1080    } else {
1081        panic("Not in timing or atomic mode\n");
1082    }
1083}
1084
1085ItsAction
1086Gicv3Its::runProcessTiming(ItsProcess *proc, PacketPtr pkt)
1087{
1088    ItsAction action = proc->run(pkt);
1089
1090    switch (action.type) {
1091      case ItsActionType::SEND_REQ:
1092        action.pkt->pushSenderState(proc);
1093
1094        if (packetsToRetry.empty() &&
1095            dmaPort.sendTimingReq(action.pkt)) {
1096
1097        } else {
1098            packetsToRetry.push(action);
1099        }
1100        break;
1101
1102      case ItsActionType::TERMINATE:
1103        delete proc;
1104        if (!pendingCommands && !commandEvent.scheduled()) {
1105            schedule(commandEvent, clockEdge());
1106        }
1107        break;
1108
1109      default:
1110        panic("Unknown action\n");
1111    }
1112
1113    return action;
1114}
1115
1116ItsAction
1117Gicv3Its::runProcessAtomic(ItsProcess *proc, PacketPtr pkt)
1118{
1119    ItsAction action;
1120    Tick delay = 0;
1121    bool terminate = false;
1122
1123    do {
1124        action = proc->run(pkt);
1125
1126        switch (action.type) {
1127          case ItsActionType::SEND_REQ:
1128            delay += dmaPort.sendAtomic(action.pkt);
1129            pkt = action.pkt;
1130            break;
1131
1132          case ItsActionType::TERMINATE:
1133            delete proc;
1134            terminate = true;
1135            break;
1136
1137          default:
1138            panic("Unknown action\n");
1139        }
1140
1141    } while (!terminate);
1142
1143    action.delay = delay;
1144
1145    return action;
1146}
1147
1148void
1149Gicv3Its::translate(PacketPtr pkt)
1150{
1151    DPRINTF(ITS, "Starting Translation Request\n");
1152
1153    auto *proc = new ItsTranslation(*this);
1154    runProcess(proc, pkt);
1155}
1156
1157Gicv3Redistributor*
1158Gicv3Its::getRedistributor(uint64_t rd_base)
1159{
1160    if (gitsTyper.pta == 1) {
1161        // RDBase is a redistributor address
1162        return gic->getRedistributorByAddr(rd_base << 16);
1163    } else {
1164        // RDBase is a redistributor number
1165        return gic->getRedistributor(rd_base);
1166    }
1167}
1168
1169Addr
1170Gicv3Its::pageAddress(Gicv3Its::ItsTables table)
1171{
1172    const BASER base = tableBases[table];
1173    // real address depends on page size
1174    switch (base.pageSize) {
1175      case SIZE_4K:
1176      case SIZE_16K:
1177        return mbits(base, 47, 12);
1178      case SIZE_64K:
1179        return mbits(base, 47, 16) | (bits(base, 15, 12) << 48);
1180      default:
1181        panic("Unsupported page size\n");
1182    }
1183}
1184
1185void
1186Gicv3Its::moveAllPendingState(
1187    Gicv3Redistributor *rd1, Gicv3Redistributor *rd2)
1188{
1189    const uint64_t largest_lpi_id = 1ULL << (rd1->lpiIDBits + 1);
1190    uint8_t lpi_pending_table[largest_lpi_id / 8];
1191
1192    // Copying the pending table from redistributor 1 to redistributor 2
1193    rd1->memProxy->readBlob(
1194        rd1->lpiPendingTablePtr, (uint8_t *)lpi_pending_table,
1195        sizeof(lpi_pending_table));
1196
1197    rd2->memProxy->writeBlob(
1198        rd2->lpiPendingTablePtr, (uint8_t *)lpi_pending_table,
1199        sizeof(lpi_pending_table));
1200
1201    // Clearing pending table in redistributor 2
1202    rd1->memProxy->memsetBlob(
1203        rd1->lpiPendingTablePtr,
1204        0, sizeof(lpi_pending_table));
1205
1206    rd2->updateAndInformCPUInterface();
1207}
1208
1209Gicv3Its *
1210Gicv3ItsParams::create()
1211{
1212    return new Gicv3Its(this);
1213}
1214