1 | /****************************************************************************** |
---|
2 | * xc_ia64_linux_restore.c |
---|
3 | * |
---|
4 | * Restore the state of a Linux session. |
---|
5 | * |
---|
6 | * Copyright (c) 2003, K A Fraser. |
---|
7 | * Rewritten for ia64 by Tristan Gingold <tristan.gingold@bull.net> |
---|
8 | */ |
---|
9 | |
---|
10 | #include <stdlib.h> |
---|
11 | #include <unistd.h> |
---|
12 | |
---|
13 | #include "xg_private.h" |
---|
14 | |
---|
15 | #define PFN_TO_KB(_pfn) ((_pfn) << (PAGE_SHIFT - 10)) |
---|
16 | |
---|
17 | /* number of pfns this guest has (i.e. number of entries in the P2M) */ |
---|
18 | static unsigned long p2m_size; |
---|
19 | |
---|
20 | /* number of 'in use' pfns in the guest (i.e. #P2M entries with a valid mfn) */ |
---|
21 | static unsigned long nr_pfns; |
---|
22 | |
---|
23 | static ssize_t |
---|
24 | read_exact(int fd, void *buf, size_t count) |
---|
25 | { |
---|
26 | int r = 0, s; |
---|
27 | unsigned char *b = buf; |
---|
28 | |
---|
29 | while (r < count) { |
---|
30 | s = read(fd, &b[r], count - r); |
---|
31 | if ((s == -1) && (errno == EINTR)) |
---|
32 | continue; |
---|
33 | if (s <= 0) { |
---|
34 | break; |
---|
35 | } |
---|
36 | r += s; |
---|
37 | } |
---|
38 | |
---|
39 | return (r == count) ? 1 : 0; |
---|
40 | } |
---|
41 | |
---|
42 | static int |
---|
43 | read_page(int xc_handle, int io_fd, uint32_t dom, unsigned long pfn) |
---|
44 | { |
---|
45 | void *mem; |
---|
46 | |
---|
47 | mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, |
---|
48 | PROT_READ|PROT_WRITE, pfn); |
---|
49 | if (mem == NULL) { |
---|
50 | ERROR("cannot map page"); |
---|
51 | return -1; |
---|
52 | } |
---|
53 | if (!read_exact(io_fd, mem, PAGE_SIZE)) { |
---|
54 | ERROR("Error when reading from state file (5)"); |
---|
55 | return -1; |
---|
56 | } |
---|
57 | munmap(mem, PAGE_SIZE); |
---|
58 | return 0; |
---|
59 | } |
---|
60 | |
---|
61 | int |
---|
62 | xc_domain_restore(int xc_handle, int io_fd, uint32_t dom, |
---|
63 | unsigned int store_evtchn, unsigned long *store_mfn, |
---|
64 | unsigned int console_evtchn, unsigned long *console_mfn, |
---|
65 | unsigned int hvm, unsigned int pae) |
---|
66 | { |
---|
67 | DECLARE_DOMCTL; |
---|
68 | int rc = 1, i; |
---|
69 | unsigned long gmfn; |
---|
70 | unsigned long ver; |
---|
71 | |
---|
72 | /* The new domain's shared-info frame number. */ |
---|
73 | unsigned long shared_info_frame; |
---|
74 | unsigned char shared_info_page[PAGE_SIZE]; /* saved contents from file */ |
---|
75 | shared_info_t *shared_info = (shared_info_t *)shared_info_page; |
---|
76 | |
---|
77 | /* A copy of the CPU context of the guest. */ |
---|
78 | vcpu_guest_context_t ctxt; |
---|
79 | |
---|
80 | unsigned long *page_array = NULL; |
---|
81 | |
---|
82 | /* A temporary mapping of the guest's start_info page. */ |
---|
83 | start_info_t *start_info; |
---|
84 | |
---|
85 | if (hvm) { |
---|
86 | ERROR("HVM Restore is unsupported"); |
---|
87 | goto out; |
---|
88 | } |
---|
89 | |
---|
90 | /* For info only */ |
---|
91 | nr_pfns = 0; |
---|
92 | |
---|
93 | if ( !read_exact(io_fd, &p2m_size, sizeof(unsigned long)) ) |
---|
94 | { |
---|
95 | ERROR("read: p2m_size"); |
---|
96 | goto out; |
---|
97 | } |
---|
98 | DPRINTF("xc_linux_restore start: p2m_size = %lx\n", p2m_size); |
---|
99 | |
---|
100 | if (!read_exact(io_fd, &ver, sizeof(unsigned long))) { |
---|
101 | ERROR("Error when reading version"); |
---|
102 | goto out; |
---|
103 | } |
---|
104 | if (ver != 1) { |
---|
105 | ERROR("version of save doesn't match"); |
---|
106 | goto out; |
---|
107 | } |
---|
108 | |
---|
109 | if (mlock(&ctxt, sizeof(ctxt))) { |
---|
110 | /* needed for build domctl, but might as well do early */ |
---|
111 | ERROR("Unable to mlock ctxt"); |
---|
112 | return 1; |
---|
113 | } |
---|
114 | |
---|
115 | /* Get pages. */ |
---|
116 | page_array = malloc(p2m_size * sizeof(unsigned long)); |
---|
117 | if (page_array == NULL) { |
---|
118 | ERROR("Could not allocate memory"); |
---|
119 | goto out; |
---|
120 | } |
---|
121 | |
---|
122 | for ( i = 0; i < p2m_size; i++ ) |
---|
123 | page_array[i] = i; |
---|
124 | |
---|
125 | if ( xc_domain_memory_populate_physmap(xc_handle, dom, p2m_size, |
---|
126 | 0, 0, page_array) ) |
---|
127 | { |
---|
128 | ERROR("Failed to allocate memory for %ld KB to dom %d.\n", |
---|
129 | PFN_TO_KB(p2m_size), dom); |
---|
130 | goto out; |
---|
131 | } |
---|
132 | DPRINTF("Allocated memory by %ld KB\n", PFN_TO_KB(p2m_size)); |
---|
133 | |
---|
134 | if (!read_exact(io_fd, &domctl.u.arch_setup, sizeof(domctl.u.arch_setup))) { |
---|
135 | ERROR("read: domain setup"); |
---|
136 | goto out; |
---|
137 | } |
---|
138 | |
---|
139 | /* Build firmware (will be overwritten). */ |
---|
140 | domctl.domain = (domid_t)dom; |
---|
141 | domctl.u.arch_setup.flags &= ~XEN_DOMAINSETUP_query; |
---|
142 | domctl.u.arch_setup.bp = ((p2m_size - 3) << PAGE_SHIFT) |
---|
143 | + sizeof (start_info_t); |
---|
144 | domctl.u.arch_setup.maxmem = (p2m_size - 3) << PAGE_SHIFT; |
---|
145 | |
---|
146 | domctl.cmd = XEN_DOMCTL_arch_setup; |
---|
147 | if (xc_domctl(xc_handle, &domctl)) |
---|
148 | goto out; |
---|
149 | |
---|
150 | /* Get the domain's shared-info frame. */ |
---|
151 | domctl.cmd = XEN_DOMCTL_getdomaininfo; |
---|
152 | domctl.domain = (domid_t)dom; |
---|
153 | if (xc_domctl(xc_handle, &domctl) < 0) { |
---|
154 | ERROR("Could not get information on new domain"); |
---|
155 | goto out; |
---|
156 | } |
---|
157 | shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; |
---|
158 | |
---|
159 | DPRINTF("Reloading memory pages: 0%%\n"); |
---|
160 | |
---|
161 | while (1) { |
---|
162 | if (!read_exact(io_fd, &gmfn, sizeof(unsigned long))) { |
---|
163 | ERROR("Error when reading batch size"); |
---|
164 | goto out; |
---|
165 | } |
---|
166 | if (gmfn == INVALID_MFN) |
---|
167 | break; |
---|
168 | |
---|
169 | if (read_page(xc_handle, io_fd, dom, gmfn) < 0) |
---|
170 | goto out; |
---|
171 | } |
---|
172 | |
---|
173 | DPRINTF("Received all pages\n"); |
---|
174 | |
---|
175 | /* Get the list of PFNs that are not in the psuedo-phys map */ |
---|
176 | { |
---|
177 | unsigned int count; |
---|
178 | unsigned long *pfntab; |
---|
179 | int rc; |
---|
180 | |
---|
181 | if (!read_exact(io_fd, &count, sizeof(count))) { |
---|
182 | ERROR("Error when reading pfn count"); |
---|
183 | goto out; |
---|
184 | } |
---|
185 | |
---|
186 | pfntab = malloc(sizeof(unsigned long) * count); |
---|
187 | if (!pfntab) { |
---|
188 | ERROR("Out of memory"); |
---|
189 | goto out; |
---|
190 | } |
---|
191 | |
---|
192 | if (!read_exact(io_fd, pfntab, sizeof(unsigned long)*count)) { |
---|
193 | ERROR("Error when reading pfntab"); |
---|
194 | goto out; |
---|
195 | } |
---|
196 | |
---|
197 | DPRINTF ("Try to free %u pages\n", count); |
---|
198 | |
---|
199 | for (i = 0; i < count; i++) { |
---|
200 | |
---|
201 | volatile unsigned long pfn; |
---|
202 | |
---|
203 | struct xen_memory_reservation reservation = { |
---|
204 | .nr_extents = 1, |
---|
205 | .extent_order = 0, |
---|
206 | .domid = dom |
---|
207 | }; |
---|
208 | set_xen_guest_handle(reservation.extent_start, |
---|
209 | (unsigned long *)&pfn); |
---|
210 | |
---|
211 | pfn = pfntab[i]; |
---|
212 | rc = xc_memory_op(xc_handle, XENMEM_decrease_reservation, |
---|
213 | &reservation); |
---|
214 | if (rc != 1) { |
---|
215 | ERROR("Could not decrease reservation : %d", rc); |
---|
216 | goto out; |
---|
217 | } |
---|
218 | } |
---|
219 | |
---|
220 | DPRINTF("Decreased reservation by %d pages\n", count); |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | if (!read_exact(io_fd, &ctxt, sizeof(ctxt))) { |
---|
225 | ERROR("Error when reading ctxt"); |
---|
226 | goto out; |
---|
227 | } |
---|
228 | |
---|
229 | fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt.user_regs.cr_iip, |
---|
230 | ctxt.user_regs.b0); |
---|
231 | |
---|
232 | /* First to initialize. */ |
---|
233 | domctl.cmd = XEN_DOMCTL_setvcpucontext; |
---|
234 | domctl.domain = (domid_t)dom; |
---|
235 | domctl.u.vcpucontext.vcpu = 0; |
---|
236 | set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt); |
---|
237 | if (xc_domctl(xc_handle, &domctl) != 0) { |
---|
238 | ERROR("Couldn't set vcpu context"); |
---|
239 | goto out; |
---|
240 | } |
---|
241 | |
---|
242 | /* Second to set registers... */ |
---|
243 | ctxt.flags = VGCF_EXTRA_REGS; |
---|
244 | domctl.cmd = XEN_DOMCTL_setvcpucontext; |
---|
245 | domctl.domain = (domid_t)dom; |
---|
246 | domctl.u.vcpucontext.vcpu = 0; |
---|
247 | set_xen_guest_handle(domctl.u.vcpucontext.ctxt, &ctxt); |
---|
248 | if (xc_domctl(xc_handle, &domctl) != 0) { |
---|
249 | ERROR("Couldn't set vcpu context"); |
---|
250 | goto out; |
---|
251 | } |
---|
252 | |
---|
253 | /* Just a check. */ |
---|
254 | if (xc_vcpu_getcontext(xc_handle, dom, 0 /* XXX */, &ctxt)) { |
---|
255 | ERROR("Could not get vcpu context"); |
---|
256 | goto out; |
---|
257 | } |
---|
258 | |
---|
259 | /* Then get privreg page. */ |
---|
260 | if (read_page(xc_handle, io_fd, dom, ctxt.privregs_pfn) < 0) { |
---|
261 | ERROR("Could not read vcpu privregs"); |
---|
262 | goto out; |
---|
263 | } |
---|
264 | |
---|
265 | /* Read shared info. */ |
---|
266 | shared_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, |
---|
267 | PROT_READ|PROT_WRITE, shared_info_frame); |
---|
268 | if (shared_info == NULL) { |
---|
269 | ERROR("cannot map page"); |
---|
270 | goto out; |
---|
271 | } |
---|
272 | if (!read_exact(io_fd, shared_info, PAGE_SIZE)) { |
---|
273 | ERROR("Error when reading shared_info page"); |
---|
274 | goto out; |
---|
275 | } |
---|
276 | |
---|
277 | /* clear any pending events and the selector */ |
---|
278 | memset(&(shared_info->evtchn_pending[0]), 0, |
---|
279 | sizeof (shared_info->evtchn_pending)); |
---|
280 | for (i = 0; i < MAX_VIRT_CPUS; i++) |
---|
281 | shared_info->vcpu_info[i].evtchn_pending_sel = 0; |
---|
282 | |
---|
283 | gmfn = shared_info->arch.start_info_pfn; |
---|
284 | |
---|
285 | munmap (shared_info, PAGE_SIZE); |
---|
286 | |
---|
287 | /* Uncanonicalise the suspend-record frame number and poke resume rec. */ |
---|
288 | start_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, |
---|
289 | PROT_READ | PROT_WRITE, gmfn); |
---|
290 | start_info->nr_pages = p2m_size; |
---|
291 | start_info->shared_info = shared_info_frame << PAGE_SHIFT; |
---|
292 | start_info->flags = 0; |
---|
293 | *store_mfn = start_info->store_mfn; |
---|
294 | start_info->store_evtchn = store_evtchn; |
---|
295 | *console_mfn = start_info->console.domU.mfn; |
---|
296 | start_info->console.domU.evtchn = console_evtchn; |
---|
297 | munmap(start_info, PAGE_SIZE); |
---|
298 | |
---|
299 | /* |
---|
300 | * Safety checking of saved context: |
---|
301 | * 1. user_regs is fine, as Xen checks that on context switch. |
---|
302 | * 2. fpu_ctxt is fine, as it can't hurt Xen. |
---|
303 | * 3. trap_ctxt needs the code selectors checked. |
---|
304 | * 4. ldt base must be page-aligned, no more than 8192 ents, ... |
---|
305 | * 5. gdt already done, and further checking is done by Xen. |
---|
306 | * 6. check that kernel_ss is safe. |
---|
307 | * 7. pt_base is already done. |
---|
308 | * 8. debugregs are checked by Xen. |
---|
309 | * 9. callback code selectors need checking. |
---|
310 | */ |
---|
311 | DPRINTF("Domain ready to be built.\n"); |
---|
312 | |
---|
313 | rc = 0; |
---|
314 | |
---|
315 | out: |
---|
316 | if ((rc != 0) && (dom != 0)) |
---|
317 | xc_domain_destroy(xc_handle, dom); |
---|
318 | |
---|
319 | if (page_array != NULL) |
---|
320 | free(page_array); |
---|
321 | |
---|
322 | DPRINTF("Restore exit with rc=%d\n", rc); |
---|
323 | |
---|
324 | return rc; |
---|
325 | } |
---|