xref: /openbmc/qemu/system/memory_ldst.c.inc (revision 4d6d8a05)
1/*
2 *  Physical memory access templates
3 *
4 *  Copyright (c) 2003 Fabrice Bellard
5 *  Copyright (c) 2015 Linaro, Inc.
6 *  Copyright (c) 2016 Red Hat, Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22/* warning: addr must be aligned */
23static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL,
24    hwaddr addr, MemTxAttrs attrs, MemTxResult *result,
25    enum device_endian endian)
26{
27    uint8_t *ptr;
28    uint64_t val;
29    MemoryRegion *mr;
30    hwaddr l = 4;
31    hwaddr addr1;
32    MemTxResult r;
33    bool release_lock = false;
34
35    RCU_READ_LOCK();
36    mr = TRANSLATE(addr, &addr1, &l, false, attrs);
37    if (l < 4 || !memory_access_is_direct(mr, false)) {
38        release_lock |= prepare_mmio_access(mr);
39
40        /* I/O case */
41        r = memory_region_dispatch_read(mr, addr1, &val,
42                                        MO_32 | devend_memop(endian), attrs);
43    } else {
44        /* RAM case */
45        fuzz_dma_read_cb(addr, 4, mr);
46        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
47        switch (endian) {
48        case DEVICE_LITTLE_ENDIAN:
49            val = ldl_le_p(ptr);
50            break;
51        case DEVICE_BIG_ENDIAN:
52            val = ldl_be_p(ptr);
53            break;
54        default:
55            val = ldl_p(ptr);
56            break;
57        }
58        r = MEMTX_OK;
59    }
60    if (result) {
61        *result = r;
62    }
63    if (release_lock) {
64        bql_unlock();
65    }
66    RCU_READ_UNLOCK();
67    return val;
68}
69
70uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL,
71    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
72{
73    return glue(address_space_ldl_internal, SUFFIX)(ARG1, addr, attrs, result,
74                                                    DEVICE_NATIVE_ENDIAN);
75}
76
77uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL,
78    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
79{
80    return glue(address_space_ldl_internal, SUFFIX)(ARG1, addr, attrs, result,
81                                                    DEVICE_LITTLE_ENDIAN);
82}
83
84uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL,
85    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
86{
87    return glue(address_space_ldl_internal, SUFFIX)(ARG1, addr, attrs, result,
88                                                    DEVICE_BIG_ENDIAN);
89}
90
91/* warning: addr must be aligned */
92static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL,
93    hwaddr addr, MemTxAttrs attrs, MemTxResult *result,
94    enum device_endian endian)
95{
96    uint8_t *ptr;
97    uint64_t val;
98    MemoryRegion *mr;
99    hwaddr l = 8;
100    hwaddr addr1;
101    MemTxResult r;
102    bool release_lock = false;
103
104    RCU_READ_LOCK();
105    mr = TRANSLATE(addr, &addr1, &l, false, attrs);
106    if (l < 8 || !memory_access_is_direct(mr, false)) {
107        release_lock |= prepare_mmio_access(mr);
108
109        /* I/O case */
110        r = memory_region_dispatch_read(mr, addr1, &val,
111                                        MO_64 | devend_memop(endian), attrs);
112    } else {
113        /* RAM case */
114        fuzz_dma_read_cb(addr, 8, mr);
115        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
116        switch (endian) {
117        case DEVICE_LITTLE_ENDIAN:
118            val = ldq_le_p(ptr);
119            break;
120        case DEVICE_BIG_ENDIAN:
121            val = ldq_be_p(ptr);
122            break;
123        default:
124            val = ldq_p(ptr);
125            break;
126        }
127        r = MEMTX_OK;
128    }
129    if (result) {
130        *result = r;
131    }
132    if (release_lock) {
133        bql_unlock();
134    }
135    RCU_READ_UNLOCK();
136    return val;
137}
138
139uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL,
140    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
141{
142    return glue(address_space_ldq_internal, SUFFIX)(ARG1, addr, attrs, result,
143                                                    DEVICE_NATIVE_ENDIAN);
144}
145
146uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL,
147    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
148{
149    return glue(address_space_ldq_internal, SUFFIX)(ARG1, addr, attrs, result,
150                                                    DEVICE_LITTLE_ENDIAN);
151}
152
153uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL,
154    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
155{
156    return glue(address_space_ldq_internal, SUFFIX)(ARG1, addr, attrs, result,
157                                                    DEVICE_BIG_ENDIAN);
158}
159
160uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL,
161    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
162{
163    uint8_t *ptr;
164    uint64_t val;
165    MemoryRegion *mr;
166    hwaddr l = 1;
167    hwaddr addr1;
168    MemTxResult r;
169    bool release_lock = false;
170
171    RCU_READ_LOCK();
172    mr = TRANSLATE(addr, &addr1, &l, false, attrs);
173    if (!memory_access_is_direct(mr, false)) {
174        release_lock |= prepare_mmio_access(mr);
175
176        /* I/O case */
177        r = memory_region_dispatch_read(mr, addr1, &val, MO_8, attrs);
178    } else {
179        /* RAM case */
180        fuzz_dma_read_cb(addr, 1, mr);
181        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
182        val = ldub_p(ptr);
183        r = MEMTX_OK;
184    }
185    if (result) {
186        *result = r;
187    }
188    if (release_lock) {
189        bql_unlock();
190    }
191    RCU_READ_UNLOCK();
192    return val;
193}
194
195/* warning: addr must be aligned */
196static inline uint16_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL,
197    hwaddr addr, MemTxAttrs attrs, MemTxResult *result,
198    enum device_endian endian)
199{
200    uint8_t *ptr;
201    uint64_t val;
202    MemoryRegion *mr;
203    hwaddr l = 2;
204    hwaddr addr1;
205    MemTxResult r;
206    bool release_lock = false;
207
208    RCU_READ_LOCK();
209    mr = TRANSLATE(addr, &addr1, &l, false, attrs);
210    if (l < 2 || !memory_access_is_direct(mr, false)) {
211        release_lock |= prepare_mmio_access(mr);
212
213        /* I/O case */
214        r = memory_region_dispatch_read(mr, addr1, &val,
215                                        MO_16 | devend_memop(endian), attrs);
216    } else {
217        /* RAM case */
218        fuzz_dma_read_cb(addr, 2, mr);
219        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
220        switch (endian) {
221        case DEVICE_LITTLE_ENDIAN:
222            val = lduw_le_p(ptr);
223            break;
224        case DEVICE_BIG_ENDIAN:
225            val = lduw_be_p(ptr);
226            break;
227        default:
228            val = lduw_p(ptr);
229            break;
230        }
231        r = MEMTX_OK;
232    }
233    if (result) {
234        *result = r;
235    }
236    if (release_lock) {
237        bql_unlock();
238    }
239    RCU_READ_UNLOCK();
240    return val;
241}
242
243uint16_t glue(address_space_lduw, SUFFIX)(ARG1_DECL,
244    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
245{
246    return glue(address_space_lduw_internal, SUFFIX)(ARG1, addr, attrs, result,
247                                                     DEVICE_NATIVE_ENDIAN);
248}
249
250uint16_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL,
251    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
252{
253    return glue(address_space_lduw_internal, SUFFIX)(ARG1, addr, attrs, result,
254                                                     DEVICE_LITTLE_ENDIAN);
255}
256
257uint16_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL,
258    hwaddr addr, MemTxAttrs attrs, MemTxResult *result)
259{
260    return glue(address_space_lduw_internal, SUFFIX)(ARG1, addr, attrs, result,
261                                       DEVICE_BIG_ENDIAN);
262}
263
264/* warning: addr must be aligned. The ram page is not masked as dirty
265   and the code inside is not invalidated. It is useful if the dirty
266   bits are used to track modified PTEs */
267void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL,
268    hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result)
269{
270    uint8_t *ptr;
271    MemoryRegion *mr;
272    hwaddr l = 4;
273    hwaddr addr1;
274    MemTxResult r;
275    uint8_t dirty_log_mask;
276    bool release_lock = false;
277
278    RCU_READ_LOCK();
279    mr = TRANSLATE(addr, &addr1, &l, true, attrs);
280    if (l < 4 || !memory_access_is_direct(mr, true)) {
281        release_lock |= prepare_mmio_access(mr);
282
283        r = memory_region_dispatch_write(mr, addr1, val, MO_32, attrs);
284    } else {
285        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
286        stl_p(ptr, val);
287
288        dirty_log_mask = memory_region_get_dirty_log_mask(mr);
289        dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
290        cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr,
291                                            4, dirty_log_mask);
292        r = MEMTX_OK;
293    }
294    if (result) {
295        *result = r;
296    }
297    if (release_lock) {
298        bql_unlock();
299    }
300    RCU_READ_UNLOCK();
301}
302
303/* warning: addr must be aligned */
304static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL,
305    hwaddr addr, uint32_t val, MemTxAttrs attrs,
306    MemTxResult *result, enum device_endian endian)
307{
308    uint8_t *ptr;
309    MemoryRegion *mr;
310    hwaddr l = 4;
311    hwaddr addr1;
312    MemTxResult r;
313    bool release_lock = false;
314
315    RCU_READ_LOCK();
316    mr = TRANSLATE(addr, &addr1, &l, true, attrs);
317    if (l < 4 || !memory_access_is_direct(mr, true)) {
318        release_lock |= prepare_mmio_access(mr);
319        r = memory_region_dispatch_write(mr, addr1, val,
320                                         MO_32 | devend_memop(endian), attrs);
321    } else {
322        /* RAM case */
323        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
324        switch (endian) {
325        case DEVICE_LITTLE_ENDIAN:
326            stl_le_p(ptr, val);
327            break;
328        case DEVICE_BIG_ENDIAN:
329            stl_be_p(ptr, val);
330            break;
331        default:
332            stl_p(ptr, val);
333            break;
334        }
335        invalidate_and_set_dirty(mr, addr1, 4);
336        r = MEMTX_OK;
337    }
338    if (result) {
339        *result = r;
340    }
341    if (release_lock) {
342        bql_unlock();
343    }
344    RCU_READ_UNLOCK();
345}
346
347void glue(address_space_stl, SUFFIX)(ARG1_DECL,
348    hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result)
349{
350    glue(address_space_stl_internal, SUFFIX)(ARG1, addr, val, attrs,
351                                             result, DEVICE_NATIVE_ENDIAN);
352}
353
354void glue(address_space_stl_le, SUFFIX)(ARG1_DECL,
355    hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result)
356{
357    glue(address_space_stl_internal, SUFFIX)(ARG1, addr, val, attrs,
358                                             result, DEVICE_LITTLE_ENDIAN);
359}
360
361void glue(address_space_stl_be, SUFFIX)(ARG1_DECL,
362    hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result)
363{
364    glue(address_space_stl_internal, SUFFIX)(ARG1, addr, val, attrs,
365                                             result, DEVICE_BIG_ENDIAN);
366}
367
368void glue(address_space_stb, SUFFIX)(ARG1_DECL,
369    hwaddr addr, uint8_t val, MemTxAttrs attrs, MemTxResult *result)
370{
371    uint8_t *ptr;
372    MemoryRegion *mr;
373    hwaddr l = 1;
374    hwaddr addr1;
375    MemTxResult r;
376    bool release_lock = false;
377
378    RCU_READ_LOCK();
379    mr = TRANSLATE(addr, &addr1, &l, true, attrs);
380    if (!memory_access_is_direct(mr, true)) {
381        release_lock |= prepare_mmio_access(mr);
382        r = memory_region_dispatch_write(mr, addr1, val, MO_8, attrs);
383    } else {
384        /* RAM case */
385        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
386        stb_p(ptr, val);
387        invalidate_and_set_dirty(mr, addr1, 1);
388        r = MEMTX_OK;
389    }
390    if (result) {
391        *result = r;
392    }
393    if (release_lock) {
394        bql_unlock();
395    }
396    RCU_READ_UNLOCK();
397}
398
399/* warning: addr must be aligned */
400static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL,
401    hwaddr addr, uint16_t val, MemTxAttrs attrs,
402    MemTxResult *result, enum device_endian endian)
403{
404    uint8_t *ptr;
405    MemoryRegion *mr;
406    hwaddr l = 2;
407    hwaddr addr1;
408    MemTxResult r;
409    bool release_lock = false;
410
411    RCU_READ_LOCK();
412    mr = TRANSLATE(addr, &addr1, &l, true, attrs);
413    if (l < 2 || !memory_access_is_direct(mr, true)) {
414        release_lock |= prepare_mmio_access(mr);
415        r = memory_region_dispatch_write(mr, addr1, val,
416                                         MO_16 | devend_memop(endian), attrs);
417    } else {
418        /* RAM case */
419        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
420        switch (endian) {
421        case DEVICE_LITTLE_ENDIAN:
422            stw_le_p(ptr, val);
423            break;
424        case DEVICE_BIG_ENDIAN:
425            stw_be_p(ptr, val);
426            break;
427        default:
428            stw_p(ptr, val);
429            break;
430        }
431        invalidate_and_set_dirty(mr, addr1, 2);
432        r = MEMTX_OK;
433    }
434    if (result) {
435        *result = r;
436    }
437    if (release_lock) {
438        bql_unlock();
439    }
440    RCU_READ_UNLOCK();
441}
442
443void glue(address_space_stw, SUFFIX)(ARG1_DECL,
444    hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result)
445{
446    glue(address_space_stw_internal, SUFFIX)(ARG1, addr, val, attrs, result,
447                                             DEVICE_NATIVE_ENDIAN);
448}
449
450void glue(address_space_stw_le, SUFFIX)(ARG1_DECL,
451    hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result)
452{
453    glue(address_space_stw_internal, SUFFIX)(ARG1, addr, val, attrs, result,
454                                             DEVICE_LITTLE_ENDIAN);
455}
456
457void glue(address_space_stw_be, SUFFIX)(ARG1_DECL,
458    hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result)
459{
460    glue(address_space_stw_internal, SUFFIX)(ARG1, addr, val, attrs, result,
461                               DEVICE_BIG_ENDIAN);
462}
463
464static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL,
465    hwaddr addr, uint64_t val, MemTxAttrs attrs,
466    MemTxResult *result, enum device_endian endian)
467{
468    uint8_t *ptr;
469    MemoryRegion *mr;
470    hwaddr l = 8;
471    hwaddr addr1;
472    MemTxResult r;
473    bool release_lock = false;
474
475    RCU_READ_LOCK();
476    mr = TRANSLATE(addr, &addr1, &l, true, attrs);
477    if (l < 8 || !memory_access_is_direct(mr, true)) {
478        release_lock |= prepare_mmio_access(mr);
479        r = memory_region_dispatch_write(mr, addr1, val,
480                                         MO_64 | devend_memop(endian), attrs);
481    } else {
482        /* RAM case */
483        ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
484        switch (endian) {
485        case DEVICE_LITTLE_ENDIAN:
486            stq_le_p(ptr, val);
487            break;
488        case DEVICE_BIG_ENDIAN:
489            stq_be_p(ptr, val);
490            break;
491        default:
492            stq_p(ptr, val);
493            break;
494        }
495        invalidate_and_set_dirty(mr, addr1, 8);
496        r = MEMTX_OK;
497    }
498    if (result) {
499        *result = r;
500    }
501    if (release_lock) {
502        bql_unlock();
503    }
504    RCU_READ_UNLOCK();
505}
506
507void glue(address_space_stq, SUFFIX)(ARG1_DECL,
508    hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result)
509{
510    glue(address_space_stq_internal, SUFFIX)(ARG1, addr, val, attrs, result,
511                                             DEVICE_NATIVE_ENDIAN);
512}
513
514void glue(address_space_stq_le, SUFFIX)(ARG1_DECL,
515    hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result)
516{
517    glue(address_space_stq_internal, SUFFIX)(ARG1, addr, val, attrs, result,
518                                             DEVICE_LITTLE_ENDIAN);
519}
520
521void glue(address_space_stq_be, SUFFIX)(ARG1_DECL,
522    hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result)
523{
524    glue(address_space_stq_internal, SUFFIX)(ARG1, addr, val, attrs, result,
525                                             DEVICE_BIG_ENDIAN);
526}
527
528#undef ARG1_DECL
529#undef ARG1
530#undef SUFFIX
531#undef TRANSLATE
532#undef RCU_READ_LOCK
533#undef RCU_READ_UNLOCK
534