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