xref: /openbmc/qemu/linux-user/uaccess.c (revision 19f4ed36)
1 /* User memory access */
2 #include "qemu/osdep.h"
3 #include "qemu/cutils.h"
4 
5 #include "qemu.h"
6 
7 void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy)
8 {
9     void *host_addr;
10 
11     guest_addr = cpu_untagged_addr(thread_cpu, guest_addr);
12     if (!access_ok_untagged(type, guest_addr, len)) {
13         return NULL;
14     }
15     host_addr = g2h_untagged(guest_addr);
16 #ifdef DEBUG_REMAP
17     if (copy) {
18         host_addr = g_memdup(host_addr, len);
19     } else {
20         host_addr = g_malloc0(len);
21     }
22 #endif
23     return host_addr;
24 }
25 
26 #ifdef DEBUG_REMAP
27 void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len)
28 {
29     void *host_ptr_conv;
30 
31     if (!host_ptr) {
32         return;
33     }
34     host_ptr_conv = g2h(thread_cpu, guest_addr);
35     if (host_ptr == host_ptr_conv) {
36         return;
37     }
38     if (len > 0) {
39         memcpy(host_ptr_conv, host_ptr, len);
40     }
41     g_free(host_ptr);
42 }
43 #endif
44 
45 void *lock_user_string(abi_ulong guest_addr)
46 {
47     ssize_t len = target_strlen(guest_addr);
48     if (len < 0) {
49         return NULL;
50     }
51     return lock_user(VERIFY_READ, guest_addr, len + 1, 1);
52 }
53 
54 /* copy_from_user() and copy_to_user() are usually used to copy data
55  * buffers between the target and host.  These internally perform
56  * locking/unlocking of the memory.
57  */
58 int copy_from_user(void *hptr, abi_ulong gaddr, ssize_t len)
59 {
60     int ret = 0;
61     void *ghptr = lock_user(VERIFY_READ, gaddr, len, 1);
62 
63     if (ghptr) {
64         memcpy(hptr, ghptr, len);
65         unlock_user(ghptr, gaddr, 0);
66     } else {
67         ret = -TARGET_EFAULT;
68     }
69     return ret;
70 }
71 
72 int copy_to_user(abi_ulong gaddr, void *hptr, ssize_t len)
73 {
74     int ret = 0;
75     void *ghptr = lock_user(VERIFY_WRITE, gaddr, len, 0);
76 
77     if (ghptr) {
78         memcpy(ghptr, hptr, len);
79         unlock_user(ghptr, gaddr, len);
80     } else {
81         ret = -TARGET_EFAULT;
82     }
83 
84     return ret;
85 }
86 
87 /* Return the length of a string in target memory or -TARGET_EFAULT if
88    access error  */
89 ssize_t target_strlen(abi_ulong guest_addr1)
90 {
91     uint8_t *ptr;
92     abi_ulong guest_addr;
93     size_t max_len, len;
94 
95     guest_addr = guest_addr1;
96     for(;;) {
97         max_len = TARGET_PAGE_SIZE - (guest_addr & ~TARGET_PAGE_MASK);
98         ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1);
99         if (!ptr)
100             return -TARGET_EFAULT;
101         len = qemu_strnlen((const char *)ptr, max_len);
102         unlock_user(ptr, guest_addr, 0);
103         guest_addr += len;
104         /* we don't allow wrapping or integer overflow */
105         if (guest_addr == 0 || (guest_addr - guest_addr1) > 0x7fffffff) {
106             return -TARGET_EFAULT;
107         }
108         if (len != max_len) {
109             break;
110         }
111     }
112     return guest_addr - guest_addr1;
113 }
114