1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #include <xen/domain_page.h>
3 #include <xen/guest_access.h>
4 #include <xen/lib.h>
5 #include <xen/mm.h>
6 #include <xen/sched.h>
7 
8 #include <asm/current.h>
9 
10 #define COPY_flush_dcache   (1U << 0)
11 #define COPY_from_guest     (0U << 1)
12 #define COPY_to_guest       (1U << 1)
13 #define COPY_ipa            (0U << 2)
14 #define COPY_linear         (1U << 2)
15 
16 typedef union
17 {
18     struct
19     {
20         struct vcpu *v;
21     } gva;
22 
23     struct
24     {
25         struct domain *d;
26     } gpa;
27 } copy_info_t;
28 
29 #define GVA_INFO(vcpu) ((copy_info_t) { .gva = { vcpu } })
30 #define GPA_INFO(domain) ((copy_info_t) { .gpa = { domain } })
31 
translate_get_page(copy_info_t info,uint64_t addr,bool linear,bool write)32 static struct page_info *translate_get_page(copy_info_t info, uint64_t addr,
33                                             bool linear, bool write)
34 {
35     p2m_type_t p2mt;
36     struct page_info *page;
37 
38     if ( linear )
39         return get_page_from_gva(info.gva.v, addr,
40                                  write ? GV2M_WRITE : GV2M_READ);
41 
42     page = get_page_from_gfn(info.gpa.d, paddr_to_pfn(addr), &p2mt, P2M_ALLOC);
43 
44     if ( !page )
45         return NULL;
46 
47     if ( !p2m_is_ram(p2mt) )
48     {
49         put_page(page);
50         return NULL;
51     }
52 
53     return page;
54 }
55 
copy_guest(void * buf,uint64_t addr,unsigned int len,copy_info_t info,unsigned int flags)56 static unsigned long copy_guest(void *buf, uint64_t addr, unsigned int len,
57                                 copy_info_t info, unsigned int flags)
58 {
59     /* XXX needs to handle faults */
60     unsigned int offset = addr & ~PAGE_MASK;
61 
62     BUILD_BUG_ON((sizeof(addr)) < sizeof(vaddr_t));
63     BUILD_BUG_ON((sizeof(addr)) < sizeof(paddr_t));
64 
65     while ( len )
66     {
67         void *p;
68         unsigned int size = min(len, (unsigned int)PAGE_SIZE - offset);
69         struct page_info *page;
70 
71         page = translate_get_page(info, addr, flags & COPY_linear,
72                                   flags & COPY_to_guest);
73         if ( page == NULL )
74             return len;
75 
76         p = __map_domain_page(page);
77         p += offset;
78         if ( flags & COPY_to_guest )
79         {
80             /*
81              * buf will be NULL when the caller request to zero the
82              * guest memory.
83              */
84             if ( buf )
85                 memcpy(p, buf, size);
86             else
87                 memset(p, 0, size);
88         }
89         else
90             memcpy(buf, p, size);
91 
92         if ( flags & COPY_flush_dcache )
93             clean_dcache_va_range(p, size);
94 
95         unmap_domain_page(p - offset);
96         put_page(page);
97         len -= size;
98         buf += size;
99         addr += size;
100         /*
101          * After the first iteration, guest virtual address is correctly
102          * aligned to PAGE_SIZE.
103          */
104         offset = 0;
105     }
106 
107     return 0;
108 }
109 
raw_copy_to_guest(void * to,const void * from,unsigned int len)110 unsigned long raw_copy_to_guest(void *to, const void *from, unsigned int len)
111 {
112     return copy_guest((void *)from, (vaddr_t)to, len,
113                       GVA_INFO(current), COPY_to_guest | COPY_linear);
114 }
115 
raw_copy_to_guest_flush_dcache(void * to,const void * from,unsigned int len)116 unsigned long raw_copy_to_guest_flush_dcache(void *to, const void *from,
117                                              unsigned int len)
118 {
119     return copy_guest((void *)from, (vaddr_t)to, len, GVA_INFO(current),
120                       COPY_to_guest | COPY_flush_dcache | COPY_linear);
121 }
122 
raw_clear_guest(void * to,unsigned int len)123 unsigned long raw_clear_guest(void *to, unsigned int len)
124 {
125     return copy_guest(NULL, (vaddr_t)to, len, GVA_INFO(current),
126                       COPY_to_guest | COPY_linear);
127 }
128 
raw_copy_from_guest(void * to,const void __user * from,unsigned int len)129 unsigned long raw_copy_from_guest(void *to, const void __user *from,
130                                   unsigned int len)
131 {
132     return copy_guest(to, (vaddr_t)from, len, GVA_INFO(current),
133                       COPY_from_guest | COPY_linear);
134 }
135 
copy_to_guest_phys_flush_dcache(struct domain * d,paddr_t gpa,void * buf,unsigned int len)136 unsigned long copy_to_guest_phys_flush_dcache(struct domain *d,
137                                               paddr_t gpa,
138                                               void *buf,
139                                               unsigned int len)
140 {
141     return copy_guest(buf, gpa, len, GPA_INFO(d),
142                       COPY_to_guest | COPY_ipa | COPY_flush_dcache);
143 }
144 
access_guest_memory_by_gpa(struct domain * d,paddr_t gpa,void * buf,uint32_t size,bool is_write)145 int access_guest_memory_by_gpa(struct domain *d, paddr_t gpa, void *buf,
146                                uint32_t size, bool is_write)
147 {
148     unsigned long left;
149     int flags = COPY_ipa;
150 
151     flags |= is_write ? COPY_to_guest : COPY_from_guest;
152 
153     left = copy_guest(buf, gpa, size, GPA_INFO(d), flags);
154 
155     return (!left) ? 0 : -EINVAL;
156 }
157 
158 /*
159  * Local variables:
160  * mode: C
161  * c-file-style: "BSD"
162  * c-basic-offset: 4
163  * indent-tabs-mode: nil
164  * End:
165  */
166