|
| 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) |
0 commit comments