Skip to content

Commit dcda77b

Browse files
committed
AIA: Add APLIC support
1 parent abe6fd2 commit dcda77b

File tree

9 files changed

+398
-5
lines changed

9 files changed

+398
-5
lines changed

riscv/aplic.cc

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
// See LICENSE for license details.
2+
#include "devices.h"
3+
#include "processor.h"
4+
#include "arith.h"
5+
#include "sim.h"
6+
7+
#define DOMAINCFG 0x0
8+
#define SOURCECFG_BASE 0x4
9+
#define MMSIADDRCFG 0x1bc0
10+
#define MMSIADDRCFGH 0x1bc4
11+
#define SMSIADDRCFG 0x1bc8
12+
#define SMSIADDRCFGH 0x1bcc
13+
#define SETIP_BASE 0x1c00
14+
#define SETIPNUM 0x1cdc
15+
#define IN_CLRIP_BASE 0x1d00
16+
#define CLRIPNUM 0x1ddc
17+
#define SETIE_BASE 0x1e00
18+
#define SETIENUM 0x1edc
19+
#define CLRIE_BASE 0x1f00
20+
#define CLRIENUM 0x1fdc
21+
#define GENMSI 0x3000
22+
#define TARGET_BASE 0x3004
23+
#define IDC 0x4000
24+
25+
#define DOMAINCFG_IE_MASK 0x100
26+
#define DOMAINCFG_DM_MASK 0x4
27+
28+
#define SOURCECFG_D_MASK 0x400
29+
#define SOURCECFG_CHILD_MASK 0x3ff
30+
#define SOURCECFG_SM_MASK 0x7
31+
32+
#define MMSIADDRCFGH_L_MASK 0x80000000
33+
34+
#define GENMSI_HART_MASK 0xfffc0000
35+
#define GENMSI_BUSY_MASK 0x1000
36+
#define GENMSI_EIID_MASK 0x7ff
37+
38+
#define TARGET_HART_MASK 0xfffc0000
39+
#define TARGET_GUEST_MASK 0x3f000
40+
#define TARGET_EIID_MASK 0x7ff
41+
42+
aplic_t::aplic_t(std::vector<processor_t*>& procs, aplic_t *parent) : procs(procs), parent(parent), child(nullptr), domaincfg((0x80 << 24) | DOMAINCFG_DM_MASK), sourcecfg(), mmsiaddrcfgh(MMSIADDRCFGH_L_MASK), ie(), target(), level(), genmsi(0), deleg_mask()
43+
{
44+
}
45+
46+
void aplic_t::delegate(uint32_t id, bool dm)
47+
{
48+
if (!id || id >= APLIC_MAX_DEVICES)
49+
return;
50+
51+
if (!child) {
52+
// child's sourcecfg = 0 when interrupt is changed to delegated
53+
if (!parent->delegated(id) && dm)
54+
sourcecfg[id] = 0;
55+
} else {
56+
uint32_t mask = (1 << (id % 32));
57+
deleg_mask[id / 32] &= ~mask;
58+
deleg_mask[id / 32] |= dm ? mask : 0;
59+
child->delegate(id, dm);
60+
}
61+
}
62+
63+
bool aplic_t::delegated(uint32_t id)
64+
{
65+
if (!id || id >= APLIC_MAX_DEVICES)
66+
return false;
67+
return (sourcecfg[id] & SOURCECFG_D_MASK);
68+
}
69+
70+
uint32_t aplic_t::get_deleg_mask(uint32_t idx) {
71+
if (idx >= APLIC_MAX_DEVICES / 32)
72+
return 0;
73+
if (parent)
74+
return parent->get_deleg_mask(idx);
75+
return deleg_mask[idx];
76+
}
77+
78+
bool aplic_t::interrupt_enabled(uint32_t id)
79+
{
80+
if (!id || id >= APLIC_MAX_DEVICES)
81+
return false;
82+
if (parent && !parent->delegated(id))
83+
return false;
84+
// Domain interrupt enabled, source IE and source mode != inactive (0)
85+
return (domaincfg & DOMAINCFG_IE_MASK) && (ie[id / 32] & (1 << (id % 32))) && (sourcecfg[id] & SOURCECFG_SM_MASK);
86+
}
87+
88+
bool aplic_t::accessible(uint32_t id)
89+
{
90+
if (!id || id >= APLIC_MAX_DEVICES)
91+
return false;
92+
if (!parent)
93+
return true;
94+
return parent->delegated(id);
95+
}
96+
97+
void aplic_t::set_interrupt_level(uint32_t id, int lvl)
98+
{
99+
if (child && delegated(id)) {
100+
child->set_interrupt_level(id, lvl);
101+
return;
102+
}
103+
104+
if (!accessible(id))
105+
return;
106+
107+
uint32_t mask = 1 << (id % 32);
108+
level[id / 32] &= ~mask;
109+
level[id / 32] |= lvl ? mask : 0;
110+
111+
update_interrupt(id);
112+
}
113+
114+
void aplic_t::send_msi(uint32_t proc_id, uint32_t guest, uint32_t eiid)
115+
{
116+
if (!eiid || proc_id >= procs.size())
117+
return;
118+
119+
auto proc = procs[proc_id];
120+
if (!parent) {
121+
proc->imsic->m->pendei(eiid);
122+
} else if (guest) {
123+
if (proc->imsic->vs.count(guest))
124+
proc->imsic->vs[guest]->pendei(eiid);
125+
} else {
126+
proc->imsic->s->pendei(eiid);
127+
}
128+
}
129+
130+
void aplic_t::send_msi(uint32_t id)
131+
{
132+
if (id >= APLIC_MAX_DEVICES)
133+
return;
134+
auto tgt = target[id];
135+
send_msi(get_field(tgt, TARGET_HART_MASK), get_field(tgt, TARGET_GUEST_MASK), get_field(tgt, TARGET_EIID_MASK));
136+
}
137+
138+
void aplic_t::update_interrupt(uint32_t id)
139+
{
140+
// check level and if interrupt enabled, send MSI if necessary
141+
if (id >= APLIC_MAX_DEVICES)
142+
return;
143+
bool lvl = level[id / 32] & (1 << (id % 32));
144+
if (lvl && interrupt_enabled(id))
145+
send_msi(id);
146+
}
147+
148+
void aplic_t::update_interrupt_masked(uint32_t idx, uint32_t mask)
149+
{
150+
if (idx >= APLIC_MAX_DEVICES / 32)
151+
return;
152+
while(mask) {
153+
auto id = ctz(mask);
154+
update_interrupt(idx * 32 + id);
155+
mask &= ~(1 << id);
156+
}
157+
}
158+
159+
bool aplic_t::load(reg_t addr, size_t len, uint8_t* bytes)
160+
{
161+
uint32_t val = 0;
162+
163+
if (len != 4) {
164+
return false;
165+
}
166+
167+
addr &= ~reg_t(3);
168+
169+
if (addr == DOMAINCFG) {
170+
val = domaincfg;
171+
} else if (addr >= SOURCECFG_BASE && addr < MMSIADDRCFG) {
172+
auto idx = (addr - SOURCECFG_BASE) / 4 + 1;
173+
if (accessible(idx))
174+
val = sourcecfg[idx];
175+
} else if (addr == MMSIADDRCFGH) {
176+
val = mmsiaddrcfgh;
177+
} else if (addr >= IN_CLRIP_BASE && addr < CLRIPNUM) {
178+
auto idx = (addr - IN_CLRIP_BASE) / 4;
179+
val = level[idx];
180+
} else if (addr >= SETIE_BASE && addr < SETIENUM) {
181+
auto idx = (addr - SETIE_BASE) / 4;
182+
val = ie[idx];
183+
} else if (addr == GENMSI) {
184+
val = genmsi;
185+
} else if (addr >= TARGET_BASE && addr < IDC) {
186+
auto idx = (addr - TARGET_BASE) / 4 + 1;
187+
if (accessible(idx))
188+
val = target[idx];
189+
}
190+
191+
memcpy(bytes, (uint8_t *)&val, len);
192+
193+
return true;
194+
}
195+
196+
bool aplic_t::store(reg_t addr, size_t len, const uint8_t* bytes)
197+
{
198+
uint32_t val = 0;
199+
200+
if (len != 4) {
201+
return false;
202+
}
203+
204+
addr &= ~reg_t(3);
205+
memcpy((uint8_t *)&val, bytes, len);
206+
207+
if (addr == DOMAINCFG) {
208+
domaincfg &= ~DOMAINCFG_IE_MASK;
209+
domaincfg |= val & DOMAINCFG_IE_MASK;
210+
} else if (addr >= SOURCECFG_BASE && addr < MMSIADDRCFG) {
211+
auto idx = (addr - SOURCECFG_BASE) / 4 + 1;
212+
// write to D bit sets child's sourcecfg to 0
213+
// SM = inactive (0) or Level1 (6)
214+
if (accessible(idx)) {
215+
if (!child && (val & SOURCECFG_D_MASK))
216+
val = 0;
217+
else if (child)
218+
delegate(idx, val & SOURCECFG_D_MASK);
219+
220+
// force SM to be 6 if non-zero
221+
if (val & SOURCECFG_SM_MASK)
222+
val = set_field(val, SOURCECFG_SM_MASK, 6);
223+
224+
sourcecfg[idx] = val;
225+
}
226+
} else if (addr >= SETIP_BASE && addr < SETIPNUM) {
227+
auto idx = (addr - SETIP_BASE) / 4;
228+
val &= get_deleg_mask(idx);
229+
update_interrupt_masked(idx, val);
230+
} else if (addr == SETIPNUM) {
231+
update_interrupt(val);
232+
} else if (addr >= SETIE_BASE && addr < SETIENUM) {
233+
auto idx = (addr - SETIE_BASE) / 4;
234+
val &= get_deleg_mask(idx);
235+
ie[idx] |= val;
236+
} else if (addr == SETIENUM) {
237+
if (accessible(val))
238+
ie[val / 32] |= 1 << (val % 32);
239+
} else if (addr >= CLRIE_BASE && addr < CLRIENUM) {
240+
auto idx = (addr - CLRIE_BASE) / 4;
241+
val &= get_deleg_mask(idx);
242+
ie[idx] &= ~val;
243+
} else if (addr == CLRIENUM) {
244+
if (accessible(val))
245+
ie[val / 32] &= ~(1 << (val % 32));
246+
} else if (addr == GENMSI) {
247+
genmsi = val & (GENMSI_HART_MASK | GENMSI_EIID_MASK);
248+
send_msi(get_field(genmsi, GENMSI_HART_MASK), 0, get_field(genmsi, GENMSI_EIID_MASK));
249+
} else if (addr >= TARGET_BASE && addr < IDC) {
250+
auto idx = (addr - TARGET_BASE) / 4 + 1;
251+
if (accessible(val))
252+
target[idx] = val;
253+
}
254+
255+
return true;
256+
}
257+
258+
std::string aplic_generate_dts(const sim_t* sim, const std::vector<std::string>& sargs UNUSED)
259+
{
260+
auto cfg = sim->get_cfg();
261+
isa_parser_t isa(cfg.isa, cfg.priv);
262+
std::stringstream s;
263+
if (isa.extension_enabled(EXT_SMAIA)) {
264+
s << std::hex
265+
<< " APLIC_M: aplic@" << APLIC_M_BASE << " {\n"
266+
" riscv,delegate = <&APLIC_S 0x01 0x35>;\n"
267+
" riscv,children = <&APLIC_S>;\n"
268+
" riscv,num-sources = <0x35>;\n"
269+
" reg = <0x00 0x" << APLIC_M_BASE << " 0x00 0x" << APLIC_SIZE << ">;\n"
270+
" msi-parent = <&IMSIC_M>;\n"
271+
" interrupt-controller;\n"
272+
" #interrupt-cells = <0x02>;\n"
273+
" compatible = \"riscv,aplic\";\n"
274+
" };\n";
275+
}
276+
if (isa.extension_enabled(EXT_SSAIA)) {
277+
s << std::hex
278+
<< " APLIC_S: aplic@" << APLIC_S_BASE << " {\n"
279+
" riscv,num-sources = <0x35>;\n"
280+
" reg = <0x00 0x" << APLIC_S_BASE << " 0x00 0x" << APLIC_SIZE << ">;\n"
281+
" msi-parent = <&IMSIC_S>;\n"
282+
" interrupt-controller;\n"
283+
" #interrupt-cells = <0x02>;\n"
284+
" compatible = \"riscv,aplic\";\n"
285+
" };\n";
286+
}
287+
return s.str();
288+
}
289+
290+
aplic_t* aplic_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base, const std::vector<std::string>& sargs UNUSED)
291+
{
292+
return nullptr;
293+
}
294+
295+
REGISTER_DEVICE(aplic, aplic_parse_from_fdt, aplic_generate_dts)

