xref: /openbmc/qemu/semihosting/uaccess.c (revision 9c2ff9cdc9b33472333e9431cbf4417f5f228883)
1 /*
2  * Helper routines to provide target memory access for semihosting
3  * syscalls in system emulation mode.
4  *
5  * Copyright (c) 2007 CodeSourcery.
6  *
7  * This code is licensed under the GPL
8  */
9 
10 #include "qemu/osdep.h"
11 #include "exec/cpu-all.h"
12 #include "accel/tcg/cpu-mmu-index.h"
13 #include "exec/exec-all.h"
14 #include "exec/target_page.h"
15 #include "exec/tlb-flags.h"
16 #include "semihosting/uaccess.h"
17 
18 void *uaccess_lock_user(CPUArchState *env, target_ulong addr,
19                         target_ulong len, bool copy)
20 {
21     void *p = malloc(len);
22     if (p && copy) {
23         if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
24             free(p);
25             p = NULL;
26         }
27     }
28     return p;
29 }
30 
31 ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr)
32 {
33     int mmu_idx = cpu_mmu_index(env_cpu(env), false);
34     size_t len = 0;
35 
36     while (1) {
37         size_t left_in_page;
38         int flags;
39         void *h;
40 
41         /* Find the number of bytes remaining in the page. */
42         left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK);
43 
44         flags = probe_access_flags(env, addr, 0, MMU_DATA_LOAD,
45                                    mmu_idx, true, &h, 0);
46         if (flags & TLB_INVALID_MASK) {
47             return -1;
48         }
49         if (flags & TLB_MMIO) {
50             do {
51                 uint8_t c;
52                 if (cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0)) {
53                     return -1;
54                 }
55                 if (c == 0) {
56                     return len;
57                 }
58                 addr++;
59                 len++;
60                 if (len > INT32_MAX) {
61                     return -1;
62                 }
63             } while (--left_in_page != 0);
64         } else {
65             char *p = memchr(h, 0, left_in_page);
66             if (p) {
67                 len += p - (char *)h;
68                 return len <= INT32_MAX ? (ssize_t)len : -1;
69             }
70             addr += left_in_page;
71             len += left_in_page;
72             if (len > INT32_MAX) {
73                 return -1;
74             }
75         }
76     }
77 }
78 
79 char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr)
80 {
81     ssize_t len = uaccess_strlen_user(env, addr);
82     if (len < 0) {
83         return NULL;
84     }
85     return uaccess_lock_user(env, addr, len + 1, true);
86 }
87 
88 void uaccess_unlock_user(CPUArchState *env, void *p,
89                          target_ulong addr, target_ulong len)
90 {
91     if (len) {
92         cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1);
93     }
94     free(p);
95 }
96