xref: /qemu/hw/intc/sifive_plic.c (revision 7b0f26e420f7988ae8c257c1f4369ba92c9b1594)
11e24429eSMichael Clark /*
21e24429eSMichael Clark  * SiFive PLIC (Platform Level Interrupt Controller)
31e24429eSMichael Clark  *
41e24429eSMichael Clark  * Copyright (c) 2017 SiFive, Inc.
51e24429eSMichael Clark  *
61e24429eSMichael Clark  * This provides a parameterizable interrupt controller based on SiFive's PLIC.
71e24429eSMichael Clark  *
81e24429eSMichael Clark  * This program is free software; you can redistribute it and/or modify it
91e24429eSMichael Clark  * under the terms and conditions of the GNU General Public License,
101e24429eSMichael Clark  * version 2 or later, as published by the Free Software Foundation.
111e24429eSMichael Clark  *
121e24429eSMichael Clark  * This program is distributed in the hope it will be useful, but WITHOUT
131e24429eSMichael Clark  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
141e24429eSMichael Clark  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
151e24429eSMichael Clark  * more details.
161e24429eSMichael Clark  *
171e24429eSMichael Clark  * You should have received a copy of the GNU General Public License along with
181e24429eSMichael Clark  * this program.  If not, see <http://www.gnu.org/licenses/>.
191e24429eSMichael Clark  */
201e24429eSMichael Clark 
211e24429eSMichael Clark #include "qemu/osdep.h"
223e80f690SMarkus Armbruster #include "qapi/error.h"
231e24429eSMichael Clark #include "qemu/log.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
251e24429eSMichael Clark #include "qemu/error-report.h"
261e24429eSMichael Clark #include "hw/sysbus.h"
274f5604c4SAlistair Francis #include "hw/pci/msi.h"
28a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2984fcf3c1SBin Meng #include "hw/intc/sifive_plic.h"
301e24429eSMichael Clark #include "target/riscv/cpu.h"
31dbd3ec54SYifei Jiang #include "migration/vmstate.h"
32f436ecc3SAlistair Francis #include "hw/irq.h"
33ad40be27SYifei Jiang #include "sysemu/kvm.h"
341e24429eSMichael Clark 
35fb926d57SAlistair Francis static bool addr_between(uint32_t addr, uint32_t base, uint32_t num)
36fb926d57SAlistair Francis {
37fb926d57SAlistair Francis     return addr >= base && addr - base < num;
38fb926d57SAlistair Francis }
39fb926d57SAlistair Francis 
401e24429eSMichael Clark static PLICMode char_to_mode(char c)
411e24429eSMichael Clark {
421e24429eSMichael Clark     switch (c) {
431e24429eSMichael Clark     case 'U': return PLICMode_U;
441e24429eSMichael Clark     case 'S': return PLICMode_S;
451e24429eSMichael Clark     case 'M': return PLICMode_M;
461e24429eSMichael Clark     default:
471e24429eSMichael Clark         error_report("plic: invalid mode '%c'", c);
481e24429eSMichael Clark         exit(1);
491e24429eSMichael Clark     }
501e24429eSMichael Clark }
511e24429eSMichael Clark 
52d78940ecSMichael Clark static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
531e24429eSMichael Clark {
54d73415a3SStefan Hajnoczi     uint32_t old, new, cmp = qatomic_read(a);
55d78940ecSMichael Clark 
56d78940ecSMichael Clark     do {
57d78940ecSMichael Clark         old = cmp;
58d78940ecSMichael Clark         new = (old & ~mask) | (value & mask);
59d73415a3SStefan Hajnoczi         cmp = qatomic_cmpxchg(a, old, new);
60d78940ecSMichael Clark     } while (old != cmp);
61d78940ecSMichael Clark 
62d78940ecSMichael Clark     return old;
631e24429eSMichael Clark }
641e24429eSMichael Clark 
65d78940ecSMichael Clark static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
661e24429eSMichael Clark {
67d78940ecSMichael Clark     atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
681e24429eSMichael Clark }
691e24429eSMichael Clark 
70d78940ecSMichael Clark static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
711e24429eSMichael Clark {
72d78940ecSMichael Clark     atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
73d78940ecSMichael Clark }
74d78940ecSMichael Clark 
7541bcc44aSAlistair Francis static uint32_t sifive_plic_claimed(SiFivePLICState *plic, uint32_t addrid)
76d78940ecSMichael Clark {
7741bcc44aSAlistair Francis     uint32_t max_irq = 0;
7841bcc44aSAlistair Francis     uint32_t max_prio = plic->target_priority[addrid];
79d78940ecSMichael Clark     int i, j;
80bb22d391SJim Shu     int num_irq_in_word = 32;
8141bcc44aSAlistair Francis 
821e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
831e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
841e24429eSMichael Clark                         (plic->pending[i] & ~plic->claimed[i]) &
851e24429eSMichael Clark                             plic->enable[addrid * plic->bitfield_words + i];
8641bcc44aSAlistair Francis 
871e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
881e24429eSMichael Clark             continue;
891e24429eSMichael Clark         }
9041bcc44aSAlistair Francis 
91bb22d391SJim Shu         if (i == (plic->bitfield_words - 1)) {
92bb22d391SJim Shu             /*
93bb22d391SJim Shu              * If plic->num_sources is not multiple of 32, num-of-irq in last
94bb22d391SJim Shu              * word is not 32. Compute the num-of-irq of last word to avoid
95bb22d391SJim Shu              * out-of-bound access of source_priority array.
96bb22d391SJim Shu              */
97bb22d391SJim Shu             num_irq_in_word = plic->num_sources - ((plic->bitfield_words - 1) << 5);
98bb22d391SJim Shu         }
99bb22d391SJim Shu 
100bb22d391SJim Shu         for (j = 0; j < num_irq_in_word; j++) {
1011e24429eSMichael Clark             int irq = (i << 5) + j;
1021e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1031e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
10441bcc44aSAlistair Francis 
10541bcc44aSAlistair Francis             if (enabled && prio > max_prio) {
10641bcc44aSAlistair Francis                 max_irq = irq;
10741bcc44aSAlistair Francis                 max_prio = prio;
1081e24429eSMichael Clark             }
1091e24429eSMichael Clark         }
1101e24429eSMichael Clark     }
11141bcc44aSAlistair Francis 
11241bcc44aSAlistair Francis     return max_irq;
1131e24429eSMichael Clark }
1141e24429eSMichael Clark 
1151e24429eSMichael Clark static void sifive_plic_update(SiFivePLICState *plic)
1161e24429eSMichael Clark {
1171e24429eSMichael Clark     int addrid;
1181e24429eSMichael Clark 
1191e24429eSMichael Clark     /* raise irq on harts where this irq is enabled */
1201e24429eSMichael Clark     for (addrid = 0; addrid < plic->num_addrs; addrid++) {
1211e24429eSMichael Clark         uint32_t hartid = plic->addr_config[addrid].hartid;
1221e24429eSMichael Clark         PLICMode mode = plic->addr_config[addrid].mode;
12341bcc44aSAlistair Francis         bool level = !!sifive_plic_claimed(plic, addrid);
124f436ecc3SAlistair Francis 
1251e24429eSMichael Clark         switch (mode) {
1261e24429eSMichael Clark         case PLICMode_M:
127f436ecc3SAlistair Francis             qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level);
1281e24429eSMichael Clark             break;
1291e24429eSMichael Clark         case PLICMode_S:
130f436ecc3SAlistair Francis             qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level);
1311e24429eSMichael Clark             break;
1321e24429eSMichael Clark         default:
1331e24429eSMichael Clark             break;
1341e24429eSMichael Clark         }
1351e24429eSMichael Clark     }
1361e24429eSMichael Clark }
1371e24429eSMichael Clark 
1381e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
1391e24429eSMichael Clark {
1401e24429eSMichael Clark     SiFivePLICState *plic = opaque;
1411e24429eSMichael Clark 
142b79e1c76SAlistair Francis     if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) {
1430feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
144b79e1c76SAlistair Francis 
1451e24429eSMichael Clark         return plic->source_priority[irq];
146b79e1c76SAlistair Francis     } else if (addr_between(addr, plic->pending_base, plic->num_sources >> 3)) {
147e41848e5SMichael Clark         uint32_t word = (addr - plic->pending_base) >> 2;
148b79e1c76SAlistair Francis 
1491e24429eSMichael Clark         return plic->pending[word];
150b79e1c76SAlistair Francis     } else if (addr_between(addr, plic->enable_base,
151b79e1c76SAlistair Francis                             plic->num_addrs * plic->enable_stride)) {
1521e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
1531e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
154b79e1c76SAlistair Francis 
1551e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
1561e24429eSMichael Clark             return plic->enable[addrid * plic->bitfield_words + wordid];
1571e24429eSMichael Clark         }
158b79e1c76SAlistair Francis     } else if (addr_between(addr, plic->context_base,
159b79e1c76SAlistair Francis                             plic->num_addrs * plic->context_stride)) {
1601e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
1611e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
162b79e1c76SAlistair Francis 
1631e24429eSMichael Clark         if (contextid == 0) {
1641e24429eSMichael Clark             return plic->target_priority[addrid];
1651e24429eSMichael Clark         } else if (contextid == 4) {
16641bcc44aSAlistair Francis             uint32_t max_irq = sifive_plic_claimed(plic, addrid);
16741bcc44aSAlistair Francis 
16841bcc44aSAlistair Francis             if (max_irq) {
16941bcc44aSAlistair Francis                 sifive_plic_set_pending(plic, max_irq, false);
17041bcc44aSAlistair Francis                 sifive_plic_set_claimed(plic, max_irq, true);
17141bcc44aSAlistair Francis             }
172b79e1c76SAlistair Francis 
17355765822SJessica Clarke             sifive_plic_update(plic);
17441bcc44aSAlistair Francis             return max_irq;
1751e24429eSMichael Clark         }
1761e24429eSMichael Clark     }
1771e24429eSMichael Clark 
17879bcac25SAlistair Francis     qemu_log_mask(LOG_GUEST_ERROR,
17979bcac25SAlistair Francis                   "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
18079bcac25SAlistair Francis                   __func__, addr);
1811e24429eSMichael Clark     return 0;
1821e24429eSMichael Clark }
1831e24429eSMichael Clark 
1841e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
1851e24429eSMichael Clark         unsigned size)
1861e24429eSMichael Clark {
1871e24429eSMichael Clark     SiFivePLICState *plic = opaque;
1881e24429eSMichael Clark 
189fb926d57SAlistair Francis     if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) {
1900feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
191fb926d57SAlistair Francis 
1921aae4a12SJim Shu         if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
1931aae4a12SJim Shu             /*
1941aae4a12SJim Shu              * if "num_priorities + 1" is power-of-2, make each register bit of
1951aae4a12SJim Shu              * interrupt priority WARL (Write-Any-Read-Legal). Just filter
1961aae4a12SJim Shu              * out the access to unsupported priority bits.
1971aae4a12SJim Shu              */
1981aae4a12SJim Shu             plic->source_priority[irq] = value % (plic->num_priorities + 1);
1991aae4a12SJim Shu             sifive_plic_update(plic);
2001aae4a12SJim Shu         } else if (value <= plic->num_priorities) {
20155144a1fSJim Shu             plic->source_priority[irq] = value;
20255765822SJessica Clarke             sifive_plic_update(plic);
20355144a1fSJim Shu         }
204fb926d57SAlistair Francis     } else if (addr_between(addr, plic->pending_base,
205fb926d57SAlistair Francis                             plic->num_sources >> 3)) {
20679bcac25SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
20779bcac25SAlistair Francis                       "%s: invalid pending write: 0x%" HWADDR_PRIx "",
20879bcac25SAlistair Francis                       __func__, addr);
209fb926d57SAlistair Francis     } else if (addr_between(addr, plic->enable_base,
210fb926d57SAlistair Francis                             plic->num_addrs * plic->enable_stride)) {
2111e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2121e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
213fb926d57SAlistair Francis 
2141e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
2151e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + wordid] = value;
216fb926d57SAlistair Francis         } else {
217fb926d57SAlistair Francis             qemu_log_mask(LOG_GUEST_ERROR,
218fb926d57SAlistair Francis                           "%s: Invalid enable write 0x%" HWADDR_PRIx "\n",
219fb926d57SAlistair Francis                           __func__, addr);
2201e24429eSMichael Clark         }
221fb926d57SAlistair Francis     } else if (addr_between(addr, plic->context_base,
222fb926d57SAlistair Francis                             plic->num_addrs * plic->context_stride)) {
2231e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
2241e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
225fb926d57SAlistair Francis 
2261e24429eSMichael Clark         if (contextid == 0) {
2271aae4a12SJim Shu             if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
2281aae4a12SJim Shu                 /*
2291aae4a12SJim Shu                  * if "num_priorities + 1" is power-of-2, each register bit of
2301aae4a12SJim Shu                  * interrupt priority is WARL (Write-Any-Read-Legal). Just
2311aae4a12SJim Shu                  * filter out the access to unsupported priority bits.
2321aae4a12SJim Shu                  */
2331aae4a12SJim Shu                 plic->target_priority[addrid] = value %
2341aae4a12SJim Shu                                                 (plic->num_priorities + 1);
2351aae4a12SJim Shu                 sifive_plic_update(plic);
2361aae4a12SJim Shu             } else if (value <= plic->num_priorities) {
2371e24429eSMichael Clark                 plic->target_priority[addrid] = value;
2381e24429eSMichael Clark                 sifive_plic_update(plic);
2391e24429eSMichael Clark             }
2401e24429eSMichael Clark         } else if (contextid == 4) {
2411e24429eSMichael Clark             if (value < plic->num_sources) {
2421e24429eSMichael Clark                 sifive_plic_set_claimed(plic, value, false);
2431e24429eSMichael Clark                 sifive_plic_update(plic);
2441e24429eSMichael Clark             }
245fb926d57SAlistair Francis         } else {
246fb926d57SAlistair Francis             qemu_log_mask(LOG_GUEST_ERROR,
247fb926d57SAlistair Francis                           "%s: Invalid context write 0x%" HWADDR_PRIx "\n",
248fb926d57SAlistair Francis                           __func__, addr);
2491e24429eSMichael Clark         }
250fb926d57SAlistair Francis     } else {
25179bcac25SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
25279bcac25SAlistair Francis                       "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
25379bcac25SAlistair Francis                       __func__, addr);
2541e24429eSMichael Clark     }
255fb926d57SAlistair Francis }
2561e24429eSMichael Clark 
2571e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = {
2581e24429eSMichael Clark     .read = sifive_plic_read,
2591e24429eSMichael Clark     .write = sifive_plic_write,
2601e24429eSMichael Clark     .endianness = DEVICE_LITTLE_ENDIAN,
2611e24429eSMichael Clark     .valid = {
2621e24429eSMichael Clark         .min_access_size = 4,
2631e24429eSMichael Clark         .max_access_size = 4
2641e24429eSMichael Clark     }
2651e24429eSMichael Clark };
2661e24429eSMichael Clark 
26783b92b8eSAlistair Francis static void sifive_plic_reset(DeviceState *dev)
26883b92b8eSAlistair Francis {
26983b92b8eSAlistair Francis     SiFivePLICState *s = SIFIVE_PLIC(dev);
27083b92b8eSAlistair Francis     int i;
27183b92b8eSAlistair Francis 
27283b92b8eSAlistair Francis     memset(s->source_priority, 0, sizeof(uint32_t) * s->num_sources);
27383b92b8eSAlistair Francis     memset(s->target_priority, 0, sizeof(uint32_t) * s->num_addrs);
27483b92b8eSAlistair Francis     memset(s->pending, 0, sizeof(uint32_t) * s->bitfield_words);
27583b92b8eSAlistair Francis     memset(s->claimed, 0, sizeof(uint32_t) * s->bitfield_words);
27683b92b8eSAlistair Francis     memset(s->enable, 0, sizeof(uint32_t) * s->num_enables);
27783b92b8eSAlistair Francis 
27883b92b8eSAlistair Francis     for (i = 0; i < s->num_harts; i++) {
27983b92b8eSAlistair Francis         qemu_set_irq(s->m_external_irqs[i], 0);
28083b92b8eSAlistair Francis         qemu_set_irq(s->s_external_irqs[i], 0);
28183b92b8eSAlistair Francis     }
28283b92b8eSAlistair Francis }
28383b92b8eSAlistair Francis 
2841e24429eSMichael Clark /*
2851e24429eSMichael Clark  * parse PLIC hart/mode address offset config
2861e24429eSMichael Clark  *
2871e24429eSMichael Clark  * "M"              1 hart with M mode
2881e24429eSMichael Clark  * "MS,MS"          2 harts, 0-1 with M and S mode
2891e24429eSMichael Clark  * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
2901e24429eSMichael Clark  */
2911e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic)
2921e24429eSMichael Clark {
293*7b0f26e4SBin Meng     int addrid, hartid, modes, m;
2941e24429eSMichael Clark     const char *p;
2951e24429eSMichael Clark     char c;
2961e24429eSMichael Clark 
2971e24429eSMichael Clark     /* count and validate hart/mode combinations */
2981e24429eSMichael Clark     addrid = 0, hartid = 0, modes = 0;
2991e24429eSMichael Clark     p = plic->hart_config;
3001e24429eSMichael Clark     while ((c = *p++)) {
3011e24429eSMichael Clark         if (c == ',') {
302*7b0f26e4SBin Meng             if (modes) {
303244df421SMichael Clark                 addrid += ctpop8(modes);
3041e24429eSMichael Clark                 hartid++;
305*7b0f26e4SBin Meng                 modes = 0;
306*7b0f26e4SBin Meng             }
3071e24429eSMichael Clark         } else {
308*7b0f26e4SBin Meng             m = 1 << char_to_mode(c);
3091e24429eSMichael Clark             if (modes == (modes | m)) {
3101e24429eSMichael Clark                 error_report("plic: duplicate mode '%c' in config: %s",
3111e24429eSMichael Clark                              c, plic->hart_config);
3121e24429eSMichael Clark                 exit(1);
3131e24429eSMichael Clark             }
3141e24429eSMichael Clark             modes |= m;
3151e24429eSMichael Clark         }
3161e24429eSMichael Clark     }
3171e24429eSMichael Clark     if (modes) {
318244df421SMichael Clark         addrid += ctpop8(modes);
3191e24429eSMichael Clark         hartid++;
320*7b0f26e4SBin Meng         modes = 0;
321*7b0f26e4SBin Meng     }
3221e24429eSMichael Clark 
3231e24429eSMichael Clark     plic->num_addrs = addrid;
324c9270e10SAnup Patel     plic->num_harts = hartid;
325c9270e10SAnup Patel 
326c9270e10SAnup Patel     /* store hart/mode combinations */
3271e24429eSMichael Clark     plic->addr_config = g_new(PLICAddr, plic->num_addrs);
328c9270e10SAnup Patel     addrid = 0, hartid = plic->hartid_base;
3291e24429eSMichael Clark     p = plic->hart_config;
3301e24429eSMichael Clark     while ((c = *p++)) {
3311e24429eSMichael Clark         if (c == ',') {
332*7b0f26e4SBin Meng             if (modes) {
3331e24429eSMichael Clark                 hartid++;
334*7b0f26e4SBin Meng                 modes = 0;
335*7b0f26e4SBin Meng             }
3361e24429eSMichael Clark         } else {
337*7b0f26e4SBin Meng             m = char_to_mode(c);
3381e24429eSMichael Clark             plic->addr_config[addrid].addrid = addrid;
3391e24429eSMichael Clark             plic->addr_config[addrid].hartid = hartid;
340*7b0f26e4SBin Meng             plic->addr_config[addrid].mode = m;
341*7b0f26e4SBin Meng             modes |= (1 << m);
3421e24429eSMichael Clark             addrid++;
3431e24429eSMichael Clark         }
3441e24429eSMichael Clark     }
3451e24429eSMichael Clark }
3461e24429eSMichael Clark 
3471e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level)
3481e24429eSMichael Clark {
3498d3dae16SAlistair Francis     SiFivePLICState *s = opaque;
3508d3dae16SAlistair Francis 
3518d3dae16SAlistair Francis     sifive_plic_set_pending(s, irq, level > 0);
3528d3dae16SAlistair Francis     sifive_plic_update(s);
3531e24429eSMichael Clark }
3541e24429eSMichael Clark 
3551e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp)
3561e24429eSMichael Clark {
357d680ff66SAlistair Francis     SiFivePLICState *s = SIFIVE_PLIC(dev);
358e3e7039cSMichael Clark     int i;
3591e24429eSMichael Clark 
360d680ff66SAlistair Francis     memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_plic_ops, s,
361d680ff66SAlistair Francis                           TYPE_SIFIVE_PLIC, s->aperture_size);
362d680ff66SAlistair Francis     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
363e3e7039cSMichael Clark 
364d680ff66SAlistair Francis     parse_hart_config(s);
365f436ecc3SAlistair Francis 
366d680ff66SAlistair Francis     s->bitfield_words = (s->num_sources + 31) >> 5;
367d680ff66SAlistair Francis     s->num_enables = s->bitfield_words * s->num_addrs;
368d680ff66SAlistair Francis     s->source_priority = g_new0(uint32_t, s->num_sources);
369d680ff66SAlistair Francis     s->target_priority = g_new(uint32_t, s->num_addrs);
370d680ff66SAlistair Francis     s->pending = g_new0(uint32_t, s->bitfield_words);
371d680ff66SAlistair Francis     s->claimed = g_new0(uint32_t, s->bitfield_words);
372d680ff66SAlistair Francis     s->enable = g_new0(uint32_t, s->num_enables);
373d680ff66SAlistair Francis 
374d680ff66SAlistair Francis     qdev_init_gpio_in(dev, sifive_plic_irq_request, s->num_sources);
375d680ff66SAlistair Francis 
376d680ff66SAlistair Francis     s->s_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
377d680ff66SAlistair Francis     qdev_init_gpio_out(dev, s->s_external_irqs, s->num_harts);
378d680ff66SAlistair Francis 
379d680ff66SAlistair Francis     s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
380d680ff66SAlistair Francis     qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts);
381f436ecc3SAlistair Francis 
382e3e7039cSMichael Clark     /* We can't allow the supervisor to control SEIP as this would allow the
383e3e7039cSMichael Clark      * supervisor to clear a pending external interrupt which will result in
384e3e7039cSMichael Clark      * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
385e3e7039cSMichael Clark      * hardware controlled when a PLIC is attached.
386e3e7039cSMichael Clark      */
387d680ff66SAlistair Francis     for (i = 0; i < s->num_harts; i++) {
388d680ff66SAlistair Francis         RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i));
389e3e7039cSMichael Clark         if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
390e3e7039cSMichael Clark             error_report("SEIP already claimed");
391e3e7039cSMichael Clark             exit(1);
392e3e7039cSMichael Clark         }
393e3e7039cSMichael Clark     }
39484bdc58cSPeter Maydell 
3954f5604c4SAlistair Francis     msi_nonbroken = true;
3961e24429eSMichael Clark }
3971e24429eSMichael Clark 
398dbd3ec54SYifei Jiang static const VMStateDescription vmstate_sifive_plic = {
399dbd3ec54SYifei Jiang     .name = "riscv_sifive_plic",
400dbd3ec54SYifei Jiang     .version_id = 1,
401dbd3ec54SYifei Jiang     .minimum_version_id = 1,
402dbd3ec54SYifei Jiang     .fields = (VMStateField[]) {
403dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState,
404dbd3ec54SYifei Jiang                                   num_sources, 0,
405dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
406dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState,
407dbd3ec54SYifei Jiang                                   num_addrs, 0,
408dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
409dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0,
410dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
411dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0,
412dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
413dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0,
414dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
415dbd3ec54SYifei Jiang             VMSTATE_END_OF_LIST()
416dbd3ec54SYifei Jiang         }
417dbd3ec54SYifei Jiang };
418dbd3ec54SYifei Jiang 
419d8c6590fSAlistair Francis static Property sifive_plic_properties[] = {
420d8c6590fSAlistair Francis     DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
421d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0),
422d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
423d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
424d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
425d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
426d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
427d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
428d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
429d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
430d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
431d8c6590fSAlistair Francis     DEFINE_PROP_END_OF_LIST(),
432d8c6590fSAlistair Francis };
433d8c6590fSAlistair Francis 
4341e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data)
4351e24429eSMichael Clark {
4361e24429eSMichael Clark     DeviceClass *dc = DEVICE_CLASS(klass);
4371e24429eSMichael Clark 
43883b92b8eSAlistair Francis     dc->reset = sifive_plic_reset;
4394f67d30bSMarc-André Lureau     device_class_set_props(dc, sifive_plic_properties);
4401e24429eSMichael Clark     dc->realize = sifive_plic_realize;
441dbd3ec54SYifei Jiang     dc->vmsd = &vmstate_sifive_plic;
4421e24429eSMichael Clark }
4431e24429eSMichael Clark 
4441e24429eSMichael Clark static const TypeInfo sifive_plic_info = {
4451e24429eSMichael Clark     .name          = TYPE_SIFIVE_PLIC,
4461e24429eSMichael Clark     .parent        = TYPE_SYS_BUS_DEVICE,
4471e24429eSMichael Clark     .instance_size = sizeof(SiFivePLICState),
4481e24429eSMichael Clark     .class_init    = sifive_plic_class_init,
4491e24429eSMichael Clark };
4501e24429eSMichael Clark 
4511e24429eSMichael Clark static void sifive_plic_register_types(void)
4521e24429eSMichael Clark {
4531e24429eSMichael Clark     type_register_static(&sifive_plic_info);
4541e24429eSMichael Clark }
4551e24429eSMichael Clark 
4561e24429eSMichael Clark type_init(sifive_plic_register_types)
4571e24429eSMichael Clark 
4581e24429eSMichael Clark /*
4591e24429eSMichael Clark  * Create PLIC device.
4601e24429eSMichael Clark  */
4611e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
462f436ecc3SAlistair Francis     uint32_t num_harts,
463c9270e10SAnup Patel     uint32_t hartid_base, uint32_t num_sources,
464c9270e10SAnup Patel     uint32_t num_priorities, uint32_t priority_base,
465c9270e10SAnup Patel     uint32_t pending_base, uint32_t enable_base,
466c9270e10SAnup Patel     uint32_t enable_stride, uint32_t context_base,
467c9270e10SAnup Patel     uint32_t context_stride, uint32_t aperture_size)
4681e24429eSMichael Clark {
4693e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
47040244040SAlistair Francis     int i;
471ad40be27SYifei Jiang     SiFivePLICState *plic;
472f436ecc3SAlistair Francis 
4731e24429eSMichael Clark     assert(enable_stride == (enable_stride & -enable_stride));
4741e24429eSMichael Clark     assert(context_stride == (context_stride & -context_stride));
4751e24429eSMichael Clark     qdev_prop_set_string(dev, "hart-config", hart_config);
476c9270e10SAnup Patel     qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
4771e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-sources", num_sources);
4781e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
4791e24429eSMichael Clark     qdev_prop_set_uint32(dev, "priority-base", priority_base);
4801e24429eSMichael Clark     qdev_prop_set_uint32(dev, "pending-base", pending_base);
4811e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-base", enable_base);
4821e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
4831e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-base", context_base);
4841e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-stride", context_stride);
4851e24429eSMichael Clark     qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
4863c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
4871e24429eSMichael Clark     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
488f436ecc3SAlistair Francis 
489ad40be27SYifei Jiang     plic = SIFIVE_PLIC(dev);
490f436ecc3SAlistair Francis 
49140244040SAlistair Francis     for (i = 0; i < plic->num_addrs; i++) {
49240244040SAlistair Francis         int cpu_num = plic->addr_config[i].hartid;
49354f21836SAtish Patra         CPUState *cpu = qemu_get_cpu(cpu_num);
49440244040SAlistair Francis 
49540244040SAlistair Francis         if (plic->addr_config[i].mode == PLICMode_M) {
4960a9a6cbaSFrédéric Pétrot             qdev_connect_gpio_out(dev, cpu_num - hartid_base + num_harts,
497f436ecc3SAlistair Francis                                   qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
498f436ecc3SAlistair Francis         }
49940244040SAlistair Francis         if (plic->addr_config[i].mode == PLICMode_S) {
5000a9a6cbaSFrédéric Pétrot             qdev_connect_gpio_out(dev, cpu_num - hartid_base,
501ad40be27SYifei Jiang                                   qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
502ad40be27SYifei Jiang         }
503ad40be27SYifei Jiang     }
504ad40be27SYifei Jiang 
5051e24429eSMichael Clark     return dev;
5061e24429eSMichael Clark }
507