1 /*
2 * arch/x86/pv/descriptor-tables.c
3 *
4 * Descriptor table manipulation code for PV guests
5 *
6 * Copyright (c) 2002-2005 K A Fraser
7 * Copyright (c) 2004 Christian Limpach
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms and conditions of the GNU General Public
11 * License, version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <xen/guest_access.h>
23 #include <xen/hypercall.h>
24
25 #include <asm/p2m.h>
26 #include <asm/pv/mm.h>
27
28 /* Override macros from asm/page.h to make them work with mfn_t */
29 #undef mfn_to_page
30 #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
31 #undef page_to_mfn
32 #define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
33
34 /*******************
35 * Descriptor Tables
36 */
37
pv_destroy_gdt(struct vcpu * v)38 void pv_destroy_gdt(struct vcpu *v)
39 {
40 l1_pgentry_t *pl1e;
41 unsigned int i;
42 unsigned long pfn, zero_pfn = PFN_DOWN(__pa(zero_page));
43
44 v->arch.pv_vcpu.gdt_ents = 0;
45 pl1e = pv_gdt_ptes(v);
46 for ( i = 0; i < FIRST_RESERVED_GDT_PAGE; i++ )
47 {
48 pfn = l1e_get_pfn(pl1e[i]);
49 if ( (l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) && pfn != zero_pfn )
50 put_page_and_type(mfn_to_page(_mfn(pfn)));
51 l1e_write(&pl1e[i], l1e_from_pfn(zero_pfn, __PAGE_HYPERVISOR_RO));
52 v->arch.pv_vcpu.gdt_frames[i] = 0;
53 }
54 }
55
pv_set_gdt(struct vcpu * v,unsigned long * frames,unsigned int entries)56 long pv_set_gdt(struct vcpu *v, unsigned long *frames, unsigned int entries)
57 {
58 struct domain *d = v->domain;
59 l1_pgentry_t *pl1e;
60 /* NB. There are 512 8-byte entries per GDT page. */
61 unsigned int i, nr_pages = (entries + 511) / 512;
62
63 if ( entries > FIRST_RESERVED_GDT_ENTRY )
64 return -EINVAL;
65
66 /* Check the pages in the new GDT. */
67 for ( i = 0; i < nr_pages; i++ )
68 {
69 struct page_info *page;
70
71 page = get_page_from_gfn(d, frames[i], NULL, P2M_ALLOC);
72 if ( !page )
73 goto fail;
74 if ( !get_page_type(page, PGT_seg_desc_page) )
75 {
76 put_page(page);
77 goto fail;
78 }
79 frames[i] = mfn_x(page_to_mfn(page));
80 }
81
82 /* Tear down the old GDT. */
83 pv_destroy_gdt(v);
84
85 /* Install the new GDT. */
86 v->arch.pv_vcpu.gdt_ents = entries;
87 pl1e = pv_gdt_ptes(v);
88 for ( i = 0; i < nr_pages; i++ )
89 {
90 v->arch.pv_vcpu.gdt_frames[i] = frames[i];
91 l1e_write(&pl1e[i], l1e_from_pfn(frames[i], __PAGE_HYPERVISOR_RW));
92 }
93
94 return 0;
95
96 fail:
97 while ( i-- > 0 )
98 {
99 put_page_and_type(mfn_to_page(_mfn(frames[i])));
100 }
101 return -EINVAL;
102 }
103
do_set_gdt(XEN_GUEST_HANDLE_PARAM (xen_ulong_t)frame_list,unsigned int entries)104 long do_set_gdt(XEN_GUEST_HANDLE_PARAM(xen_ulong_t) frame_list,
105 unsigned int entries)
106 {
107 int nr_pages = (entries + 511) / 512;
108 unsigned long frames[16];
109 struct vcpu *curr = current;
110 long ret;
111
112 /* Rechecked in set_gdt, but ensures a sane limit for copy_from_user(). */
113 if ( entries > FIRST_RESERVED_GDT_ENTRY )
114 return -EINVAL;
115
116 if ( copy_from_guest(frames, frame_list, nr_pages) )
117 return -EFAULT;
118
119 domain_lock(curr->domain);
120
121 if ( (ret = pv_set_gdt(curr, frames, entries)) == 0 )
122 flush_tlb_local();
123
124 domain_unlock(curr->domain);
125
126 return ret;
127 }
128
do_update_descriptor(uint64_t pa,uint64_t desc)129 long do_update_descriptor(uint64_t pa, uint64_t desc)
130 {
131 struct domain *currd = current->domain;
132 unsigned long gmfn = pa >> PAGE_SHIFT;
133 unsigned long mfn;
134 unsigned int offset;
135 struct desc_struct *gdt_pent, d;
136 struct page_info *page;
137 long ret = -EINVAL;
138
139 offset = ((unsigned int)pa & ~PAGE_MASK) / sizeof(struct desc_struct);
140
141 *(uint64_t *)&d = desc;
142
143 page = get_page_from_gfn(currd, gmfn, NULL, P2M_ALLOC);
144 if ( (((unsigned int)pa % sizeof(struct desc_struct)) != 0) ||
145 !page ||
146 !check_descriptor(currd, &d) )
147 {
148 if ( page )
149 put_page(page);
150 return -EINVAL;
151 }
152 mfn = mfn_x(page_to_mfn(page));
153
154 /* Check if the given frame is in use in an unsafe context. */
155 switch ( page->u.inuse.type_info & PGT_type_mask )
156 {
157 case PGT_seg_desc_page:
158 if ( unlikely(!get_page_type(page, PGT_seg_desc_page)) )
159 goto out;
160 break;
161 default:
162 if ( unlikely(!get_page_type(page, PGT_writable_page)) )
163 goto out;
164 break;
165 }
166
167 paging_mark_dirty(currd, _mfn(mfn));
168
169 /* All is good so make the update. */
170 gdt_pent = map_domain_page(_mfn(mfn));
171 write_atomic((uint64_t *)&gdt_pent[offset], *(uint64_t *)&d);
172 unmap_domain_page(gdt_pent);
173
174 put_page_type(page);
175
176 ret = 0; /* success */
177
178 out:
179 put_page(page);
180
181 return ret;
182 }
183
compat_set_gdt(XEN_GUEST_HANDLE_PARAM (uint)frame_list,unsigned int entries)184 int compat_set_gdt(XEN_GUEST_HANDLE_PARAM(uint) frame_list, unsigned int entries)
185 {
186 unsigned int i, nr_pages = (entries + 511) / 512;
187 unsigned long frames[16];
188 int ret;
189
190 /* Rechecked in set_gdt, but ensures a sane limit for copy_from_user(). */
191 if ( entries > FIRST_RESERVED_GDT_ENTRY )
192 return -EINVAL;
193
194 if ( !guest_handle_okay(frame_list, nr_pages) )
195 return -EFAULT;
196
197 for ( i = 0; i < nr_pages; ++i )
198 {
199 unsigned int frame;
200
201 if ( __copy_from_guest(&frame, frame_list, 1) )
202 return -EFAULT;
203 frames[i] = frame;
204 guest_handle_add_offset(frame_list, 1);
205 }
206
207 domain_lock(current->domain);
208
209 if ( (ret = pv_set_gdt(current, frames, entries)) == 0 )
210 flush_tlb_local();
211
212 domain_unlock(current->domain);
213
214 return ret;
215 }
216
compat_update_descriptor(uint32_t pa_lo,uint32_t pa_hi,uint32_t desc_lo,uint32_t desc_hi)217 int compat_update_descriptor(uint32_t pa_lo, uint32_t pa_hi,
218 uint32_t desc_lo, uint32_t desc_hi)
219 {
220 return do_update_descriptor(pa_lo | ((uint64_t)pa_hi << 32),
221 desc_lo | ((uint64_t)desc_hi << 32));
222 }
223
224 /*
225 * Local variables:
226 * mode: C
227 * c-file-style: "BSD"
228 * c-basic-offset: 4
229 * tab-width: 4
230 * indent-tabs-mode: nil
231 * End:
232 */
233