riscv/devices.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,40 @@ class plic_t : public abstract_device_t, public abstract_interrupt_controller_t
159159
reg_t offset, uint32_t val);
160160
};
161161

162+
#define APLIC_MAX_DEVICES 1024
163+
class aplic_t : public abstract_device_t, public abstract_interrupt_controller_t {
164+
public:
165+
aplic_t(std::vector<processor_t*>&, aplic_t *parent);
166+
bool load(reg_t addr, size_t len, uint8_t* bytes);
167+
bool store(reg_t addr, size_t len, const uint8_t* bytes);
168+
void set_interrupt_level(uint32_t id, int lvl);
169+
size_t size() { return APLIC_SIZE; }
170+
void set_child(aplic_t *c) { child = c; }
171+
void delegate(uint32_t id, bool dm);
172+
bool delegated(uint32_t id);
173+
uint32_t get_deleg_mask(uint32_t idx);
174+
private:
175+
std::vector<processor_t*>& procs;
176+
class aplic_t *parent;
177+
class aplic_t *child;
178+
uint32_t domaincfg;
179+
uint32_t sourcecfg[APLIC_MAX_DEVICES];
180+
uint32_t mmsiaddrcfgh;
181+
uint32_t ie[APLIC_MAX_DEVICES / 32];
182+
uint32_t target[APLIC_MAX_DEVICES];
183+
uint32_t level[APLIC_MAX_DEVICES / 32];
184+
uint32_t genmsi;
185+
// deleg_mask can be directly applied to other packed registers
186+
uint32_t deleg_mask[APLIC_MAX_DEVICES / 32];
187+
188+
bool interrupt_enabled(uint32_t id);
189+
bool accessible(uint32_t id);
190+
void send_msi(uint32_t proc_id, uint32_t guest, uint32_t eiid);
191+
void send_msi(uint32_t id);
192+
void update_interrupt(uint32_t id);
193+
void update_interrupt_masked(uint32_t idx, uint32_t mask);
194+
};
195+
162196
class ns16550_t : public abstract_device_t {
163197
public:
164198
ns16550_t(abstract_interrupt_controller_t *intctrl,

riscv/dts.cc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,14 +446,14 @@ int fdt_parse_imsics(const void *fdt, reg_t *imsic_m_addr, reg_t *imsic_s_addr,
446446
{
447447
int nodeoffset = -1, len, rc;
448448
const fdt32_t *p;
449-
reg_t addr = 0;
450449
int val;
451450

452451
for (int i = 0; i < 2; i++) {
453452
nodeoffset = fdt_node_offset_by_compatible(fdt, nodeoffset, compatible);
454453
if (nodeoffset < 0)
455454
return nodeoffset;
456455

456+
reg_t addr = 0;
457457
rc = fdt_get_node_addr_size(fdt, nodeoffset, &addr, NULL, "reg");
458458
if (rc < 0)
459459
return -ENODEV;
@@ -470,3 +470,29 @@ int fdt_parse_imsics(const void *fdt, reg_t *imsic_m_addr, reg_t *imsic_s_addr,
470470
}
471471
return 0;
472472
}
473+
474+
int fdt_parse_aplic(const void *fdt, reg_t *aplic_m_addr, reg_t *aplic_s_addr, const char *compatible)
475+
{
476+
int nodeoffset = -1, len, rc;
477+
const fdt32_t *p;
478+
479+
for (int i = 0; i < 2; i++) {
480+
nodeoffset = fdt_node_offset_by_compatible(fdt, nodeoffset, compatible);
481+
if (nodeoffset < 0)
482+
return nodeoffset;
483+
484+
reg_t addr = 0;
485+
rc = fdt_get_node_addr_size(fdt, nodeoffset, &addr, NULL, "reg");
486+
if (rc < 0)
487+
return -ENODEV;
488+
489+
p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "riscv,children", &len);
490+
491+
if (p && aplic_m_addr)
492+
*aplic_m_addr = addr;
493+
else if (!p && aplic_s_addr)
494+
*aplic_s_addr = addr;
495+
}
496+
497+
return 0;
498+
}

riscv/dts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ int fdt_parse_mmu_type(const void *fdt, int cpu_offset, const char **mmu_type);
3333
int fdt_parse_isa(const void *fdt, int cpu_offset, const char **isa_str);
3434
int fdt_parse_hartid(const void *fdt, int cpu_offset, uint32_t *hartid);
3535
int fdt_parse_imsics(const void *fdt, reg_t *imsic_m_addr, reg_t *imsic_s_addr, const char *compatible);
36+
int fdt_parse_aplic(const void *fdt, reg_t *aplic_m_addr, reg_t *aplic_s_addr, const char *compatible);
3637
#endif

0 commit comments

Comments
 (0)