xref: /qemu/hw/misc/tz-mpc.c (revision 3c8133f973767460f8e42c9e656f2f3ed703d00d)
1344f4b15SPeter Maydell /*
2344f4b15SPeter Maydell  * ARM AHB5 TrustZone Memory Protection Controller emulation
3344f4b15SPeter Maydell  *
4344f4b15SPeter Maydell  * Copyright (c) 2018 Linaro Limited
5344f4b15SPeter Maydell  * Written by Peter Maydell
6344f4b15SPeter Maydell  *
7344f4b15SPeter Maydell  * This program is free software; you can redistribute it and/or modify
8344f4b15SPeter Maydell  * it under the terms of the GNU General Public License version 2 or
9344f4b15SPeter Maydell  * (at your option) any later version.
10344f4b15SPeter Maydell  */
11344f4b15SPeter Maydell 
12344f4b15SPeter Maydell #include "qemu/osdep.h"
13344f4b15SPeter Maydell #include "qemu/log.h"
14344f4b15SPeter Maydell #include "qapi/error.h"
15344f4b15SPeter Maydell #include "trace.h"
16344f4b15SPeter Maydell #include "hw/sysbus.h"
17344f4b15SPeter Maydell #include "hw/registerfields.h"
18344f4b15SPeter Maydell #include "hw/misc/tz-mpc.h"
19344f4b15SPeter Maydell 
20344f4b15SPeter Maydell /* Our IOMMU has two IOMMU indexes, one for secure transactions and one for
21344f4b15SPeter Maydell  * non-secure transactions.
22344f4b15SPeter Maydell  */
23344f4b15SPeter Maydell enum {
24344f4b15SPeter Maydell     IOMMU_IDX_S,
25344f4b15SPeter Maydell     IOMMU_IDX_NS,
26344f4b15SPeter Maydell     IOMMU_NUM_INDEXES,
27344f4b15SPeter Maydell };
28344f4b15SPeter Maydell 
29344f4b15SPeter Maydell /* Config registers */
30344f4b15SPeter Maydell REG32(CTRL, 0x00)
31cdb60998SPeter Maydell     FIELD(CTRL, SEC_RESP, 4, 1)
32cdb60998SPeter Maydell     FIELD(CTRL, AUTOINC, 8, 1)
33cdb60998SPeter Maydell     FIELD(CTRL, LOCKDOWN, 31, 1)
34344f4b15SPeter Maydell REG32(BLK_MAX, 0x10)
35344f4b15SPeter Maydell REG32(BLK_CFG, 0x14)
36344f4b15SPeter Maydell REG32(BLK_IDX, 0x18)
37344f4b15SPeter Maydell REG32(BLK_LUT, 0x1c)
38344f4b15SPeter Maydell REG32(INT_STAT, 0x20)
39cdb60998SPeter Maydell     FIELD(INT_STAT, IRQ, 0, 1)
40344f4b15SPeter Maydell REG32(INT_CLEAR, 0x24)
41cdb60998SPeter Maydell     FIELD(INT_CLEAR, IRQ, 0, 1)
42344f4b15SPeter Maydell REG32(INT_EN, 0x28)
43cdb60998SPeter Maydell     FIELD(INT_EN, IRQ, 0, 1)
44344f4b15SPeter Maydell REG32(INT_INFO1, 0x2c)
45344f4b15SPeter Maydell REG32(INT_INFO2, 0x30)
4657c49a6eSPeter Maydell     FIELD(INT_INFO2, HMASTER, 0, 16)
4757c49a6eSPeter Maydell     FIELD(INT_INFO2, HNONSEC, 16, 1)
4857c49a6eSPeter Maydell     FIELD(INT_INFO2, CFG_NS, 17, 1)
49344f4b15SPeter Maydell REG32(INT_SET, 0x34)
50cdb60998SPeter Maydell     FIELD(INT_SET, IRQ, 0, 1)
51344f4b15SPeter Maydell REG32(PIDR4, 0xfd0)
52344f4b15SPeter Maydell REG32(PIDR5, 0xfd4)
53344f4b15SPeter Maydell REG32(PIDR6, 0xfd8)
54344f4b15SPeter Maydell REG32(PIDR7, 0xfdc)
55344f4b15SPeter Maydell REG32(PIDR0, 0xfe0)
56344f4b15SPeter Maydell REG32(PIDR1, 0xfe4)
57344f4b15SPeter Maydell REG32(PIDR2, 0xfe8)
58344f4b15SPeter Maydell REG32(PIDR3, 0xfec)
59344f4b15SPeter Maydell REG32(CIDR0, 0xff0)
60344f4b15SPeter Maydell REG32(CIDR1, 0xff4)
61344f4b15SPeter Maydell REG32(CIDR2, 0xff8)
62344f4b15SPeter Maydell REG32(CIDR3, 0xffc)
63344f4b15SPeter Maydell 
64344f4b15SPeter Maydell static const uint8_t tz_mpc_idregs[] = {
65344f4b15SPeter Maydell     0x04, 0x00, 0x00, 0x00,
66344f4b15SPeter Maydell     0x60, 0xb8, 0x1b, 0x00,
67344f4b15SPeter Maydell     0x0d, 0xf0, 0x05, 0xb1,
68344f4b15SPeter Maydell };
69344f4b15SPeter Maydell 
70cdb60998SPeter Maydell static void tz_mpc_irq_update(TZMPC *s)
71cdb60998SPeter Maydell {
72cdb60998SPeter Maydell     qemu_set_irq(s->irq, s->int_stat && s->int_en);
73cdb60998SPeter Maydell }
74cdb60998SPeter Maydell 
75dd29d068SPeter Maydell static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx,
76dd29d068SPeter Maydell                                 uint32_t oldlut, uint32_t newlut)
77dd29d068SPeter Maydell {
78dd29d068SPeter Maydell     /* Called when the LUT word at lutidx has changed from oldlut to newlut;
79dd29d068SPeter Maydell      * must call the IOMMU notifiers for the changed blocks.
80dd29d068SPeter Maydell      */
81dd29d068SPeter Maydell     IOMMUTLBEntry entry = {
82dd29d068SPeter Maydell         .addr_mask = s->blocksize - 1,
83dd29d068SPeter Maydell     };
84dd29d068SPeter Maydell     hwaddr addr = lutidx * s->blocksize * 32;
85dd29d068SPeter Maydell     int i;
86dd29d068SPeter Maydell 
87dd29d068SPeter Maydell     for (i = 0; i < 32; i++, addr += s->blocksize) {
88dd29d068SPeter Maydell         bool block_is_ns;
89dd29d068SPeter Maydell 
90dd29d068SPeter Maydell         if (!((oldlut ^ newlut) & (1 << i))) {
91dd29d068SPeter Maydell             continue;
92dd29d068SPeter Maydell         }
93dd29d068SPeter Maydell         /* This changes the mappings for both the S and the NS space,
94dd29d068SPeter Maydell          * so we need to do four notifies: an UNMAP then a MAP for each.
95dd29d068SPeter Maydell          */
96dd29d068SPeter Maydell         block_is_ns = newlut & (1 << i);
97dd29d068SPeter Maydell 
98dd29d068SPeter Maydell         trace_tz_mpc_iommu_notify(addr);
99dd29d068SPeter Maydell         entry.iova = addr;
100dd29d068SPeter Maydell         entry.translated_addr = addr;
101dd29d068SPeter Maydell 
102dd29d068SPeter Maydell         entry.perm = IOMMU_NONE;
103dd29d068SPeter Maydell         memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
104dd29d068SPeter Maydell         memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
105dd29d068SPeter Maydell 
106dd29d068SPeter Maydell         entry.perm = IOMMU_RW;
107dd29d068SPeter Maydell         if (block_is_ns) {
108dd29d068SPeter Maydell             entry.target_as = &s->blocked_io_as;
109dd29d068SPeter Maydell         } else {
110dd29d068SPeter Maydell             entry.target_as = &s->downstream_as;
111dd29d068SPeter Maydell         }
112dd29d068SPeter Maydell         memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
113dd29d068SPeter Maydell         if (block_is_ns) {
114dd29d068SPeter Maydell             entry.target_as = &s->downstream_as;
115dd29d068SPeter Maydell         } else {
116dd29d068SPeter Maydell             entry.target_as = &s->blocked_io_as;
117dd29d068SPeter Maydell         }
118dd29d068SPeter Maydell         memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
119dd29d068SPeter Maydell     }
120dd29d068SPeter Maydell }
121dd29d068SPeter Maydell 
122cdb60998SPeter Maydell static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size)
123cdb60998SPeter Maydell {
124cdb60998SPeter Maydell     /* Auto-increment BLK_IDX if necessary */
125cdb60998SPeter Maydell     if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) {
126cdb60998SPeter Maydell         s->blk_idx++;
127cdb60998SPeter Maydell         s->blk_idx %= s->blk_max;
128cdb60998SPeter Maydell     }
129cdb60998SPeter Maydell }
130cdb60998SPeter Maydell 
131344f4b15SPeter Maydell static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr,
132344f4b15SPeter Maydell                                    uint64_t *pdata,
133344f4b15SPeter Maydell                                    unsigned size, MemTxAttrs attrs)
134344f4b15SPeter Maydell {
135cdb60998SPeter Maydell     TZMPC *s = TZ_MPC(opaque);
136344f4b15SPeter Maydell     uint64_t r;
137344f4b15SPeter Maydell     uint32_t offset = addr & ~0x3;
138344f4b15SPeter Maydell 
139344f4b15SPeter Maydell     if (!attrs.secure && offset < A_PIDR4) {
140344f4b15SPeter Maydell         /* NS accesses can only see the ID registers */
141344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
142344f4b15SPeter Maydell                       "TZ MPC register read: NS access to offset 0x%x\n",
143344f4b15SPeter Maydell                       offset);
144344f4b15SPeter Maydell         r = 0;
145344f4b15SPeter Maydell         goto read_out;
146344f4b15SPeter Maydell     }
147344f4b15SPeter Maydell 
148344f4b15SPeter Maydell     switch (offset) {
149cdb60998SPeter Maydell     case A_CTRL:
150cdb60998SPeter Maydell         r = s->ctrl;
151cdb60998SPeter Maydell         break;
152cdb60998SPeter Maydell     case A_BLK_MAX:
153cdb60998SPeter Maydell         r = s->blk_max;
154cdb60998SPeter Maydell         break;
155cdb60998SPeter Maydell     case A_BLK_CFG:
156cdb60998SPeter Maydell         /* We are never in "init in progress state", so this just indicates
157cdb60998SPeter Maydell          * the block size. s->blocksize == (1 << BLK_CFG + 5), so
158cdb60998SPeter Maydell          * BLK_CFG == ctz32(s->blocksize) - 5
159cdb60998SPeter Maydell          */
160cdb60998SPeter Maydell         r = ctz32(s->blocksize) - 5;
161cdb60998SPeter Maydell         break;
162cdb60998SPeter Maydell     case A_BLK_IDX:
163cdb60998SPeter Maydell         r = s->blk_idx;
164cdb60998SPeter Maydell         break;
165cdb60998SPeter Maydell     case A_BLK_LUT:
166cdb60998SPeter Maydell         r = s->blk_lut[s->blk_idx];
167cdb60998SPeter Maydell         tz_mpc_autoinc_idx(s, size);
168cdb60998SPeter Maydell         break;
169cdb60998SPeter Maydell     case A_INT_STAT:
170cdb60998SPeter Maydell         r = s->int_stat;
171cdb60998SPeter Maydell         break;
172cdb60998SPeter Maydell     case A_INT_EN:
173cdb60998SPeter Maydell         r = s->int_en;
174cdb60998SPeter Maydell         break;
175cdb60998SPeter Maydell     case A_INT_INFO1:
176cdb60998SPeter Maydell         r = s->int_info1;
177cdb60998SPeter Maydell         break;
178cdb60998SPeter Maydell     case A_INT_INFO2:
179cdb60998SPeter Maydell         r = s->int_info2;
180cdb60998SPeter Maydell         break;
181344f4b15SPeter Maydell     case A_PIDR4:
182344f4b15SPeter Maydell     case A_PIDR5:
183344f4b15SPeter Maydell     case A_PIDR6:
184344f4b15SPeter Maydell     case A_PIDR7:
185344f4b15SPeter Maydell     case A_PIDR0:
186344f4b15SPeter Maydell     case A_PIDR1:
187344f4b15SPeter Maydell     case A_PIDR2:
188344f4b15SPeter Maydell     case A_PIDR3:
189344f4b15SPeter Maydell     case A_CIDR0:
190344f4b15SPeter Maydell     case A_CIDR1:
191344f4b15SPeter Maydell     case A_CIDR2:
192344f4b15SPeter Maydell     case A_CIDR3:
193344f4b15SPeter Maydell         r = tz_mpc_idregs[(offset - A_PIDR4) / 4];
194344f4b15SPeter Maydell         break;
195344f4b15SPeter Maydell     case A_INT_CLEAR:
196344f4b15SPeter Maydell     case A_INT_SET:
197344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
198344f4b15SPeter Maydell                       "TZ MPC register read: write-only offset 0x%x\n",
199344f4b15SPeter Maydell                       offset);
200344f4b15SPeter Maydell         r = 0;
201344f4b15SPeter Maydell         break;
202344f4b15SPeter Maydell     default:
203344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
204344f4b15SPeter Maydell                       "TZ MPC register read: bad offset 0x%x\n", offset);
205344f4b15SPeter Maydell         r = 0;
206344f4b15SPeter Maydell         break;
207344f4b15SPeter Maydell     }
208344f4b15SPeter Maydell 
209344f4b15SPeter Maydell     if (size != 4) {
210344f4b15SPeter Maydell         /* None of our registers are read-sensitive (except BLK_LUT,
211344f4b15SPeter Maydell          * which can special case the "size not 4" case), so just
212344f4b15SPeter Maydell          * pull the right bytes out of the word read result.
213344f4b15SPeter Maydell          */
214344f4b15SPeter Maydell         r = extract32(r, (addr & 3) * 8, size * 8);
215344f4b15SPeter Maydell     }
216344f4b15SPeter Maydell 
217344f4b15SPeter Maydell read_out:
218344f4b15SPeter Maydell     trace_tz_mpc_reg_read(addr, r, size);
219344f4b15SPeter Maydell     *pdata = r;
220344f4b15SPeter Maydell     return MEMTX_OK;
221344f4b15SPeter Maydell }
222344f4b15SPeter Maydell 
223344f4b15SPeter Maydell static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr,
224344f4b15SPeter Maydell                                     uint64_t value,
225344f4b15SPeter Maydell                                     unsigned size, MemTxAttrs attrs)
226344f4b15SPeter Maydell {
227cdb60998SPeter Maydell     TZMPC *s = TZ_MPC(opaque);
228344f4b15SPeter Maydell     uint32_t offset = addr & ~0x3;
229344f4b15SPeter Maydell 
230344f4b15SPeter Maydell     trace_tz_mpc_reg_write(addr, value, size);
231344f4b15SPeter Maydell 
232344f4b15SPeter Maydell     if (!attrs.secure && offset < A_PIDR4) {
233344f4b15SPeter Maydell         /* NS accesses can only see the ID registers */
234344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
235344f4b15SPeter Maydell                       "TZ MPC register write: NS access to offset 0x%x\n",
236344f4b15SPeter Maydell                       offset);
237344f4b15SPeter Maydell         return MEMTX_OK;
238344f4b15SPeter Maydell     }
239344f4b15SPeter Maydell 
240344f4b15SPeter Maydell     if (size != 4) {
241344f4b15SPeter Maydell         /* Expand the byte or halfword write to a full word size.
242344f4b15SPeter Maydell          * In most cases we can do this with zeroes; the exceptions
243344f4b15SPeter Maydell          * are CTRL, BLK_IDX and BLK_LUT.
244344f4b15SPeter Maydell          */
245344f4b15SPeter Maydell         uint32_t oldval;
246344f4b15SPeter Maydell 
247344f4b15SPeter Maydell         switch (offset) {
248cdb60998SPeter Maydell         case A_CTRL:
249cdb60998SPeter Maydell             oldval = s->ctrl;
250cdb60998SPeter Maydell             break;
251cdb60998SPeter Maydell         case A_BLK_IDX:
252cdb60998SPeter Maydell             oldval = s->blk_idx;
253cdb60998SPeter Maydell             break;
254cdb60998SPeter Maydell         case A_BLK_LUT:
255cdb60998SPeter Maydell             oldval = s->blk_lut[s->blk_idx];
256cdb60998SPeter Maydell             break;
257344f4b15SPeter Maydell         default:
258344f4b15SPeter Maydell             oldval = 0;
259344f4b15SPeter Maydell             break;
260344f4b15SPeter Maydell         }
261344f4b15SPeter Maydell         value = deposit32(oldval, (addr & 3) * 8, size * 8, value);
262344f4b15SPeter Maydell     }
263344f4b15SPeter Maydell 
264cdb60998SPeter Maydell     if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) &&
265cdb60998SPeter Maydell         (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) {
266cdb60998SPeter Maydell         /* Lockdown mode makes these three registers read-only, and
267cdb60998SPeter Maydell          * the only way out of it is to reset the device.
268cdb60998SPeter Maydell          */
269cdb60998SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x "
270cdb60998SPeter Maydell                       "while MPC is in lockdown mode\n", offset);
271cdb60998SPeter Maydell         return MEMTX_OK;
272cdb60998SPeter Maydell     }
273cdb60998SPeter Maydell 
274344f4b15SPeter Maydell     switch (offset) {
275cdb60998SPeter Maydell     case A_CTRL:
276cdb60998SPeter Maydell         /* We don't implement the 'data gating' feature so all other bits
277cdb60998SPeter Maydell          * are reserved and we make them RAZ/WI.
278cdb60998SPeter Maydell          */
279cdb60998SPeter Maydell         s->ctrl = value & (R_CTRL_SEC_RESP_MASK |
280cdb60998SPeter Maydell                            R_CTRL_AUTOINC_MASK |
281cdb60998SPeter Maydell                            R_CTRL_LOCKDOWN_MASK);
282cdb60998SPeter Maydell         break;
283cdb60998SPeter Maydell     case A_BLK_IDX:
284cdb60998SPeter Maydell         s->blk_idx = value % s->blk_max;
285cdb60998SPeter Maydell         break;
286cdb60998SPeter Maydell     case A_BLK_LUT:
287dd29d068SPeter Maydell         tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value);
288cdb60998SPeter Maydell         s->blk_lut[s->blk_idx] = value;
289cdb60998SPeter Maydell         tz_mpc_autoinc_idx(s, size);
290cdb60998SPeter Maydell         break;
291cdb60998SPeter Maydell     case A_INT_CLEAR:
292cdb60998SPeter Maydell         if (value & R_INT_CLEAR_IRQ_MASK) {
293cdb60998SPeter Maydell             s->int_stat = 0;
294cdb60998SPeter Maydell             tz_mpc_irq_update(s);
295cdb60998SPeter Maydell         }
296cdb60998SPeter Maydell         break;
297cdb60998SPeter Maydell     case A_INT_EN:
298cdb60998SPeter Maydell         s->int_en = value & R_INT_EN_IRQ_MASK;
299cdb60998SPeter Maydell         tz_mpc_irq_update(s);
300cdb60998SPeter Maydell         break;
301cdb60998SPeter Maydell     case A_INT_SET:
302cdb60998SPeter Maydell         if (value & R_INT_SET_IRQ_MASK) {
303cdb60998SPeter Maydell             s->int_stat = R_INT_STAT_IRQ_MASK;
304cdb60998SPeter Maydell             tz_mpc_irq_update(s);
305cdb60998SPeter Maydell         }
306cdb60998SPeter Maydell         break;
307344f4b15SPeter Maydell     case A_PIDR4:
308344f4b15SPeter Maydell     case A_PIDR5:
309344f4b15SPeter Maydell     case A_PIDR6:
310344f4b15SPeter Maydell     case A_PIDR7:
311344f4b15SPeter Maydell     case A_PIDR0:
312344f4b15SPeter Maydell     case A_PIDR1:
313344f4b15SPeter Maydell     case A_PIDR2:
314344f4b15SPeter Maydell     case A_PIDR3:
315344f4b15SPeter Maydell     case A_CIDR0:
316344f4b15SPeter Maydell     case A_CIDR1:
317344f4b15SPeter Maydell     case A_CIDR2:
318344f4b15SPeter Maydell     case A_CIDR3:
319344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
320344f4b15SPeter Maydell                       "TZ MPC register write: read-only offset 0x%x\n", offset);
321344f4b15SPeter Maydell         break;
322344f4b15SPeter Maydell     default:
323344f4b15SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
324344f4b15SPeter Maydell                       "TZ MPC register write: bad offset 0x%x\n", offset);
325344f4b15SPeter Maydell         break;
326344f4b15SPeter Maydell     }
327344f4b15SPeter Maydell 
328344f4b15SPeter Maydell     return MEMTX_OK;
329344f4b15SPeter Maydell }
330344f4b15SPeter Maydell 
331344f4b15SPeter Maydell static const MemoryRegionOps tz_mpc_reg_ops = {
332344f4b15SPeter Maydell     .read_with_attrs = tz_mpc_reg_read,
333344f4b15SPeter Maydell     .write_with_attrs = tz_mpc_reg_write,
334344f4b15SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
335344f4b15SPeter Maydell     .valid.min_access_size = 1,
336344f4b15SPeter Maydell     .valid.max_access_size = 4,
337344f4b15SPeter Maydell     .impl.min_access_size = 1,
338344f4b15SPeter Maydell     .impl.max_access_size = 4,
339344f4b15SPeter Maydell };
340344f4b15SPeter Maydell 
34157c49a6eSPeter Maydell static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr)
34257c49a6eSPeter Maydell {
34357c49a6eSPeter Maydell     /* Return the cfg_ns bit from the LUT for the specified address */
34457c49a6eSPeter Maydell     hwaddr blknum = addr / s->blocksize;
34557c49a6eSPeter Maydell     hwaddr blkword = blknum / 32;
34657c49a6eSPeter Maydell     uint32_t blkbit = 1U << (blknum % 32);
34757c49a6eSPeter Maydell 
34857c49a6eSPeter Maydell     /* This would imply the address was larger than the size we
34957c49a6eSPeter Maydell      * defined this memory region to be, so it can't happen.
35057c49a6eSPeter Maydell      */
35157c49a6eSPeter Maydell     assert(blkword < s->blk_max);
35257c49a6eSPeter Maydell     return s->blk_lut[blkword] & blkbit;
35357c49a6eSPeter Maydell }
35457c49a6eSPeter Maydell 
35557c49a6eSPeter Maydell static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs)
35657c49a6eSPeter Maydell {
35757c49a6eSPeter Maydell     /* Handle a blocked transaction: raise IRQ, capture info, etc */
35857c49a6eSPeter Maydell     if (!s->int_stat) {
35957c49a6eSPeter Maydell         /* First blocked transfer: capture information into INT_INFO1 and
36057c49a6eSPeter Maydell          * INT_INFO2. Subsequent transfers are still blocked but don't
36157c49a6eSPeter Maydell          * capture information until the guest clears the interrupt.
36257c49a6eSPeter Maydell          */
36357c49a6eSPeter Maydell 
36457c49a6eSPeter Maydell         s->int_info1 = addr;
36557c49a6eSPeter Maydell         s->int_info2 = 0;
36657c49a6eSPeter Maydell         s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER,
36757c49a6eSPeter Maydell                                   attrs.requester_id & 0xffff);
36857c49a6eSPeter Maydell         s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC,
36957c49a6eSPeter Maydell                                   ~attrs.secure);
37057c49a6eSPeter Maydell         s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS,
37157c49a6eSPeter Maydell                                   tz_mpc_cfg_ns(s, addr));
37257c49a6eSPeter Maydell         s->int_stat |= R_INT_STAT_IRQ_MASK;
37357c49a6eSPeter Maydell         tz_mpc_irq_update(s);
37457c49a6eSPeter Maydell     }
37557c49a6eSPeter Maydell 
37657c49a6eSPeter Maydell     /* Generate bus error if desired; otherwise RAZ/WI */
37757c49a6eSPeter Maydell     return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK;
37857c49a6eSPeter Maydell }
37957c49a6eSPeter Maydell 
380344f4b15SPeter Maydell /* Accesses only reach these read and write functions if the MPC is
381344f4b15SPeter Maydell  * blocking them; non-blocked accesses go directly to the downstream
382344f4b15SPeter Maydell  * memory region without passing through this code.
383344f4b15SPeter Maydell  */
384344f4b15SPeter Maydell static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr,
385344f4b15SPeter Maydell                                            uint64_t *pdata,
386344f4b15SPeter Maydell                                            unsigned size, MemTxAttrs attrs)
387344f4b15SPeter Maydell {
38857c49a6eSPeter Maydell     TZMPC *s = TZ_MPC(opaque);
38957c49a6eSPeter Maydell 
390344f4b15SPeter Maydell     trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure);
391344f4b15SPeter Maydell 
392344f4b15SPeter Maydell     *pdata = 0;
39357c49a6eSPeter Maydell     return tz_mpc_handle_block(s, addr, attrs);
394344f4b15SPeter Maydell }
395344f4b15SPeter Maydell 
396344f4b15SPeter Maydell static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr,
397344f4b15SPeter Maydell                                             uint64_t value,
398344f4b15SPeter Maydell                                             unsigned size, MemTxAttrs attrs)
399344f4b15SPeter Maydell {
40057c49a6eSPeter Maydell     TZMPC *s = TZ_MPC(opaque);
40157c49a6eSPeter Maydell 
402344f4b15SPeter Maydell     trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure);
403344f4b15SPeter Maydell 
40457c49a6eSPeter Maydell     return tz_mpc_handle_block(s, addr, attrs);
405344f4b15SPeter Maydell }
406344f4b15SPeter Maydell 
407344f4b15SPeter Maydell static const MemoryRegionOps tz_mpc_mem_blocked_ops = {
408344f4b15SPeter Maydell     .read_with_attrs = tz_mpc_mem_blocked_read,
409344f4b15SPeter Maydell     .write_with_attrs = tz_mpc_mem_blocked_write,
410344f4b15SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
411344f4b15SPeter Maydell     .valid.min_access_size = 1,
412344f4b15SPeter Maydell     .valid.max_access_size = 8,
413344f4b15SPeter Maydell     .impl.min_access_size = 1,
414344f4b15SPeter Maydell     .impl.max_access_size = 8,
415344f4b15SPeter Maydell };
416344f4b15SPeter Maydell 
417344f4b15SPeter Maydell static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu,
418344f4b15SPeter Maydell                                       hwaddr addr, IOMMUAccessFlags flags,
419344f4b15SPeter Maydell                                       int iommu_idx)
420344f4b15SPeter Maydell {
421344f4b15SPeter Maydell     TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream));
422344f4b15SPeter Maydell     bool ok;
423344f4b15SPeter Maydell 
424344f4b15SPeter Maydell     IOMMUTLBEntry ret = {
425344f4b15SPeter Maydell         .iova = addr & ~(s->blocksize - 1),
426344f4b15SPeter Maydell         .translated_addr = addr & ~(s->blocksize - 1),
427344f4b15SPeter Maydell         .addr_mask = s->blocksize - 1,
428344f4b15SPeter Maydell         .perm = IOMMU_RW,
429344f4b15SPeter Maydell     };
430344f4b15SPeter Maydell 
431344f4b15SPeter Maydell     /* Look at the per-block configuration for this address, and
432344f4b15SPeter Maydell      * return a TLB entry directing the transaction at either
433344f4b15SPeter Maydell      * downstream_as or blocked_io_as, as appropriate.
434dd29d068SPeter Maydell      * If the LUT cfg_ns bit is 1, only non-secure transactions
435dd29d068SPeter Maydell      * may pass. If the bit is 0, only secure transactions may pass.
436344f4b15SPeter Maydell      */
437dd29d068SPeter Maydell     ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS);
438344f4b15SPeter Maydell 
439344f4b15SPeter Maydell     trace_tz_mpc_translate(addr, flags,
440344f4b15SPeter Maydell                            iommu_idx == IOMMU_IDX_S ? "S" : "NS",
441344f4b15SPeter Maydell                            ok ? "pass" : "block");
442344f4b15SPeter Maydell 
443344f4b15SPeter Maydell     ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as;
444344f4b15SPeter Maydell     return ret;
445344f4b15SPeter Maydell }
446344f4b15SPeter Maydell 
447344f4b15SPeter Maydell static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs)
448344f4b15SPeter Maydell {
449344f4b15SPeter Maydell     /* We treat unspecified attributes like secure. Transactions with
450344f4b15SPeter Maydell      * unspecified attributes come from places like
451*3c8133f9SPeter Maydell      * rom_reset() for initial image load, and we want
452344f4b15SPeter Maydell      * those to pass through the from-reset "everything is secure" config.
453344f4b15SPeter Maydell      * All the real during-emulation transactions from the CPU will
454344f4b15SPeter Maydell      * specify attributes.
455344f4b15SPeter Maydell      */
456344f4b15SPeter Maydell     return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS;
457344f4b15SPeter Maydell }
458344f4b15SPeter Maydell 
459344f4b15SPeter Maydell static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu)
460344f4b15SPeter Maydell {
461344f4b15SPeter Maydell     return IOMMU_NUM_INDEXES;
462344f4b15SPeter Maydell }
463344f4b15SPeter Maydell 
464344f4b15SPeter Maydell static void tz_mpc_reset(DeviceState *dev)
465344f4b15SPeter Maydell {
466cdb60998SPeter Maydell     TZMPC *s = TZ_MPC(dev);
467cdb60998SPeter Maydell 
468cdb60998SPeter Maydell     s->ctrl = 0x00000100;
469cdb60998SPeter Maydell     s->blk_idx = 0;
470cdb60998SPeter Maydell     s->int_stat = 0;
471cdb60998SPeter Maydell     s->int_en = 1;
472cdb60998SPeter Maydell     s->int_info1 = 0;
473cdb60998SPeter Maydell     s->int_info2 = 0;
474cdb60998SPeter Maydell 
475cdb60998SPeter Maydell     memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t));
476344f4b15SPeter Maydell }
477344f4b15SPeter Maydell 
478344f4b15SPeter Maydell static void tz_mpc_init(Object *obj)
479344f4b15SPeter Maydell {
480344f4b15SPeter Maydell     DeviceState *dev = DEVICE(obj);
481344f4b15SPeter Maydell     TZMPC *s = TZ_MPC(obj);
482344f4b15SPeter Maydell 
483344f4b15SPeter Maydell     qdev_init_gpio_out_named(dev, &s->irq, "irq", 1);
484344f4b15SPeter Maydell }
485344f4b15SPeter Maydell 
486344f4b15SPeter Maydell static void tz_mpc_realize(DeviceState *dev, Error **errp)
487344f4b15SPeter Maydell {
488344f4b15SPeter Maydell     Object *obj = OBJECT(dev);
489344f4b15SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
490344f4b15SPeter Maydell     TZMPC *s = TZ_MPC(dev);
491344f4b15SPeter Maydell     uint64_t size;
492344f4b15SPeter Maydell 
493344f4b15SPeter Maydell     /* We can't create the upstream end of the port until realize,
494344f4b15SPeter Maydell      * as we don't know the size of the MR used as the downstream until then.
495344f4b15SPeter Maydell      * We insist on having a downstream, to avoid complicating the code
496344f4b15SPeter Maydell      * with handling the "don't know how big this is" case. It's easy
497344f4b15SPeter Maydell      * enough for the user to create an unimplemented_device as downstream
498344f4b15SPeter Maydell      * if they have nothing else to plug into this.
499344f4b15SPeter Maydell      */
500344f4b15SPeter Maydell     if (!s->downstream) {
501344f4b15SPeter Maydell         error_setg(errp, "MPC 'downstream' link not set");
502344f4b15SPeter Maydell         return;
503344f4b15SPeter Maydell     }
504344f4b15SPeter Maydell 
505344f4b15SPeter Maydell     size = memory_region_size(s->downstream);
506344f4b15SPeter Maydell 
507344f4b15SPeter Maydell     memory_region_init_iommu(&s->upstream, sizeof(s->upstream),
508344f4b15SPeter Maydell                              TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
509344f4b15SPeter Maydell                              obj, "tz-mpc-upstream", size);
510344f4b15SPeter Maydell 
511344f4b15SPeter Maydell     /* In real hardware the block size is configurable. In QEMU we could
512344f4b15SPeter Maydell      * make it configurable but will need it to be at least as big as the
513344f4b15SPeter Maydell      * target page size so we can execute out of the resulting MRs. Guest
514344f4b15SPeter Maydell      * software is supposed to check the block size using the BLK_CFG
515344f4b15SPeter Maydell      * register, so make it fixed at the page size.
516344f4b15SPeter Maydell      */
517344f4b15SPeter Maydell     s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream);
518344f4b15SPeter Maydell     if (size % s->blocksize != 0) {
519344f4b15SPeter Maydell         error_setg(errp,
520344f4b15SPeter Maydell                    "MPC 'downstream' size %" PRId64
521344f4b15SPeter Maydell                    " is not a multiple of %" HWADDR_PRIx " bytes",
522344f4b15SPeter Maydell                    size, s->blocksize);
523344f4b15SPeter Maydell         object_unref(OBJECT(&s->upstream));
524344f4b15SPeter Maydell         return;
525344f4b15SPeter Maydell     }
526344f4b15SPeter Maydell 
527344f4b15SPeter Maydell     /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit
528344f4b15SPeter Maydell      * words, each bit of which indicates one block.
529344f4b15SPeter Maydell      */
530344f4b15SPeter Maydell     s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32);
531344f4b15SPeter Maydell 
532344f4b15SPeter Maydell     memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops,
533344f4b15SPeter Maydell                           s, "tz-mpc-regs", 0x1000);
534344f4b15SPeter Maydell     sysbus_init_mmio(sbd, &s->regmr);
535344f4b15SPeter Maydell 
536344f4b15SPeter Maydell     sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream));
537344f4b15SPeter Maydell 
538344f4b15SPeter Maydell     /* This memory region is not exposed to users of this device as a
539344f4b15SPeter Maydell      * sysbus MMIO region, but is instead used internally as something
540344f4b15SPeter Maydell      * that our IOMMU translate function might direct accesses to.
541344f4b15SPeter Maydell      */
542344f4b15SPeter Maydell     memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops,
543344f4b15SPeter Maydell                           s, "tz-mpc-blocked-io", size);
544344f4b15SPeter Maydell 
545344f4b15SPeter Maydell     address_space_init(&s->downstream_as, s->downstream,
546344f4b15SPeter Maydell                        "tz-mpc-downstream");
547344f4b15SPeter Maydell     address_space_init(&s->blocked_io_as, &s->blocked_io,
548344f4b15SPeter Maydell                        "tz-mpc-blocked-io");
549cdb60998SPeter Maydell 
550218fe5ceSPeter Maydell     s->blk_lut = g_new0(uint32_t, s->blk_max);
551cdb60998SPeter Maydell }
552cdb60998SPeter Maydell 
553cdb60998SPeter Maydell static int tz_mpc_post_load(void *opaque, int version_id)
554cdb60998SPeter Maydell {
555cdb60998SPeter Maydell     TZMPC *s = TZ_MPC(opaque);
556cdb60998SPeter Maydell 
557cdb60998SPeter Maydell     /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */
558cdb60998SPeter Maydell     if (s->blk_idx >= s->blk_max) {
559cdb60998SPeter Maydell         return -1;
560cdb60998SPeter Maydell     }
561cdb60998SPeter Maydell     return 0;
562344f4b15SPeter Maydell }
563344f4b15SPeter Maydell 
564344f4b15SPeter Maydell static const VMStateDescription tz_mpc_vmstate = {
565344f4b15SPeter Maydell     .name = "tz-mpc",
566344f4b15SPeter Maydell     .version_id = 1,
567344f4b15SPeter Maydell     .minimum_version_id = 1,
568cdb60998SPeter Maydell     .post_load = tz_mpc_post_load,
569344f4b15SPeter Maydell     .fields = (VMStateField[]) {
570cdb60998SPeter Maydell         VMSTATE_UINT32(ctrl, TZMPC),
571cdb60998SPeter Maydell         VMSTATE_UINT32(blk_idx, TZMPC),
572cdb60998SPeter Maydell         VMSTATE_UINT32(int_stat, TZMPC),
573cdb60998SPeter Maydell         VMSTATE_UINT32(int_en, TZMPC),
574cdb60998SPeter Maydell         VMSTATE_UINT32(int_info1, TZMPC),
575cdb60998SPeter Maydell         VMSTATE_UINT32(int_info2, TZMPC),
576cdb60998SPeter Maydell         VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max,
577cdb60998SPeter Maydell                               0, vmstate_info_uint32, uint32_t),
578344f4b15SPeter Maydell         VMSTATE_END_OF_LIST()
579344f4b15SPeter Maydell     }
580344f4b15SPeter Maydell };
581344f4b15SPeter Maydell 
582344f4b15SPeter Maydell static Property tz_mpc_properties[] = {
583344f4b15SPeter Maydell     DEFINE_PROP_LINK("downstream", TZMPC, downstream,
584344f4b15SPeter Maydell                      TYPE_MEMORY_REGION, MemoryRegion *),
585344f4b15SPeter Maydell     DEFINE_PROP_END_OF_LIST(),
586344f4b15SPeter Maydell };
587344f4b15SPeter Maydell 
588344f4b15SPeter Maydell static void tz_mpc_class_init(ObjectClass *klass, void *data)
589344f4b15SPeter Maydell {
590344f4b15SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
591344f4b15SPeter Maydell 
592344f4b15SPeter Maydell     dc->realize = tz_mpc_realize;
593344f4b15SPeter Maydell     dc->vmsd = &tz_mpc_vmstate;
594344f4b15SPeter Maydell     dc->reset = tz_mpc_reset;
595344f4b15SPeter Maydell     dc->props = tz_mpc_properties;
596344f4b15SPeter Maydell }
597344f4b15SPeter Maydell 
598344f4b15SPeter Maydell static const TypeInfo tz_mpc_info = {
599344f4b15SPeter Maydell     .name = TYPE_TZ_MPC,
600344f4b15SPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
601344f4b15SPeter Maydell     .instance_size = sizeof(TZMPC),
602344f4b15SPeter Maydell     .instance_init = tz_mpc_init,
603344f4b15SPeter Maydell     .class_init = tz_mpc_class_init,
604344f4b15SPeter Maydell };
605344f4b15SPeter Maydell 
606344f4b15SPeter Maydell static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass,
607344f4b15SPeter Maydell                                                   void *data)
608344f4b15SPeter Maydell {
609344f4b15SPeter Maydell     IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
610344f4b15SPeter Maydell 
611344f4b15SPeter Maydell     imrc->translate = tz_mpc_translate;
612344f4b15SPeter Maydell     imrc->attrs_to_index = tz_mpc_attrs_to_index;
613344f4b15SPeter Maydell     imrc->num_indexes = tz_mpc_num_indexes;
614344f4b15SPeter Maydell }
615344f4b15SPeter Maydell 
616344f4b15SPeter Maydell static const TypeInfo tz_mpc_iommu_memory_region_info = {
617344f4b15SPeter Maydell     .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION,
618344f4b15SPeter Maydell     .parent = TYPE_IOMMU_MEMORY_REGION,
619344f4b15SPeter Maydell     .class_init = tz_mpc_iommu_memory_region_class_init,
620344f4b15SPeter Maydell };
621344f4b15SPeter Maydell 
622344f4b15SPeter Maydell static void tz_mpc_register_types(void)
623344f4b15SPeter Maydell {
624344f4b15SPeter Maydell     type_register_static(&tz_mpc_info);
625344f4b15SPeter Maydell     type_register_static(&tz_mpc_iommu_memory_region_info);
626344f4b15SPeter Maydell }
627344f4b15SPeter Maydell 
628344f4b15SPeter Maydell type_init(tz_mpc_register_types);
629