1 /*
2  * Copyright (c) 2013, Google, Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <err.h>
25 #include <lib/trusty/uio.h>
26 #include <string.h>
27 
membuf_to_kern_iovec(const struct iovec_kern * iov,uint iov_cnt,const uint8_t * buf,size_t len)28 ssize_t membuf_to_kern_iovec(const struct iovec_kern* iov,
29                              uint iov_cnt,
30                              const uint8_t* buf,
31                              size_t len) {
32     size_t copied = 0;
33 
34     if (unlikely(iov_cnt == 0 || len == 0))
35         return 0;
36 
37     if (unlikely(iov == NULL || buf == NULL))
38         return (ssize_t)ERR_INVALID_ARGS;
39 
40     for (uint i = 0; i < iov_cnt; i++, iov++) {
41         size_t to_copy = len;
42         if (to_copy > iov->iov_len)
43             to_copy = iov->iov_len;
44 
45         if (unlikely(to_copy == 0))
46             continue;
47 
48         if (unlikely(iov->iov_base == NULL))
49             return (ssize_t)ERR_INVALID_ARGS;
50 
51         memcpy(iov->iov_base, buf, to_copy);
52 
53         copied += to_copy;
54         buf += to_copy;
55         len -= to_copy;
56 
57         if (len == 0)
58             break;
59     }
60 
61     return (ssize_t)copied;
62 }
63 
kern_iovec_to_membuf(uint8_t * buf,size_t len,const struct iovec_kern * iov,uint iov_cnt)64 ssize_t kern_iovec_to_membuf(uint8_t* buf,
65                              size_t len,
66                              const struct iovec_kern* iov,
67                              uint iov_cnt) {
68     size_t copied = 0;
69 
70     if (unlikely(iov_cnt == 0 || len == 0))
71         return 0;
72 
73     if (unlikely(buf == NULL || iov == NULL))
74         return (ssize_t)ERR_INVALID_ARGS;
75 
76     for (uint i = 0; i < iov_cnt; i++, iov++) {
77         size_t to_copy = len;
78         if (to_copy > iov->iov_len)
79             to_copy = iov->iov_len;
80 
81         if (unlikely(to_copy == 0))
82             continue;
83 
84         if (unlikely(iov->iov_base == NULL))
85             return (ssize_t)ERR_INVALID_ARGS;
86 
87         memcpy(buf, iov->iov_base, to_copy);
88 
89         copied += to_copy;
90         buf += to_copy;
91         len -= to_copy;
92 
93         if (len == 0)
94             break;
95     }
96 
97     return (ssize_t)copied;
98 }
99 
membuf_to_user_iovec(user_addr_t iov_uaddr,uint iov_cnt,const uint8_t * buf,size_t len)100 ssize_t membuf_to_user_iovec(user_addr_t iov_uaddr,
101                              uint iov_cnt,
102                              const uint8_t* buf,
103                              size_t len) {
104     status_t ret;
105     size_t copied = 0;
106     struct iovec_user uiov;
107 
108     if (unlikely(iov_cnt == 0 || len == 0))
109         return 0;
110 
111     if (unlikely(buf == NULL))
112         return (ssize_t)ERR_INVALID_ARGS;
113 
114     /* for each iov entry */
115     for (uint i = 0; i < iov_cnt; i++) {
116         /* copy user iovec from user space into local buffer */
117         ret = copy_from_user(&uiov, iov_uaddr + i * sizeof(struct iovec_user),
118                              sizeof(struct iovec_user));
119         if (unlikely(ret != NO_ERROR))
120             return (ssize_t)ret;
121 
122         size_t to_copy = len;
123         if (to_copy > uiov.iov_len)
124             to_copy = uiov.iov_len;
125 
126         /* copy data to user space */
127         ret = copy_to_user(uiov.iov_base, buf, to_copy);
128         if (unlikely(ret != NO_ERROR))
129             return (ssize_t)ret;
130 
131         copied += to_copy;
132         buf += to_copy;
133         len -= to_copy;
134 
135         if (len == 0)
136             break;
137         ;
138     }
139 
140     return (ssize_t)copied;
141 }
142 
user_iovec_to_membuf_iter(uint8_t * buf,size_t buf_len,user_addr_t iov_uaddr,struct iovec_iter * iter)143 ssize_t user_iovec_to_membuf_iter(uint8_t* buf,
144                                   size_t buf_len,
145                                   user_addr_t iov_uaddr,
146                                   struct iovec_iter* iter) {
147     status_t ret;
148     size_t copied = 0;
149     struct iovec_user uiov;
150 
151     if (unlikely(buf_len == 0))
152         return 0;
153 
154     if (unlikely(buf == NULL))
155         return (ssize_t)ERR_INVALID_ARGS;
156 
157     while (buf_len > 0 && iovec_iter_has_next(iter)) {
158         /* copy user iovec from user space into local buffer */
159         ret = copy_from_user(
160                 &uiov, iov_uaddr + iter->iov_index * sizeof(struct iovec_user),
161                 sizeof(struct iovec_user));
162         if (unlikely(ret != NO_ERROR))
163             return (ssize_t)ret;
164 
165         /* we've re-read the iov from userspace, it may have changed */
166         if (uiov.iov_len < iter->data_offset)
167             return (ssize_t)ERR_INVALID_ARGS;
168 
169         /* figure out how much to copy */
170         size_t to_copy = uiov.iov_len - iter->data_offset;
171         if (to_copy > buf_len)
172             to_copy = buf_len;
173 
174         /* copy data from user space */
175         ret = copy_from_user(buf, uiov.iov_base + iter->data_offset, to_copy);
176         if (unlikely(ret != NO_ERROR))
177             return (ssize_t)ret;
178 
179         /* update the input state */
180         iter->data_offset += to_copy;
181         if (iter->data_offset >= uiov.iov_len) {
182             iter->iov_index += 1;
183             iter->data_offset = 0;
184         }
185 
186         /* update the output state */
187         copied += to_copy;
188         buf += to_copy;
189         buf_len -= to_copy;
190     }
191 
192     return (ssize_t)copied;
193 }
194 
user_iovec_to_membuf(uint8_t * buf,size_t buf_len,user_addr_t iov_uaddr,uint iov_cnt)195 ssize_t user_iovec_to_membuf(uint8_t* buf,
196                              size_t buf_len,
197                              user_addr_t iov_uaddr,
198                              uint iov_cnt) {
199     struct iovec_iter iter = iovec_iter_create(iov_cnt);
200     return user_iovec_to_membuf_iter(buf, buf_len, iov_uaddr, &iter);
201 }
202