CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-plic.hh
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5#include <cheri.hh>
7#include <optional>
8#include <stdint.h>
9#include <utils.hh>
10
11/**
12 * Driver for the standard RISC-V Platform-Local Interrupt Controller (PLIC).
13 *
14 * `MaxIntrID` is the largest interrupt number that is used. `SourceID` and
15 * `Priority` are the types used for interrupt source numbers and priorities,
16 * respectively.
17 */
18template<size_t MaxIntrID, typename SourceID, typename Priority>
20{
21 public:
22 /**
23 * Constructor. Initialises the interrupt controller with all interrupts
24 * disabled.
25 */
27 {
28 volatile uint32_t *range = MMIO_CAPABILITY(uint32_t, plic);
29 size_t nSources = (MaxIntrID + 32U) & ~0x1f;
30 // We program the enable bits in groups of 32.
31 size_t nSourcesGroups = nSources >> 5;
32
33 auto setField = [&](auto &field, size_t offset, size_t size) {
34 CHERI::Capability capability{range};
35 capability.address() += offset;
36 capability.bounds() = size;
37 field = capability;
38 };
39 setField(plicPrios, PriorityOffset, nSources * sizeof(uint32_t));
40 setField(plicPendings, PendingOffset, nSources / 8);
41 setField(plicEnables, EnableOffset, nSources / 8);
42 setField(plicThres, ThresholdOffset, sizeof(uint32_t));
43 setField(plicClaim, ClaimOffset, sizeof(uint32_t));
44
45 for (size_t i = 0; i < nSourcesGroups; i++)
46 {
47 plicEnables[i] = 0U;
48 }
49 for (size_t i = 1; i <= MaxIntrID; i++)
50 {
51 plicPrios[i] = 0U;
52 }
53 // We don't make use of threshold control. Leave it at 0 so all
54 // configured interrupts can fire.
55 *plicThres = 0U;
56 }
57
58 /**
59 * Enable the specified interrupt.
60 */
61 void interrupt_enable(SourceID src)
62 {
63 size_t idx = src >> 5;
64 size_t bitPos = src & 0x1f;
65
66 uint32_t enables = plicEnables[idx] | (1U << bitPos);
67 plicEnables[idx] = enables;
68 }
69
70 /**
71 * Disable the specified interrupt.
72 */
73 void interrupt_disable(SourceID src)
74 {
75 size_t idx = src >> 5;
76 size_t bitPos = src & 0x1f;
77
78 uint32_t enables = plicEnables[idx] & ~(1U << bitPos);
79 plicEnables[idx] = enables;
80 }
81
82 /**
83 * Set the priority of the specified interrupt.
84 */
85 void priority_set(SourceID src, Priority prio)
86 {
87 plicPrios[src] = prio;
88 }
89
90 /**
91 * Fetch the interrupt number that fired and prevent it from firing. If
92 * two or more interrupts have fired then this will return the
93 * highest-priority one.
94 *
95 * If no interrupt has fired (for example, because the interrupt line on
96 * the core was raised spuriously) then this returns `stdd:nullopt`.
97 */
98 std::optional<SourceID> interrupt_claim()
99 {
100 uint32_t claim = *plicClaim;
101 // PLIC reserves source ID 0, which means no interrupts.
102 return claim == 0 ? std::nullopt : std::optional{claim};
103 }
104
105 /**
106 * Tell the interrupt controller we've handled a specified interrupt ID.
107 */
108 void interrupt_complete(SourceID src)
109 {
110 *plicClaim = src;
111 }
112
113 private:
114 // generic offsets according to spec
115 static constexpr size_t PriorityOffset = 0x0U;
116 static constexpr size_t PendingOffset = 0x1000U;
117 static constexpr size_t EnableOffset = 0x2000U;
118 static constexpr size_t ThresholdOffset = 0x200000U;
119 static constexpr size_t ClaimOffset = 0x200004U;
120
121 // Bounded capabilities to the individual structure fields.
122 // Ideally these should be contained in one volatile struct, but
123 // they are so far apart (and this lets us apply the bounds once).
124 volatile uint32_t *plicPrios;
125 volatile uint32_t *plicPendings;
126 volatile uint32_t *plicEnables;
127 volatile uint32_t *plicThres;
128 volatile uint32_t *plicClaim;
129};
130
131/**
132 * Type representing no interrupt controller.
133 *
134 * Contains stub implementations of all of the methods.
135 */
136template<size_t MaxIntrID, typename SourceID, typename Priority>
138{
139 public:
140 void interrupt_enable(SourceID) {}
141 void interrupt_disable(SourceID) {}
142
143 void priority_set(SourceID, Priority) {}
144
145 std::optional<SourceID> interrupt_claim()
146 {
147 return std::nullopt;
148 }
149 void interrupt_complete(SourceID) {}
150};
151
152/**
153 * The type for the Programmable Local Interrupt Controller (PLIC) to use.
154 *
155 * If there is no plic device then this provides a stub version.
156 */
157template<size_t MaxIntrID, typename SourceID, typename Priority>
158using Plic =
159#if DEVICE_EXISTS(plic)
161#else
162 NoPlic
163#endif
164 <MaxIntrID, SourceID, Priority>;
C++ helpers for operating on capabilities.
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
AddressProxy address()
Access the address of the capability.
Definition cheri.hh:851
BoundsProxy bounds()
Access (read, set) the capability's bounds.
Definition cheri.hh:867
Type representing no interrupt controller.
Driver for the standard RISC-V Platform-Local Interrupt Controller (PLIC).
void interrupt_disable(SourceID src)
Disable the specified interrupt.
StandardPlic()
Constructor.
void interrupt_complete(SourceID src)
Tell the interrupt controller we've handled a specified interrupt ID.
std::optional< SourceID > interrupt_claim()
Fetch the interrupt number that fired and prevent it from firing.
void priority_set(SourceID src, Priority prio)
Set the priority of the specified interrupt.
void interrupt_enable(SourceID src)
Enable the specified interrupt.
Utility class to delete copy and move contructors.
Definition utils.hh:53
Macros for interacting with the compartmentalisation model in CHERIoT.
#define MMIO_CAPABILITY(type, name)
Provide a capability of the type volatile type * referring to the MMIO region exported in the linker ...
Miscellaneous utility functions and classes.