2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2011 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/term.h>
21 #include <grub/misc.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>
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
;
42 grub_xen_ptr2mfn (void *ptr
)
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
];
50 grub_xen_alloc_shared_page (domid_t dom
, grub_xen_grant_t
* grnum
)
54 volatile grant_entry_v2_t
*entry
;
57 for (entry
= grub_xen_grant_table
;
58 entry
< grub_xen_grant_table
+ total_grants
; entry
++)
59 if (!entry
->hdr
.flags
)
62 if (entry
== grub_xen_grant_table
+ total_grants
)
64 grub_error (GRUB_ERR_OUT_OF_MEMORY
, "out of grant entries");
67 ret
= grub_memalign (GRUB_XEN_PAGE_SIZE
, GRUB_XEN_PAGE_SIZE
);
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
;
75 entry
->full_page
.hdr
.flags
= GTF_permit_access
;
77 *grnum
= entry
- grub_xen_grant_table
;
78 grub_xen_n_allocated_shared_pages
++;
83 grub_xen_free_shared_page (void *ptr
)
86 volatile grant_entry_v2_t
*entry
;
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
)
96 entry
->full_page
.frame
= 0;
99 grub_xen_n_allocated_shared_pages
--;
103 grub_machine_get_bootlocation (char **device
__attribute__ ((unused
)),
104 char **path
__attribute__ ((unused
)))
108 static grub_uint8_t window
[GRUB_XEN_PAGE_SIZE
]
109 __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE
)));
112 #define NUMBER_OF_LEVELS 4
114 #define NUMBER_OF_LEVELS 3
117 #define LOG_POINTERS_PER_PAGE 9
118 #define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
121 grub_xen_store_send (const void *buf_
, grub_size_t len
)
123 const grub_uint8_t
*buf
= buf_
;
124 struct evtchn_send send
;
128 grub_size_t avail
, inbuf
;
129 grub_size_t prod
, cons
;
131 prod
= grub_xen_xenstore
->req_prod
;
132 cons
= grub_xen_xenstore
->req_cons
;
133 if (prod
>= cons
+ sizeof (grub_xen_xenstore
->req
))
137 send
.port
= grub_xen_start_page_addr
->store_evtchn
;
138 grub_xen_event_channel_op (EVTCHNOP_send
, &send
);
141 grub_xen_sched_op (SCHEDOP_yield
, 0);
145 avail
= cons
+ sizeof (grub_xen_xenstore
->req
) - prod
;
146 inbuf
= (~prod
& (sizeof (grub_xen_xenstore
->req
) - 1)) + 1;
151 grub_memcpy ((void *) &grub_xen_xenstore
->req
[prod
& (sizeof (grub_xen_xenstore
->req
) - 1)],
156 grub_xen_xenstore
->req_prod
+= avail
;
160 send
.port
= grub_xen_start_page_addr
->store_evtchn
;
161 grub_xen_event_channel_op (EVTCHNOP_send
, &send
);
164 grub_xen_sched_op (SCHEDOP_yield
, 0);
169 grub_xen_store_recv (void *buf_
, grub_size_t len
)
171 grub_uint8_t
*buf
= buf_
;
172 struct evtchn_send send
;
176 grub_size_t avail
, inbuf
;
177 grub_size_t prod
, cons
;
179 prod
= grub_xen_xenstore
->rsp_prod
;
180 cons
= grub_xen_xenstore
->rsp_cons
;
185 send
.port
= grub_xen_start_page_addr
->store_evtchn
;
186 grub_xen_event_channel_op (EVTCHNOP_send
, &send
);
189 grub_xen_sched_op (SCHEDOP_yield
, 0);
194 inbuf
= (~cons
& (sizeof (grub_xen_xenstore
->req
) - 1)) + 1;
200 (void *) &grub_xen_xenstore
->rsp
[cons
& (sizeof (grub_xen_xenstore
->rsp
) - 1)],
205 grub_xen_xenstore
->rsp_cons
+= avail
;
209 send
.port
= grub_xen_start_page_addr
->store_evtchn
;
210 grub_xen_event_channel_op(EVTCHNOP_send
, &send
);
213 grub_xen_sched_op(SCHEDOP_yield
, 0);
218 grub_xenstore_get_file (const char *dir
, grub_size_t
*len
)
220 struct xsd_sockmsg msg
;
222 grub_size_t dirlen
= grub_strlen (dir
) + 1;
227 grub_memset (&msg
, 0, sizeof (msg
));
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);
236 grub_dprintf ("xen", "msg type = %d, len = %d\n", msg
.type
, msg
.len
);
237 grub_xen_store_recv (buf
, msg
.len
);
239 if (msg
.type
== XS_ERROR
)
241 grub_error (GRUB_ERR_IO
, "couldn't read xenstorage `%s': %s", dir
, buf
);
251 grub_xenstore_write_file (const char *dir
, const void *buf
, grub_size_t len
)
253 struct xsd_sockmsg msg
;
254 grub_size_t dirlen
= grub_strlen (dir
) + 1;
257 grub_memset (&msg
, 0, sizeof (msg
));
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);
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
)
272 grub_dprintf ("xen", "error = %s\n", resp
);
273 grub_error (GRUB_ERR_IO
, "couldn't read xenstorage `%s': %s",
279 return GRUB_ERR_NONE
;
282 /* FIXME: error handling. */
284 grub_xenstore_dir (const char *dir
,
285 int (*hook
) (const char *dir
, void *hook_data
),
288 struct xsd_sockmsg msg
;
291 grub_size_t dirlen
= grub_strlen (dir
) + 1;
293 grub_memset (&msg
, 0, sizeof (msg
));
294 msg
.type
= XS_DIRECTORY
;
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);
302 grub_dprintf ("xen", "msg type = %d, len = %d\n", msg
.type
, msg
.len
);
303 grub_xen_store_recv (buf
, msg
.len
);
305 if (msg
.type
== XS_ERROR
)
308 err
= grub_error (GRUB_ERR_IO
, "couldn't read xenstorage `%s': %s",
313 for (ptr
= buf
; ptr
< buf
+ msg
.len
; ptr
+= grub_strlen (ptr
) + 1)
314 if (hook (ptr
, hook_data
))
320 unsigned long gntframe
= 0;
322 #define MAX_N_UNUSABLE_PAGES 4
325 grub_xen_is_page_usable (grub_xen_mfn_t mfn
)
327 if (mfn
== grub_xen_start_page_addr
->console
.domU
.mfn
)
329 if (mfn
== grub_xen_start_page_addr
->shared_info
)
331 if (mfn
== grub_xen_start_page_addr
->store_mfn
)
339 page2offset (grub_uint64_t page
)
347 grub_uint64_t total_pages
= grub_xen_start_page_addr
->nr_pages
;
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
];
358 grub_memset (&gnttab_setver
, 0, sizeof (gnttab_setver
));
360 gnttab_setver
.version
= 2;
361 grub_xen_grant_table_op (GNTTABOP_set_version
, &gnttab_setver
, 1);
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
;
368 grub_xen_grant_table_op (GNTTABOP_setup_table
, &gnttab_setup
, 1);
370 for (j
= 0; j
< total_pages
- n_unusable_pages
; j
++)
371 while (!grub_xen_is_page_usable (mfn_list
[j
]))
374 if (n_unusable_pages
>= MAX_N_UNUSABLE_PAGES
)
376 struct sched_shutdown arg
;
377 arg
.reason
= SHUTDOWN_crash
;
378 grub_xen_sched_op (SCHEDOP_shutdown
, &arg
);
382 mfn_list
[j
] = mfn_list
[total_pages
- n_unusable_pages
- 1];
383 mfn_list
[total_pages
- n_unusable_pages
- 1] = t
;
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;
397 grub_xen_mmu_update (m2p_updates
, 2 * n_unusable_pages
, NULL
, DOMID_SELF
);
401 grub_uint64_t lx
[NUMBER_OF_LEVELS
], nlx
;
402 grub_uint64_t paging_start
= total_pages
- 4 - n_unusable_pages
, curpage
;
404 for (nlx
= total_pages
, i
= 0; i
< (unsigned) NUMBER_OF_LEVELS
; i
++)
406 nlx
= (nlx
+ POINTERS_PER_PAGE
- 1) >> LOG_POINTERS_PER_PAGE
;
407 /* PAE wants all 4 root directories present. */
416 oldpgstart
= grub_xen_start_page_addr
->pt_base
>> 12;
417 oldpgend
= oldpgstart
+ grub_xen_start_page_addr
->nr_pt_frames
;
419 curpage
= paging_start
;
423 for (l
= NUMBER_OF_LEVELS
- 1; l
>= 1; l
--)
425 for (i
= 0; i
< lx
[l
]; i
++)
427 grub_xen_update_va_mapping (&window
,
428 page2offset (mfn_list
[curpage
+ i
]) | 7,
430 grub_memset (&window
, 0, sizeof (window
));
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
])
444 for (i
= 0; i
< lx
[0]; i
++)
446 grub_xen_update_va_mapping (&window
,
447 page2offset (mfn_list
[curpage
+ i
]) | 7,
449 grub_memset (&window
, 0, sizeof (window
));
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
)
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
);
463 else if (j
== grub_xen_start_page_addr
->nr_pages
+ 1)
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
);
469 else if (j
== grub_xen_start_page_addr
->nr_pages
+ 2)
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
);
475 else if (j
== grub_xen_start_page_addr
->nr_pages
+ 3)
477 pg
[j
- i
* POINTERS_PER_PAGE
] = page2offset (gntframe
) | 7;
478 grub_xen_grant_table
= (void *) (grub_addr_t
) page2offset (j
);
482 grub_xen_update_va_mapping (&window
, 0, UVMF_INVLPG
);
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
];
493 grub_xen_mmuext_op (op
, 3, NULL
, DOMID_SELF
);
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
;
511 grub_addr_t heap_start
= grub_modules_get_end ();
512 grub_addr_t heap_end
= (grub_addr_t
) new_mfn_list
;
514 grub_mm_init_region ((void *) heap_start
, heap_end
- heap_start
);
520 grub_machine_init (void)
523 grub_xen_vm_assist (VMASST_CMD_enable
, VMASST_TYPE_pae_extended_cr3
);
526 grub_modbase
= ALIGN_UP ((grub_addr_t
) _end
527 + GRUB_KERNEL_MACHINE_MOD_GAP
,
528 GRUB_KERNEL_MACHINE_MOD_ALIGN
);
532 grub_console_init ();
536 grub_xendisk_init ();
544 struct sched_shutdown arg
;
546 arg
.reason
= SHUTDOWN_poweroff
;
547 grub_xen_sched_op (SCHEDOP_shutdown
, &arg
);
552 grub_machine_fini (int flags
__attribute__ ((unused
)))
554 grub_xendisk_fini ();
559 grub_machine_mmap_iterate (grub_memory_hook_t hook
, void *hook_data
)
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
;
566 hook (page2offset (usable_pages
), page2offset (total_pages
- usable_pages
),
567 GRUB_MEMORY_RESERVED
, hook_data
);
569 return GRUB_ERR_NONE
;