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