]> git.proxmox.com Git - grub2.git/blob - grub-core/kern/xen/init.c
* grub-core/kern/xen/init.c (grub_xenstore_write_file): Don't add
[grub2.git] / grub-core / kern / xen / init.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2011 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <grub/xen.h>
20 #include <grub/term.h>
21 #include <grub/misc.h>
22 #include <grub/env.h>
23 #include <grub/mm.h>
24 #include <grub/kernel.h>
25 #include <grub/offsets.h>
26 #include <grub/memory.h>
27 #include <grub/i386/tsc.h>
28 #include <grub/term.h>
29 #include <grub/loader.h>
30
31 grub_addr_t grub_modbase;
32 struct start_info *grub_xen_start_page_addr;
33 volatile struct xencons_interface *grub_xen_xcons;
34 volatile struct shared_info *grub_xen_shared_info;
35 volatile struct xenstore_domain_interface *grub_xen_xenstore;
36 volatile grant_entry_v2_t *grub_xen_grant_table;
37 static const grub_size_t total_grants =
38 GRUB_XEN_PAGE_SIZE / sizeof (grub_xen_grant_table[0]);
39 grub_size_t grub_xen_n_allocated_shared_pages;
40
41 static grub_xen_mfn_t
42 grub_xen_ptr2mfn (void *ptr)
43 {
44 grub_xen_mfn_t *mfn_list =
45 (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list;
46 return mfn_list[(grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE];
47 }
48
49 void *
50 grub_xen_alloc_shared_page (domid_t dom, grub_xen_grant_t * grnum)
51 {
52 void *ret;
53 grub_xen_mfn_t mfn;
54 volatile grant_entry_v2_t *entry;
55
56 /* Avoid 0. */
57 for (entry = grub_xen_grant_table;
58 entry < grub_xen_grant_table + total_grants; entry++)
59 if (!entry->hdr.flags)
60 break;
61
62 if (entry == grub_xen_grant_table + total_grants)
63 {
64 grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of grant entries");
65 return NULL;
66 }
67 ret = grub_memalign (GRUB_XEN_PAGE_SIZE, GRUB_XEN_PAGE_SIZE);
68 if (!ret)
69 return NULL;
70 mfn = grub_xen_ptr2mfn (ret);
71 entry->full_page.pad0 = 0;
72 entry->full_page.frame = mfn;
73 entry->full_page.hdr.domid = dom;
74 mb ();
75 entry->full_page.hdr.flags = GTF_permit_access;
76 mb ();
77 *grnum = entry - grub_xen_grant_table;
78 grub_xen_n_allocated_shared_pages++;
79 return ret;
80 }
81
82 void
83 grub_xen_free_shared_page (void *ptr)
84 {
85 grub_xen_mfn_t mfn;
86 volatile grant_entry_v2_t *entry;
87
88 mfn = grub_xen_ptr2mfn (ptr);
89 for (entry = grub_xen_grant_table + 1;
90 entry < grub_xen_grant_table + total_grants; entry++)
91 if (entry->hdr.flags && entry->full_page.frame == mfn)
92 {
93 mb ();
94 entry->hdr.flags = 0;
95 mb ();
96 entry->full_page.frame = 0;
97 mb ();
98 }
99 grub_xen_n_allocated_shared_pages--;
100 }
101
102 void
103 grub_machine_get_bootlocation (char **device __attribute__ ((unused)),
104 char **path __attribute__ ((unused)))
105 {
106 }
107
108 static grub_uint8_t window[GRUB_XEN_PAGE_SIZE]
109 __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE)));
110
111 #ifdef __x86_64__
112 #define NUMBER_OF_LEVELS 4
113 #else
114 #define NUMBER_OF_LEVELS 3
115 #endif
116
117 #define LOG_POINTERS_PER_PAGE 9
118 #define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
119
120 void
121 grub_xen_store_send (const void *buf_, grub_size_t len)
122 {
123 const grub_uint8_t *buf = buf_;
124 struct evtchn_send send;
125 int event_sent = 0;
126 while (len)
127 {
128 grub_size_t avail, inbuf;
129 grub_size_t prod, cons;
130 mb ();
131 prod = grub_xen_xenstore->req_prod;
132 cons = grub_xen_xenstore->req_cons;
133 if (prod >= cons + sizeof (grub_xen_xenstore->req))
134 {
135 if (!event_sent)
136 {
137 send.port = grub_xen_start_page_addr->store_evtchn;
138 grub_xen_event_channel_op (EVTCHNOP_send, &send);
139 event_sent = 1;
140 }
141 grub_xen_sched_op (SCHEDOP_yield, 0);
142 continue;
143 }
144 event_sent = 0;
145 avail = cons + sizeof (grub_xen_xenstore->req) - prod;
146 inbuf = (~prod & (sizeof (grub_xen_xenstore->req) - 1)) + 1;
147 if (avail > inbuf)
148 avail = inbuf;
149 if (avail > len)
150 avail = len;
151 grub_memcpy ((void *) &grub_xen_xenstore->req[prod & (sizeof (grub_xen_xenstore->req) - 1)],
152 buf, avail);
153 buf += avail;
154 len -= avail;
155 mb ();
156 grub_xen_xenstore->req_prod += avail;
157 mb ();
158 if (!event_sent)
159 {
160 send.port = grub_xen_start_page_addr->store_evtchn;
161 grub_xen_event_channel_op (EVTCHNOP_send, &send);
162 event_sent = 1;
163 }
164 grub_xen_sched_op (SCHEDOP_yield, 0);
165 }
166 }
167
168 void
169 grub_xen_store_recv (void *buf_, grub_size_t len)
170 {
171 grub_uint8_t *buf = buf_;
172 struct evtchn_send send;
173 int event_sent = 0;
174 while (len)
175 {
176 grub_size_t avail, inbuf;
177 grub_size_t prod, cons;
178 mb ();
179 prod = grub_xen_xenstore->rsp_prod;
180 cons = grub_xen_xenstore->rsp_cons;
181 if (prod <= cons)
182 {
183 if (!event_sent)
184 {
185 send.port = grub_xen_start_page_addr->store_evtchn;
186 grub_xen_event_channel_op (EVTCHNOP_send, &send);
187 event_sent = 1;
188 }
189 grub_xen_sched_op (SCHEDOP_yield, 0);
190 continue;
191 }
192 event_sent = 0;
193 avail = prod - cons;
194 inbuf = (~cons & (sizeof (grub_xen_xenstore->req) - 1)) + 1;
195 if (avail > inbuf)
196 avail = inbuf;
197 if (avail > len)
198 avail = len;
199 grub_memcpy (buf,
200 (void *) &grub_xen_xenstore->rsp[cons & (sizeof (grub_xen_xenstore->rsp) - 1)],
201 avail);
202 buf += avail;
203 len -= avail;
204 mb ();
205 grub_xen_xenstore->rsp_cons += avail;
206 mb ();
207 if (!event_sent)
208 {
209 send.port = grub_xen_start_page_addr->store_evtchn;
210 grub_xen_event_channel_op(EVTCHNOP_send, &send);
211 event_sent = 1;
212 }
213 grub_xen_sched_op(SCHEDOP_yield, 0);
214 }
215 }
216
217 void *
218 grub_xenstore_get_file (const char *dir, grub_size_t *len)
219 {
220 struct xsd_sockmsg msg;
221 char *buf;
222 grub_size_t dirlen = grub_strlen (dir) + 1;
223
224 if (len)
225 *len = 0;
226
227 grub_memset (&msg, 0, sizeof (msg));
228 msg.type = XS_READ;
229 msg.len = dirlen;
230 grub_xen_store_send (&msg, sizeof (msg));
231 grub_xen_store_send (dir, dirlen);
232 grub_xen_store_recv (&msg, sizeof (msg));
233 buf = grub_malloc (msg.len + 1);
234 if (!buf)
235 return NULL;
236 grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
237 grub_xen_store_recv (buf, msg.len);
238 buf[msg.len] = '\0';
239 if (msg.type == XS_ERROR)
240 {
241 grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", dir, buf);
242 grub_free (buf);
243 return NULL;
244 }
245 if (len)
246 *len = msg.len;
247 return buf;
248 }
249
250 grub_err_t
251 grub_xenstore_write_file (const char *dir, const void *buf, grub_size_t len)
252 {
253 struct xsd_sockmsg msg;
254 grub_size_t dirlen = grub_strlen (dir) + 1;
255 char *resp;
256
257 grub_memset (&msg, 0, sizeof (msg));
258 msg.type = XS_WRITE;
259 msg.len = dirlen + len;
260 grub_xen_store_send (&msg, sizeof (msg));
261 grub_xen_store_send (dir, dirlen);
262 grub_xen_store_send (buf, len);
263 grub_xen_store_recv (&msg, sizeof (msg));
264 resp = grub_malloc (msg.len + 1);
265 if (!resp)
266 return grub_errno;
267 grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
268 grub_xen_store_recv (resp, msg.len);
269 resp[msg.len] = '\0';
270 if (msg.type == XS_ERROR)
271 {
272 grub_dprintf ("xen", "error = %s\n", resp);
273 grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s",
274 dir, resp);
275 grub_free (resp);
276 return grub_errno;
277 }
278 grub_free (resp);
279 return GRUB_ERR_NONE;
280 }
281
282 /* FIXME: error handling. */
283 grub_err_t
284 grub_xenstore_dir (const char *dir,
285 int (*hook) (const char *dir, void *hook_data),
286 void *hook_data)
287 {
288 struct xsd_sockmsg msg;
289 char *buf;
290 char *ptr;
291 grub_size_t dirlen = grub_strlen (dir) + 1;
292
293 grub_memset (&msg, 0, sizeof (msg));
294 msg.type = XS_DIRECTORY;
295 msg.len = dirlen;
296 grub_xen_store_send (&msg, sizeof (msg));
297 grub_xen_store_send (dir, dirlen);
298 grub_xen_store_recv (&msg, sizeof (msg));
299 buf = grub_malloc (msg.len + 1);
300 if (!buf)
301 return grub_errno;
302 grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
303 grub_xen_store_recv (buf, msg.len);
304 buf[msg.len] = '\0';
305 if (msg.type == XS_ERROR)
306 {
307 grub_err_t err;
308 err = grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s",
309 dir, buf);
310 grub_free (buf);
311 return err;
312 }
313 for (ptr = buf; ptr < buf + msg.len; ptr += grub_strlen (ptr) + 1)
314 if (hook (ptr, hook_data))
315 break;
316 grub_free (buf);
317 return grub_errno;
318 }
319
320 unsigned long gntframe = 0;
321
322 #define MAX_N_UNUSABLE_PAGES 4
323
324 static int
325 grub_xen_is_page_usable (grub_xen_mfn_t mfn)
326 {
327 if (mfn == grub_xen_start_page_addr->console.domU.mfn)
328 return 0;
329 if (mfn == grub_xen_start_page_addr->shared_info)
330 return 0;
331 if (mfn == grub_xen_start_page_addr->store_mfn)
332 return 0;
333 if (mfn == gntframe)
334 return 0;
335 return 1;
336 }
337
338 static grub_uint64_t
339 page2offset (grub_uint64_t page)
340 {
341 return page << 12;
342 }
343
344 static void
345 map_all_pages (void)
346 {
347 grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages;
348 grub_uint64_t i, j;
349 grub_xen_mfn_t *mfn_list =
350 (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list;
351 grub_uint64_t *pg = (grub_uint64_t *) window;
352 grub_uint64_t oldpgstart, oldpgend;
353 struct gnttab_setup_table gnttab_setup;
354 struct gnttab_set_version gnttab_setver;
355 grub_size_t n_unusable_pages = 0;
356 struct mmu_update m2p_updates[2 * MAX_N_UNUSABLE_PAGES];
357
358 grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver));
359
360 gnttab_setver.version = 2;
361 grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1);
362
363 grub_memset (&gnttab_setup, 0, sizeof (gnttab_setup));
364 gnttab_setup.dom = DOMID_SELF;
365 gnttab_setup.nr_frames = 1;
366 gnttab_setup.frame_list.p = &gntframe;
367
368 grub_xen_grant_table_op (GNTTABOP_setup_table, &gnttab_setup, 1);
369
370 for (j = 0; j < total_pages - n_unusable_pages; j++)
371 while (!grub_xen_is_page_usable (mfn_list[j]))
372 {
373 grub_xen_mfn_t t;
374 if (n_unusable_pages >= MAX_N_UNUSABLE_PAGES)
375 {
376 struct sched_shutdown arg;
377 arg.reason = SHUTDOWN_crash;
378 grub_xen_sched_op (SCHEDOP_shutdown, &arg);
379 while (1);
380 }
381 t = mfn_list[j];
382 mfn_list[j] = mfn_list[total_pages - n_unusable_pages - 1];
383 mfn_list[total_pages - n_unusable_pages - 1] = t;
384
385 m2p_updates[2 * n_unusable_pages].ptr
386 = page2offset (mfn_list[j]) | MMU_MACHPHYS_UPDATE;
387 m2p_updates[2 * n_unusable_pages].val = j;
388 m2p_updates[2 * n_unusable_pages + 1].ptr
389 = page2offset (mfn_list[total_pages - n_unusable_pages - 1])
390 | MMU_MACHPHYS_UPDATE;
391 m2p_updates[2 * n_unusable_pages + 1].val = total_pages
392 - n_unusable_pages - 1;
393
394 n_unusable_pages++;
395 }
396
397 grub_xen_mmu_update (m2p_updates, 2 * n_unusable_pages, NULL, DOMID_SELF);
398
399 total_pages += 4;
400
401 grub_uint64_t lx[NUMBER_OF_LEVELS], nlx;
402 grub_uint64_t paging_start = total_pages - 4 - n_unusable_pages, curpage;
403
404 for (nlx = total_pages, i = 0; i < (unsigned) NUMBER_OF_LEVELS; i++)
405 {
406 nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE;
407 /* PAE wants all 4 root directories present. */
408 #ifdef __i386__
409 if (i == 1)
410 nlx = 4;
411 #endif
412 lx[i] = nlx;
413 paging_start -= nlx;
414 }
415
416 oldpgstart = grub_xen_start_page_addr->pt_base >> 12;
417 oldpgend = oldpgstart + grub_xen_start_page_addr->nr_pt_frames;
418
419 curpage = paging_start;
420
421 int l;
422
423 for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--)
424 {
425 for (i = 0; i < lx[l]; i++)
426 {
427 grub_xen_update_va_mapping (&window,
428 page2offset (mfn_list[curpage + i]) | 7,
429 UVMF_INVLPG);
430 grub_memset (&window, 0, sizeof (window));
431
432 for (j = i * POINTERS_PER_PAGE;
433 j < (i + 1) * POINTERS_PER_PAGE && j < lx[l - 1]; j++)
434 pg[j - i * POINTERS_PER_PAGE] =
435 page2offset (mfn_list[curpage + lx[l] + j])
436 #ifdef __x86_64__
437 | 4
438 #endif
439 | 3;
440 }
441 curpage += lx[l];
442 }
443
444 for (i = 0; i < lx[0]; i++)
445 {
446 grub_xen_update_va_mapping (&window,
447 page2offset (mfn_list[curpage + i]) | 7,
448 UVMF_INVLPG);
449 grub_memset (&window, 0, sizeof (window));
450
451 for (j = i * POINTERS_PER_PAGE;
452 j < (i + 1) * POINTERS_PER_PAGE && j < total_pages; j++)
453 if (j < paging_start && !(j >= oldpgstart && j < oldpgend))
454 pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 0x7;
455 else if (j < grub_xen_start_page_addr->nr_pages)
456 pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 5;
457 else if (j == grub_xen_start_page_addr->nr_pages)
458 {
459 pg[j - i * POINTERS_PER_PAGE] =
460 page2offset (grub_xen_start_page_addr->console.domU.mfn) | 7;
461 grub_xen_xcons = (void *) (grub_addr_t) page2offset (j);
462 }
463 else if (j == grub_xen_start_page_addr->nr_pages + 1)
464 {
465 pg[j - i * POINTERS_PER_PAGE] =
466 grub_xen_start_page_addr->shared_info | 7;
467 grub_xen_shared_info = (void *) (grub_addr_t) page2offset (j);
468 }
469 else if (j == grub_xen_start_page_addr->nr_pages + 2)
470 {
471 pg[j - i * POINTERS_PER_PAGE] =
472 page2offset (grub_xen_start_page_addr->store_mfn) | 7;
473 grub_xen_xenstore = (void *) (grub_addr_t) page2offset (j);
474 }
475 else if (j == grub_xen_start_page_addr->nr_pages + 3)
476 {
477 pg[j - i * POINTERS_PER_PAGE] = page2offset (gntframe) | 7;
478 grub_xen_grant_table = (void *) (grub_addr_t) page2offset (j);
479 }
480 }
481
482 grub_xen_update_va_mapping (&window, 0, UVMF_INVLPG);
483
484 mmuext_op_t op[3];
485
486 op[0].cmd = MMUEXT_PIN_L1_TABLE + (NUMBER_OF_LEVELS - 1);
487 op[0].arg1.mfn = mfn_list[paging_start];
488 op[1].cmd = MMUEXT_NEW_BASEPTR;
489 op[1].arg1.mfn = mfn_list[paging_start];
490 op[2].cmd = MMUEXT_UNPIN_TABLE;
491 op[2].arg1.mfn = mfn_list[oldpgstart];
492
493 grub_xen_mmuext_op (op, 3, NULL, DOMID_SELF);
494
495 for (i = oldpgstart; i < oldpgend; i++)
496 grub_xen_update_va_mapping ((void *) (grub_addr_t) page2offset (i),
497 page2offset (mfn_list[i]) | 7, UVMF_INVLPG);
498 void *new_start_page, *new_mfn_list;
499 new_start_page = (void *) (grub_addr_t) page2offset (paging_start - 1);
500 grub_memcpy (new_start_page, grub_xen_start_page_addr, 4096);
501 grub_xen_start_page_addr = new_start_page;
502 new_mfn_list = (void *) (grub_addr_t)
503 page2offset (paging_start - 1
504 - ((grub_xen_start_page_addr->nr_pages
505 * sizeof (grub_uint64_t) + 4095) / 4096));
506 grub_memcpy (new_mfn_list, mfn_list, grub_xen_start_page_addr->nr_pages
507 * sizeof (grub_uint64_t));
508 grub_xen_start_page_addr->pt_base = page2offset (paging_start);
509 grub_xen_start_page_addr->mfn_list = (grub_addr_t) new_mfn_list;
510
511 grub_addr_t heap_start = grub_modules_get_end ();
512 grub_addr_t heap_end = (grub_addr_t) new_mfn_list;
513
514 grub_mm_init_region ((void *) heap_start, heap_end - heap_start);
515 }
516
517 extern char _end[];
518
519 void
520 grub_machine_init (void)
521 {
522 #ifdef __i386__
523 grub_xen_vm_assist (VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3);
524 #endif
525
526 grub_modbase = ALIGN_UP ((grub_addr_t) _end
527 + GRUB_KERNEL_MACHINE_MOD_GAP,
528 GRUB_KERNEL_MACHINE_MOD_ALIGN);
529
530 map_all_pages ();
531
532 grub_console_init ();
533
534 grub_tsc_init ();
535
536 grub_xendisk_init ();
537
538 grub_boot_init ();
539 }
540
541 void
542 grub_exit (void)
543 {
544 struct sched_shutdown arg;
545
546 arg.reason = SHUTDOWN_poweroff;
547 grub_xen_sched_op (SCHEDOP_shutdown, &arg);
548 while (1);
549 }
550
551 void
552 grub_machine_fini (int flags __attribute__ ((unused)))
553 {
554 grub_xendisk_fini ();
555 grub_boot_fini ();
556 }
557
558 grub_err_t
559 grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
560 {
561 grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages;
562 grub_uint64_t usable_pages = grub_xen_start_page_addr->pt_base >> 12;
563 if (hook (0, page2offset (usable_pages), GRUB_MEMORY_AVAILABLE, hook_data))
564 return GRUB_ERR_NONE;
565
566 hook (page2offset (usable_pages), page2offset (total_pages - usable_pages),
567 GRUB_MEMORY_RESERVED, hook_data);
568
569 return GRUB_ERR_NONE;
570 }