/root/src/xen/xen/arch/x86/pv/domain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * arch/x86/pv/domain.c |
3 | | * |
4 | | * PV domain handling |
5 | | */ |
6 | | |
7 | | #include <xen/domain_page.h> |
8 | | #include <xen/errno.h> |
9 | | #include <xen/lib.h> |
10 | | #include <xen/sched.h> |
11 | | |
12 | | #include <asm/pv/domain.h> |
13 | | |
14 | | /* Override macros from asm/page.h to make them work with mfn_t */ |
15 | | #undef mfn_to_page |
16 | | #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn)) |
17 | | #undef page_to_mfn |
18 | 0 | #define page_to_mfn(pg) _mfn(__page_to_mfn(pg)) |
19 | | |
20 | | static void noreturn continue_nonidle_domain(struct vcpu *v) |
21 | 0 | { |
22 | 0 | check_wakeup_from_wait(); |
23 | 0 | mark_regs_dirty(guest_cpu_user_regs()); |
24 | 0 | reset_stack_and_jump(ret_from_intr); |
25 | 0 | } |
26 | | |
27 | | static int setup_compat_l4(struct vcpu *v) |
28 | 0 | { |
29 | 0 | struct page_info *pg; |
30 | 0 | l4_pgentry_t *l4tab; |
31 | 0 | mfn_t mfn; |
32 | 0 |
|
33 | 0 | pg = alloc_domheap_page(v->domain, MEMF_no_owner); |
34 | 0 | if ( pg == NULL ) |
35 | 0 | return -ENOMEM; |
36 | 0 |
|
37 | 0 | mfn = page_to_mfn(pg); |
38 | 0 | l4tab = map_domain_page(mfn); |
39 | 0 | clear_page(l4tab); |
40 | 0 | init_xen_l4_slots(l4tab, mfn, v->domain, INVALID_MFN, false); |
41 | 0 | unmap_domain_page(l4tab); |
42 | 0 |
|
43 | 0 | /* This page needs to look like a pagetable so that it can be shadowed */ |
44 | 0 | pg->u.inuse.type_info = PGT_l4_page_table | PGT_validated | 1; |
45 | 0 |
|
46 | 0 | v->arch.guest_table = pagetable_from_page(pg); |
47 | 0 | v->arch.guest_table_user = v->arch.guest_table; |
48 | 0 |
|
49 | 0 | return 0; |
50 | 0 | } |
51 | | |
52 | | static void release_compat_l4(struct vcpu *v) |
53 | 0 | { |
54 | 0 | if ( !pagetable_is_null(v->arch.guest_table) ) |
55 | 0 | free_domheap_page(pagetable_get_page(v->arch.guest_table)); |
56 | 0 | v->arch.guest_table = pagetable_null(); |
57 | 0 | v->arch.guest_table_user = pagetable_null(); |
58 | 0 | } |
59 | | |
60 | | int switch_compat(struct domain *d) |
61 | 0 | { |
62 | 0 | struct vcpu *v; |
63 | 0 | int rc; |
64 | 0 |
|
65 | 0 | if ( is_hvm_domain(d) || d->tot_pages != 0 ) |
66 | 0 | return -EACCES; |
67 | 0 | if ( is_pv_32bit_domain(d) ) |
68 | 0 | return 0; |
69 | 0 |
|
70 | 0 | d->arch.has_32bit_shinfo = 1; |
71 | 0 | d->arch.is_32bit_pv = 1; |
72 | 0 |
|
73 | 0 | for_each_vcpu( d, v ) |
74 | 0 | { |
75 | 0 | if ( (rc = setup_compat_arg_xlat(v)) || |
76 | 0 | (rc = setup_compat_l4(v)) ) |
77 | 0 | goto undo_and_fail; |
78 | 0 | } |
79 | 0 |
|
80 | 0 | domain_set_alloc_bitsize(d); |
81 | 0 | recalculate_cpuid_policy(d); |
82 | 0 |
|
83 | 0 | d->arch.x87_fip_width = 4; |
84 | 0 |
|
85 | 0 | return 0; |
86 | 0 |
|
87 | 0 | undo_and_fail: |
88 | 0 | d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0; |
89 | 0 | for_each_vcpu( d, v ) |
90 | 0 | { |
91 | 0 | free_compat_arg_xlat(v); |
92 | 0 | release_compat_l4(v); |
93 | 0 | } |
94 | 0 |
|
95 | 0 | return rc; |
96 | 0 | } |
97 | | |
98 | | static int pv_create_gdt_ldt_l1tab(struct vcpu *v) |
99 | 0 | { |
100 | 0 | return create_perdomain_mapping(v->domain, GDT_VIRT_START(v), |
101 | 0 | 1U << GDT_LDT_VCPU_SHIFT, |
102 | 0 | v->domain->arch.pv_domain.gdt_ldt_l1tab, |
103 | 0 | NULL); |
104 | 0 | } |
105 | | |
106 | | static void pv_destroy_gdt_ldt_l1tab(struct vcpu *v) |
107 | 0 | { |
108 | 0 | destroy_perdomain_mapping(v->domain, GDT_VIRT_START(v), |
109 | 0 | 1U << GDT_LDT_VCPU_SHIFT); |
110 | 0 | } |
111 | | |
112 | | void pv_vcpu_destroy(struct vcpu *v) |
113 | 0 | { |
114 | 0 | if ( is_pv_32bit_vcpu(v) ) |
115 | 0 | { |
116 | 0 | free_compat_arg_xlat(v); |
117 | 0 | release_compat_l4(v); |
118 | 0 | } |
119 | 0 |
|
120 | 0 | pv_destroy_gdt_ldt_l1tab(v); |
121 | 0 | xfree(v->arch.pv_vcpu.trap_ctxt); |
122 | 0 | v->arch.pv_vcpu.trap_ctxt = NULL; |
123 | 0 | } |
124 | | |
125 | | int pv_vcpu_initialise(struct vcpu *v) |
126 | 0 | { |
127 | 0 | struct domain *d = v->domain; |
128 | 0 | int rc; |
129 | 0 |
|
130 | 0 | ASSERT(!is_idle_domain(d)); |
131 | 0 |
|
132 | 0 | spin_lock_init(&v->arch.pv_vcpu.shadow_ldt_lock); |
133 | 0 |
|
134 | 0 | rc = pv_create_gdt_ldt_l1tab(v); |
135 | 0 | if ( rc ) |
136 | 0 | return rc; |
137 | 0 |
|
138 | 0 | BUILD_BUG_ON(NR_VECTORS * sizeof(*v->arch.pv_vcpu.trap_ctxt) > |
139 | 0 | PAGE_SIZE); |
140 | 0 | v->arch.pv_vcpu.trap_ctxt = xzalloc_array(struct trap_info, |
141 | 0 | NR_VECTORS); |
142 | 0 | if ( !v->arch.pv_vcpu.trap_ctxt ) |
143 | 0 | { |
144 | 0 | rc = -ENOMEM; |
145 | 0 | goto done; |
146 | 0 | } |
147 | 0 |
|
148 | 0 | /* PV guests by default have a 100Hz ticker. */ |
149 | 0 | v->periodic_period = MILLISECS(10); |
150 | 0 |
|
151 | 0 | v->arch.pv_vcpu.ctrlreg[4] = real_cr4_to_pv_guest_cr4(mmu_cr4_features); |
152 | 0 |
|
153 | 0 | if ( is_pv_32bit_domain(d) ) |
154 | 0 | { |
155 | 0 | if ( (rc = setup_compat_arg_xlat(v)) ) |
156 | 0 | goto done; |
157 | 0 |
|
158 | 0 | if ( (rc = setup_compat_l4(v)) ) |
159 | 0 | goto done; |
160 | 0 | } |
161 | 0 |
|
162 | 0 | done: |
163 | 0 | if ( rc ) |
164 | 0 | pv_vcpu_destroy(v); |
165 | 0 | return rc; |
166 | 0 | } |
167 | | |
168 | | void pv_domain_destroy(struct domain *d) |
169 | 0 | { |
170 | 0 | destroy_perdomain_mapping(d, GDT_LDT_VIRT_START, |
171 | 0 | GDT_LDT_MBYTES << (20 - PAGE_SHIFT)); |
172 | 0 |
|
173 | 0 | xfree(d->arch.pv_domain.cpuidmasks); |
174 | 0 | d->arch.pv_domain.cpuidmasks = NULL; |
175 | 0 |
|
176 | 0 | free_xenheap_page(d->arch.pv_domain.gdt_ldt_l1tab); |
177 | 0 | d->arch.pv_domain.gdt_ldt_l1tab = NULL; |
178 | 0 | } |
179 | | |
180 | | |
181 | | int pv_domain_initialise(struct domain *d, unsigned int domcr_flags, |
182 | | struct xen_arch_domainconfig *config) |
183 | 0 | { |
184 | 0 | static const struct arch_csw pv_csw = { |
185 | 0 | .from = paravirt_ctxt_switch_from, |
186 | 0 | .to = paravirt_ctxt_switch_to, |
187 | 0 | .tail = continue_nonidle_domain, |
188 | 0 | }; |
189 | 0 | int rc = -ENOMEM; |
190 | 0 |
|
191 | 0 | d->arch.pv_domain.gdt_ldt_l1tab = |
192 | 0 | alloc_xenheap_pages(0, MEMF_node(domain_to_node(d))); |
193 | 0 | if ( !d->arch.pv_domain.gdt_ldt_l1tab ) |
194 | 0 | goto fail; |
195 | 0 | clear_page(d->arch.pv_domain.gdt_ldt_l1tab); |
196 | 0 |
|
197 | 0 | if ( levelling_caps & ~LCAP_faulting ) |
198 | 0 | { |
199 | 0 | d->arch.pv_domain.cpuidmasks = xmalloc(struct cpuidmasks); |
200 | 0 | if ( !d->arch.pv_domain.cpuidmasks ) |
201 | 0 | goto fail; |
202 | 0 | *d->arch.pv_domain.cpuidmasks = cpuidmask_defaults; |
203 | 0 | } |
204 | 0 |
|
205 | 0 | rc = create_perdomain_mapping(d, GDT_LDT_VIRT_START, |
206 | 0 | GDT_LDT_MBYTES << (20 - PAGE_SHIFT), |
207 | 0 | NULL, NULL); |
208 | 0 | if ( rc ) |
209 | 0 | goto fail; |
210 | 0 |
|
211 | 0 | d->arch.ctxt_switch = &pv_csw; |
212 | 0 |
|
213 | 0 | /* 64-bit PV guest by default. */ |
214 | 0 | d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0; |
215 | 0 |
|
216 | 0 | return 0; |
217 | 0 |
|
218 | 0 | fail: |
219 | 0 | pv_domain_destroy(d); |
220 | 0 |
|
221 | 0 | return rc; |
222 | 0 | } |
223 | | |
224 | | void toggle_guest_mode(struct vcpu *v) |
225 | 0 | { |
226 | 0 | if ( is_pv_32bit_vcpu(v) ) |
227 | 0 | return; |
228 | 0 |
|
229 | 0 | if ( cpu_has_fsgsbase ) |
230 | 0 | { |
231 | 0 | if ( v->arch.flags & TF_kernel_mode ) |
232 | 0 | v->arch.pv_vcpu.gs_base_kernel = __rdgsbase(); |
233 | 0 | else |
234 | 0 | v->arch.pv_vcpu.gs_base_user = __rdgsbase(); |
235 | 0 | } |
236 | 0 | v->arch.flags ^= TF_kernel_mode; |
237 | 0 | asm volatile ( "swapgs" ); |
238 | 0 | update_cr3(v); |
239 | 0 | /* Don't flush user global mappings from the TLB. Don't tick TLB clock. */ |
240 | 0 | asm volatile ( "mov %0, %%cr3" : : "r" (v->arch.cr3) : "memory" ); |
241 | 0 |
|
242 | 0 | if ( !(v->arch.flags & TF_kernel_mode) ) |
243 | 0 | return; |
244 | 0 |
|
245 | 0 | if ( v->arch.pv_vcpu.need_update_runstate_area && |
246 | 0 | update_runstate_area(v) ) |
247 | 0 | v->arch.pv_vcpu.need_update_runstate_area = 0; |
248 | 0 |
|
249 | 0 | if ( v->arch.pv_vcpu.pending_system_time.version && |
250 | 0 | update_secondary_system_time(v, |
251 | 0 | &v->arch.pv_vcpu.pending_system_time) ) |
252 | 0 | v->arch.pv_vcpu.pending_system_time.version = 0; |
253 | 0 | } |
254 | | |
255 | | /* |
256 | | * Local variables: |
257 | | * mode: C |
258 | | * c-file-style: "BSD" |
259 | | * c-basic-offset: 4 |
260 | | * tab-width: 4 |
261 | | * indent-tabs-mode: nil |
262 | | * End: |
263 | | */ |