1 /******************************************************************************
2  * common/compat/grant_table.c
3  *
4  */
5 
6 #include <xen/hypercall.h>
7 #include <compat/grant_table.h>
8 
9 #define xen_grant_entry_v1 grant_entry_v1
10 CHECK_grant_entry_v1;
11 #undef xen_grant_entry_v1
12 
13 #define xen_grant_entry_header grant_entry_header
14 CHECK_grant_entry_header;
15 #undef xen_grant_entry_header
16 
17 #define xen_grant_entry_v2 grant_entry_v2
18 CHECK_grant_entry_v2;
19 #undef xen_grant_entry_v2
20 
21 #define xen_gnttab_map_grant_ref gnttab_map_grant_ref
22 CHECK_gnttab_map_grant_ref;
23 #undef xen_gnttab_map_grant_ref
24 
25 #define xen_gnttab_unmap_grant_ref gnttab_unmap_grant_ref
26 CHECK_gnttab_unmap_grant_ref;
27 #undef xen_gnttab_unmap_grant_ref
28 
29 #define xen_gnttab_unmap_and_replace gnttab_unmap_and_replace
30 CHECK_gnttab_unmap_and_replace;
31 #undef xen_gnttab_unmap_and_replace
32 
33 #define xen_gnttab_query_size gnttab_query_size
34 CHECK_gnttab_query_size;
35 #undef xen_gnttab_query_size
36 
37 DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_compat_t);
38 DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_compat_t);
39 DEFINE_XEN_GUEST_HANDLE(gnttab_copy_compat_t);
40 
41 #define xen_gnttab_dump_table gnttab_dump_table
42 CHECK_gnttab_dump_table;
43 #undef xen_gnttab_dump_table
44 
45 #define xen_gnttab_set_version gnttab_set_version
46 CHECK_gnttab_set_version;
47 #undef xen_gnttab_set_version
48 
49 DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_compat_t);
50 
51 #define xen_gnttab_get_version gnttab_get_version
52 CHECK_gnttab_get_version;
53 #undef xen_gnttab_get_version
54 
55 #define xen_gnttab_swap_grant_ref gnttab_swap_grant_ref
56 CHECK_gnttab_swap_grant_ref;
57 #undef xen_gnttab_swap_grant_ref
58 
59 #define xen_gnttab_cache_flush gnttab_cache_flush
60 CHECK_gnttab_cache_flush;
61 #undef xen_gnttab_cache_flush
62 
compat_grant_table_op(unsigned int cmd,XEN_GUEST_HANDLE_PARAM (void)uop,unsigned int count)63 int compat_grant_table_op(
64     unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) uop, unsigned int count)
65 {
66     int rc = 0;
67     unsigned int i, cmd_op;
68     XEN_GUEST_HANDLE_PARAM(void) cnt_uop;
69 
70 #ifdef CONFIG_PV_SHIM
71     if ( unlikely(pv_shim) )
72         return pv_shim_grant_table_op(cmd, uop, count);
73 #endif
74 
75     set_xen_guest_handle(cnt_uop, NULL);
76     cmd_op = cmd & GNTTABOP_CMD_MASK;
77     if ( cmd_op != GNTTABOP_cache_flush )
78         cmd_op = cmd;
79     switch ( cmd_op )
80     {
81 #define CASE(name)                                                  \
82     case GNTTABOP_ ## name:                                         \
83     {                                                               \
84         XEN_GUEST_HANDLE_PARAM(gnttab_ ## name ## _compat_t) h =    \
85             guest_handle_cast(uop, gnttab_ ## name ## _compat_t);   \
86                                                                     \
87         if ( unlikely(!guest_handle_okay(h, count)) )               \
88             rc = -EFAULT;                                           \
89     }                                                               \
90         break
91 
92 #ifndef CHECK_gnttab_map_grant_ref
93     CASE(map_grant_ref);
94 #endif
95 
96 #ifndef CHECK_gnttab_unmap_grant_ref
97     CASE(unmap_grant_ref);
98 #endif
99 
100 #ifndef CHECK_gnttab_unmap_and_replace
101     CASE(unmap_and_replace);
102 #endif
103 
104 #ifndef CHECK_gnttab_setup_table
105     CASE(setup_table);
106 #endif
107 
108 #ifndef CHECK_gnttab_transfer
109     CASE(transfer);
110 #endif
111 
112 #ifndef CHECK_gnttab_copy
113     CASE(copy);
114 #endif
115 
116 #ifndef CHECK_gnttab_query_size
117     CASE(query_size);
118 #endif
119 
120 #ifndef CHECK_gnttab_dump_table
121     CASE(dump_table);
122 #endif
123 
124 #ifndef CHECK_gnttab_get_status_frames
125     CASE(get_status_frames);
126 #endif
127 
128 #ifndef CHECK_gnttab_swap_grant_ref
129     CASE(swap_grant_ref);
130 #endif
131 
132 #ifndef CHECK_gnttab_cache_flush
133     CASE(cache_flush);
134 #endif
135 
136 #undef CASE
137     default:
138         return do_grant_table_op(cmd, uop, count);
139     }
140 
141     if ( (int)count < 0 )
142         rc = -EINVAL;
143 
144     for ( i = 0; i < count && rc == 0; )
145     {
146         unsigned int n;
147         union {
148             XEN_GUEST_HANDLE(void) uop;
149             struct gnttab_setup_table *setup;
150             struct gnttab_transfer *xfer;
151             struct gnttab_copy *copy;
152             struct gnttab_get_status_frames *get_status;
153         } nat;
154         union {
155             struct compat_gnttab_setup_table setup;
156             struct compat_gnttab_transfer xfer;
157             struct compat_gnttab_copy copy;
158             struct compat_gnttab_get_status_frames get_status;
159         } cmp;
160 
161         set_xen_guest_handle(nat.uop, COMPAT_ARG_XLAT_VIRT_BASE);
162         switch ( cmd_op )
163         {
164         case GNTTABOP_setup_table:
165             if ( unlikely(count > 1) )
166                 rc = -EINVAL;
167             else if ( unlikely(__copy_from_guest(&cmp.setup, uop, 1)) )
168                 rc = -EFAULT;
169             else if ( unlikely(!compat_handle_okay(cmp.setup.frame_list, cmp.setup.nr_frames)) )
170                 rc = -EFAULT;
171             else
172             {
173                 unsigned int max_frame_list_size_in_page =
174                     (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.setup)) /
175                     sizeof(*nat.setup->frame_list.p);
176 
177 #define XLAT_gnttab_setup_table_HNDL_frame_list(_d_, _s_) \
178                 set_xen_guest_handle((_d_)->frame_list, (unsigned long *)(nat.setup + 1))
179                 XLAT_gnttab_setup_table(nat.setup, &cmp.setup);
180 #undef XLAT_gnttab_setup_table_HNDL_frame_list
181                 rc = gnttab_setup_table(guest_handle_cast(nat.uop,
182                                                           gnttab_setup_table_t),
183                                         1, max_frame_list_size_in_page);
184             }
185             ASSERT(rc <= 0);
186             if ( rc == 0 )
187             {
188 #define XLAT_gnttab_setup_table_HNDL_frame_list(_d_, _s_) \
189                 do \
190                 { \
191                     if ( (_s_)->status == GNTST_okay ) \
192                     { \
193                         for ( i = 0; i < (_s_)->nr_frames; ++i ) \
194                         { \
195                             compat_pfn_t frame = (_s_)->frame_list.p[i]; \
196                             if ( frame != (_s_)->frame_list.p[i] ) \
197                             { \
198                                 (_s_)->status = GNTST_address_too_big; \
199                                 break; \
200                             } \
201                             if ( __copy_to_compat_offset((_d_)->frame_list, \
202                                                          i, &frame, 1) ) \
203                             { \
204                                 (_s_)->status = GNTST_bad_virt_addr; \
205                                 break; \
206                             } \
207                         } \
208                     } \
209                 } while (0)
210                 XLAT_gnttab_setup_table(&cmp.setup, nat.setup);
211 #undef XLAT_gnttab_setup_table_HNDL_frame_list
212                 if ( unlikely(__copy_to_guest(uop, &cmp.setup, 1)) )
213                     rc = -EFAULT;
214                 else
215                     i = 1;
216             }
217             break;
218 
219         case GNTTABOP_transfer:
220             for ( n = 0; n < COMPAT_ARG_XLAT_SIZE / sizeof(*nat.xfer) && i < count && rc == 0; ++i, ++n )
221             {
222                 if ( unlikely(__copy_from_guest_offset(&cmp.xfer, uop, i, 1)) )
223                     rc = -EFAULT;
224                 else
225                 {
226                     XLAT_gnttab_transfer(nat.xfer + n, &cmp.xfer);
227                 }
228             }
229             if ( rc == 0 )
230                 rc = gnttab_transfer(guest_handle_cast(nat.uop, gnttab_transfer_t), n);
231             if ( rc > 0 )
232             {
233                 ASSERT(rc < n);
234                 i -= n - rc;
235                 n = rc;
236             }
237             if ( rc >= 0 )
238             {
239                 XEN_GUEST_HANDLE_PARAM(gnttab_transfer_compat_t) xfer;
240 
241                 xfer = guest_handle_cast(uop, gnttab_transfer_compat_t);
242                 guest_handle_add_offset(xfer, i);
243                 cnt_uop = guest_handle_cast(xfer, void);
244                 while ( n-- )
245                 {
246                     guest_handle_subtract_offset(xfer, 1);
247                     if ( __copy_field_to_guest(xfer, nat.xfer + n, status) )
248                         rc = -EFAULT;
249                 }
250             }
251             break;
252 
253         case GNTTABOP_copy:
254             for ( n = 0; n < COMPAT_ARG_XLAT_SIZE / sizeof(*nat.copy) && i < count && rc == 0; ++i, ++n )
255             {
256                 if ( unlikely(__copy_from_guest_offset(&cmp.copy, uop, i, 1)) )
257                     rc = -EFAULT;
258                 else
259                 {
260                     enum XLAT_gnttab_copy_source_u source_u;
261                     enum XLAT_gnttab_copy_dest_u dest_u;
262 
263                     if ( cmp.copy.flags & GNTCOPY_source_gref )
264                         source_u = XLAT_gnttab_copy_source_u_ref;
265                     else
266                         source_u = XLAT_gnttab_copy_source_u_gmfn;
267                     if ( cmp.copy.flags & GNTCOPY_dest_gref )
268                         dest_u = XLAT_gnttab_copy_dest_u_ref;
269                     else
270                         dest_u = XLAT_gnttab_copy_dest_u_gmfn;
271                     XLAT_gnttab_copy(nat.copy + n, &cmp.copy);
272                 }
273             }
274             if ( rc == 0 )
275                 rc = gnttab_copy(guest_handle_cast(nat.uop, gnttab_copy_t), n);
276             if ( rc > 0 )
277             {
278                 ASSERT(rc <= n);
279                 i -= rc;
280                 n -= rc;
281             }
282             if ( rc >= 0 )
283             {
284                 XEN_GUEST_HANDLE_PARAM(gnttab_copy_compat_t) copy;
285 
286                 copy = guest_handle_cast(uop, gnttab_copy_compat_t);
287                 guest_handle_add_offset(copy, i);
288                 cnt_uop = guest_handle_cast(copy, void);
289                 while ( n-- )
290                 {
291                     guest_handle_subtract_offset(copy, 1);
292                     if ( __copy_field_to_guest(copy, nat.copy + n, status) )
293                         rc = -EFAULT;
294                 }
295             }
296             break;
297 
298         case GNTTABOP_get_status_frames:
299             if ( count != 1)
300             {
301                 rc = -EINVAL;
302                 break;
303             }
304             if ( unlikely(__copy_from_guest(&cmp.get_status, uop, 1) ||
305                           !compat_handle_okay(cmp.get_status.frame_list,
306                                               cmp.get_status.nr_frames)) )
307             {
308                 rc = -EFAULT;
309                 break;
310             }
311 
312 #define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
313             guest_from_compat_handle((_d_)->frame_list, (_s_)->frame_list)
314             XLAT_gnttab_get_status_frames(nat.get_status, &cmp.get_status);
315 #undef XLAT_gnttab_get_status_frames_HNDL_frame_list
316 
317             rc = gnttab_get_status_frames(
318                 guest_handle_cast(nat.uop, gnttab_get_status_frames_t), count);
319             if ( rc >= 0 )
320             {
321                 XEN_GUEST_HANDLE_PARAM(gnttab_get_status_frames_compat_t) get =
322                     guest_handle_cast(uop,
323                                       gnttab_get_status_frames_compat_t);
324 
325                 if ( unlikely(__copy_field_to_guest(get, nat.get_status,
326                                                     status)) )
327                     rc = -EFAULT;
328                 else
329                     i = 1;
330             }
331             break;
332 
333         default:
334             domain_crash(current->domain);
335             break;
336         }
337     }
338 
339     if ( rc > 0 )
340     {
341         ASSERT(i < count);
342         ASSERT(!guest_handle_is_null(cnt_uop));
343         rc = hypercall_create_continuation(__HYPERVISOR_grant_table_op,
344                                            "ihi", cmd, cnt_uop, count - i);
345     }
346 
347     return rc;
348 }
349 
350 /*
351  * Local variables:
352  * mode: C
353  * c-file-style: "BSD"
354  * c-basic-offset: 4
355  * tab-width: 4
356  * indent-tabs-mode: nil
357  * End:
358  */
359