xen.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2013 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. #include <grub/loader.h>
  19. #include <grub/memory.h>
  20. #include <grub/normal.h>
  21. #include <grub/file.h>
  22. #include <grub/disk.h>
  23. #include <grub/err.h>
  24. #include <grub/misc.h>
  25. #include <grub/types.h>
  26. #include <grub/dl.h>
  27. #include <grub/mm.h>
  28. #include <grub/term.h>
  29. #include <grub/cpu/linux.h>
  30. #include <grub/video.h>
  31. #include <grub/video_fb.h>
  32. #include <grub/command.h>
  33. #include <grub/xen/relocator.h>
  34. #include <grub/i18n.h>
  35. #include <grub/elf.h>
  36. #include <grub/elfload.h>
  37. #include <grub/lib/cmdline.h>
  38. #include <grub/xen.h>
  39. #include <grub/xen_file.h>
  40. #include <grub/linux.h>
  41. #include <grub/i386/memory.h>
  42. #include <grub/verify.h>
  43. #include <grub/safemath.h>
  44. GRUB_MOD_LICENSE ("GPLv3+");
  45. #ifdef __x86_64__
  46. #define NUMBER_OF_LEVELS 4
  47. #define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER)
  48. #define VIRT_MASK 0x0000ffffffffffffULL
  49. #else
  50. #define NUMBER_OF_LEVELS 3
  51. #define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW)
  52. #define VIRT_MASK 0x00000000ffffffffULL
  53. #define HYPERVISOR_PUD_ADDRESS 0xc0000000ULL
  54. #endif
  55. struct grub_xen_mapping_lvl {
  56. grub_uint64_t virt_start;
  57. grub_uint64_t virt_end;
  58. grub_uint64_t pfn_start;
  59. grub_uint64_t n_pt_pages;
  60. };
  61. struct grub_xen_mapping {
  62. grub_uint64_t *where;
  63. struct grub_xen_mapping_lvl area;
  64. struct grub_xen_mapping_lvl lvls[NUMBER_OF_LEVELS];
  65. };
  66. struct xen_loader_state {
  67. struct grub_relocator *relocator;
  68. struct grub_relocator_xen_state state;
  69. struct start_info next_start;
  70. struct grub_xen_file_info xen_inf;
  71. grub_xen_mfn_t *virt_mfn_list;
  72. struct start_info *virt_start_info;
  73. grub_xen_mfn_t console_pfn;
  74. grub_uint64_t max_addr;
  75. grub_uint64_t pgtbl_end;
  76. struct xen_multiboot_mod_list *module_info_page;
  77. grub_uint64_t modules_target_start;
  78. grub_size_t n_modules;
  79. struct grub_xen_mapping *map_reloc;
  80. struct grub_xen_mapping mappings[XEN_MAX_MAPPINGS];
  81. int n_mappings;
  82. int loaded;
  83. };
  84. static struct xen_loader_state xen_state;
  85. static grub_dl_t my_mod;
  86. #define MAX_MODULES (GRUB_PAGE_SIZE / sizeof (struct xen_multiboot_mod_list))
  87. #define STACK_SIZE 1048576
  88. #define ADDITIONAL_SIZE (1 << 19)
  89. #define ALIGN_SIZE (1 << 22)
  90. #define LOG_POINTERS_PER_PAGE 9
  91. #define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
  92. static grub_uint64_t
  93. page2offset (grub_uint64_t page)
  94. {
  95. return page << GRUB_PAGE_SHIFT;
  96. }
  97. static grub_err_t
  98. get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn)
  99. {
  100. struct grub_xen_mapping *map, *map_cmp;
  101. grub_uint64_t mask, bits;
  102. int i, m;
  103. if (xen_state.n_mappings == XEN_MAX_MAPPINGS)
  104. return grub_error (GRUB_ERR_BUG, "too many mapped areas");
  105. grub_dprintf ("xen", "get_pgtable_size %d from=%llx, to=%llx, pfn=%llx\n",
  106. xen_state.n_mappings, (unsigned long long) from,
  107. (unsigned long long) to, (unsigned long long) pfn);
  108. map = xen_state.mappings + xen_state.n_mappings;
  109. grub_memset (map, 0, sizeof (*map));
  110. map->area.virt_start = from & VIRT_MASK;
  111. map->area.virt_end = (to - 1) & VIRT_MASK;
  112. map->area.n_pt_pages = 0;
  113. for (i = NUMBER_OF_LEVELS - 1; i >= 0; i--)
  114. {
  115. map->lvls[i].pfn_start = pfn + map->area.n_pt_pages;
  116. if (i == NUMBER_OF_LEVELS - 1)
  117. {
  118. if (xen_state.n_mappings == 0)
  119. {
  120. map->lvls[i].virt_start = 0;
  121. map->lvls[i].virt_end = VIRT_MASK;
  122. map->lvls[i].n_pt_pages = 1;
  123. map->area.n_pt_pages++;
  124. }
  125. continue;
  126. }
  127. bits = GRUB_PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE;
  128. mask = (1ULL << bits) - 1;
  129. map->lvls[i].virt_start = map->area.virt_start & ~mask;
  130. map->lvls[i].virt_end = map->area.virt_end | mask;
  131. #ifdef __i386__
  132. /* PAE wants last root directory present. */
  133. if (i == 1 && to <= HYPERVISOR_PUD_ADDRESS && xen_state.n_mappings == 0)
  134. map->lvls[i].virt_end = VIRT_MASK;
  135. #endif
  136. for (m = 0; m < xen_state.n_mappings; m++)
  137. {
  138. map_cmp = xen_state.mappings + m;
  139. if (map_cmp->lvls[i].virt_start == map_cmp->lvls[i].virt_end)
  140. continue;
  141. if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
  142. map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
  143. {
  144. map->lvls[i].virt_start = 0;
  145. map->lvls[i].virt_end = 0;
  146. break;
  147. }
  148. if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
  149. map->lvls[i].virt_start <= map_cmp->lvls[i].virt_end)
  150. map->lvls[i].virt_start = map_cmp->lvls[i].virt_end + 1;
  151. if (map->lvls[i].virt_end >= map_cmp->lvls[i].virt_start &&
  152. map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
  153. map->lvls[i].virt_end = map_cmp->lvls[i].virt_start - 1;
  154. }
  155. if (map->lvls[i].virt_start < map->lvls[i].virt_end)
  156. map->lvls[i].n_pt_pages =
  157. ((map->lvls[i].virt_end - map->lvls[i].virt_start) >> bits) + 1;
  158. map->area.n_pt_pages += map->lvls[i].n_pt_pages;
  159. grub_dprintf ("xen", "get_pgtable_size level %d: virt %llx-%llx %d pts\n",
  160. i, (unsigned long long) map->lvls[i].virt_start,
  161. (unsigned long long) map->lvls[i].virt_end,
  162. (int) map->lvls[i].n_pt_pages);
  163. }
  164. grub_dprintf ("xen", "get_pgtable_size return: %d page tables\n",
  165. (int) map->area.n_pt_pages);
  166. xen_state.state.paging_start[xen_state.n_mappings] = pfn;
  167. xen_state.state.paging_size[xen_state.n_mappings] = map->area.n_pt_pages;
  168. return GRUB_ERR_NONE;
  169. }
  170. static grub_uint64_t *
  171. get_pg_table_virt (int mapping, int level)
  172. {
  173. grub_uint64_t pfn;
  174. struct grub_xen_mapping *map;
  175. map = xen_state.mappings + mapping;
  176. pfn = map->lvls[level].pfn_start - map->lvls[NUMBER_OF_LEVELS - 1].pfn_start;
  177. return map->where + pfn * POINTERS_PER_PAGE;
  178. }
  179. static grub_uint64_t
  180. get_pg_table_prot (int level, grub_uint64_t pfn)
  181. {
  182. int m;
  183. grub_uint64_t pfn_s, pfn_e;
  184. if (level > 0)
  185. return INTERMEDIATE_OR;
  186. for (m = 0; m < xen_state.n_mappings; m++)
  187. {
  188. pfn_s = xen_state.mappings[m].lvls[NUMBER_OF_LEVELS - 1].pfn_start;
  189. pfn_e = xen_state.mappings[m].area.n_pt_pages + pfn_s;
  190. if (pfn >= pfn_s && pfn < pfn_e)
  191. return GRUB_PAGE_PRESENT | GRUB_PAGE_USER;
  192. }
  193. return GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER;
  194. }
  195. static void
  196. generate_page_table (grub_xen_mfn_t *mfn_list)
  197. {
  198. int l, m1, m2;
  199. long p, p_s, p_e;
  200. grub_uint64_t start, end, pfn;
  201. grub_uint64_t *pg;
  202. struct grub_xen_mapping_lvl *lvl;
  203. for (m1 = 0; m1 < xen_state.n_mappings; m1++)
  204. grub_memset (xen_state.mappings[m1].where, 0,
  205. xen_state.mappings[m1].area.n_pt_pages * GRUB_PAGE_SIZE);
  206. for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--)
  207. {
  208. for (m1 = 0; m1 < xen_state.n_mappings; m1++)
  209. {
  210. start = xen_state.mappings[m1].lvls[l].virt_start;
  211. end = xen_state.mappings[m1].lvls[l].virt_end;
  212. pg = get_pg_table_virt(m1, l);
  213. for (m2 = 0; m2 < xen_state.n_mappings; m2++)
  214. {
  215. lvl = (l > 0) ? xen_state.mappings[m2].lvls + l - 1
  216. : &xen_state.mappings[m2].area;
  217. if (l > 0 && lvl->n_pt_pages == 0)
  218. continue;
  219. if (lvl->virt_start >= end || lvl->virt_end <= start)
  220. continue;
  221. p_s = (grub_max (start, lvl->virt_start) - start) >>
  222. (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
  223. p_e = (grub_min (end, lvl->virt_end) - start) >>
  224. (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
  225. pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >>
  226. (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start;
  227. grub_dprintf ("xen", "write page table entries level %d pg %p "
  228. "mapping %d/%d index %lx-%lx pfn %llx\n",
  229. l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn);
  230. for (p = p_s; p <= p_e; p++)
  231. {
  232. pg[p] = page2offset (mfn_list[pfn]) |
  233. get_pg_table_prot (l, pfn);
  234. pfn++;
  235. }
  236. }
  237. }
  238. }
  239. }
  240. static grub_err_t
  241. set_mfns (grub_xen_mfn_t pfn)
  242. {
  243. grub_xen_mfn_t i, t;
  244. grub_xen_mfn_t cn_pfn = -1, st_pfn = -1;
  245. struct mmu_update m2p_updates[4];
  246. for (i = 0; i < grub_xen_start_page_addr->nr_pages; i++)
  247. {
  248. if (xen_state.virt_mfn_list[i] ==
  249. grub_xen_start_page_addr->console.domU.mfn)
  250. cn_pfn = i;
  251. if (xen_state.virt_mfn_list[i] == grub_xen_start_page_addr->store_mfn)
  252. st_pfn = i;
  253. }
  254. if (cn_pfn == (grub_xen_mfn_t)-1)
  255. return grub_error (GRUB_ERR_BUG, "no console");
  256. if (st_pfn == (grub_xen_mfn_t)-1)
  257. return grub_error (GRUB_ERR_BUG, "no store");
  258. t = xen_state.virt_mfn_list[pfn];
  259. xen_state.virt_mfn_list[pfn] = xen_state.virt_mfn_list[cn_pfn];
  260. xen_state.virt_mfn_list[cn_pfn] = t;
  261. t = xen_state.virt_mfn_list[pfn + 1];
  262. xen_state.virt_mfn_list[pfn + 1] = xen_state.virt_mfn_list[st_pfn];
  263. xen_state.virt_mfn_list[st_pfn] = t;
  264. m2p_updates[0].ptr =
  265. page2offset (xen_state.virt_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE;
  266. m2p_updates[0].val = pfn;
  267. m2p_updates[1].ptr =
  268. page2offset (xen_state.virt_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE;
  269. m2p_updates[1].val = pfn + 1;
  270. m2p_updates[2].ptr =
  271. page2offset (xen_state.virt_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE;
  272. m2p_updates[2].val = cn_pfn;
  273. m2p_updates[3].ptr =
  274. page2offset (xen_state.virt_mfn_list[st_pfn]) | MMU_MACHPHYS_UPDATE;
  275. m2p_updates[3].val = st_pfn;
  276. grub_xen_mmu_update (m2p_updates, 4, NULL, DOMID_SELF);
  277. return GRUB_ERR_NONE;
  278. }
  279. static grub_err_t
  280. grub_xen_p2m_alloc (void)
  281. {
  282. grub_relocator_chunk_t ch;
  283. grub_size_t p2msize, p2malloc;
  284. grub_err_t err;
  285. struct grub_xen_mapping *map;
  286. if (xen_state.virt_mfn_list)
  287. return GRUB_ERR_NONE;
  288. map = xen_state.mappings + xen_state.n_mappings;
  289. p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) *
  290. grub_xen_start_page_addr->nr_pages, GRUB_PAGE_SIZE);
  291. if (xen_state.xen_inf.has_p2m_base)
  292. {
  293. err = get_pgtable_size (xen_state.xen_inf.p2m_base,
  294. xen_state.xen_inf.p2m_base + p2msize,
  295. (xen_state.max_addr + p2msize) >> GRUB_PAGE_SHIFT);
  296. if (err)
  297. return err;
  298. map->area.pfn_start = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  299. p2malloc = p2msize + page2offset (map->area.n_pt_pages);
  300. xen_state.n_mappings++;
  301. xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base;
  302. xen_state.next_start.first_p2m_pfn = map->area.pfn_start;
  303. xen_state.next_start.nr_p2m_frames = p2malloc >> GRUB_PAGE_SHIFT;
  304. }
  305. else
  306. {
  307. xen_state.next_start.mfn_list =
  308. xen_state.max_addr + xen_state.xen_inf.virt_base;
  309. p2malloc = p2msize;
  310. }
  311. xen_state.state.mfn_list = xen_state.max_addr;
  312. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  313. xen_state.max_addr, p2malloc);
  314. if (err)
  315. return err;
  316. xen_state.virt_mfn_list = get_virtual_current_address (ch);
  317. if (xen_state.xen_inf.has_p2m_base)
  318. map->where = (grub_uint64_t *) xen_state.virt_mfn_list +
  319. p2msize / sizeof (grub_uint64_t);
  320. grub_memcpy (xen_state.virt_mfn_list,
  321. (void *) grub_xen_start_page_addr->mfn_list, p2msize);
  322. xen_state.max_addr += p2malloc;
  323. return GRUB_ERR_NONE;
  324. }
  325. static grub_err_t
  326. grub_xen_special_alloc (void)
  327. {
  328. grub_relocator_chunk_t ch;
  329. grub_err_t err;
  330. if (xen_state.virt_start_info)
  331. return GRUB_ERR_NONE;
  332. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  333. xen_state.max_addr,
  334. sizeof (xen_state.next_start));
  335. if (err)
  336. return err;
  337. xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base;
  338. xen_state.virt_start_info = get_virtual_current_address (ch);
  339. xen_state.max_addr =
  340. ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), GRUB_PAGE_SIZE);
  341. xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  342. xen_state.max_addr += 2 * GRUB_PAGE_SIZE;
  343. xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages;
  344. grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic,
  345. sizeof (xen_state.next_start.magic));
  346. xen_state.next_start.store_mfn = grub_xen_start_page_addr->store_mfn;
  347. xen_state.next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn;
  348. xen_state.next_start.console.domU = grub_xen_start_page_addr->console.domU;
  349. xen_state.next_start.shared_info = grub_xen_start_page_addr->shared_info;
  350. return GRUB_ERR_NONE;
  351. }
  352. static grub_err_t
  353. grub_xen_pt_alloc (void)
  354. {
  355. grub_relocator_chunk_t ch;
  356. grub_err_t err;
  357. grub_uint64_t nr_info_pages;
  358. grub_uint64_t nr_need_pages;
  359. grub_uint64_t try_virt_end;
  360. struct grub_xen_mapping *map;
  361. if (xen_state.pgtbl_end)
  362. return GRUB_ERR_NONE;
  363. map = xen_state.mappings + xen_state.n_mappings;
  364. xen_state.map_reloc = map + 1;
  365. xen_state.next_start.pt_base =
  366. xen_state.max_addr + xen_state.xen_inf.virt_base;
  367. nr_info_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  368. nr_need_pages = nr_info_pages;
  369. while (1)
  370. {
  371. try_virt_end = ALIGN_UP (xen_state.xen_inf.virt_base +
  372. page2offset (nr_need_pages) +
  373. ADDITIONAL_SIZE + STACK_SIZE, ALIGN_SIZE);
  374. err = get_pgtable_size (xen_state.xen_inf.virt_base, try_virt_end,
  375. nr_info_pages);
  376. if (err)
  377. return err;
  378. xen_state.n_mappings++;
  379. /* Map the relocator page either at virtual 0 or after end of area. */
  380. nr_need_pages = nr_info_pages + map->area.n_pt_pages;
  381. if (xen_state.xen_inf.virt_base)
  382. err = get_pgtable_size (0, GRUB_PAGE_SIZE, nr_need_pages);
  383. else
  384. err = get_pgtable_size (try_virt_end, try_virt_end + GRUB_PAGE_SIZE,
  385. nr_need_pages);
  386. if (err)
  387. return err;
  388. nr_need_pages += xen_state.map_reloc->area.n_pt_pages;
  389. if (xen_state.xen_inf.virt_base + page2offset (nr_need_pages) <=
  390. try_virt_end)
  391. break;
  392. xen_state.n_mappings--;
  393. }
  394. xen_state.n_mappings++;
  395. nr_need_pages = map->area.n_pt_pages + xen_state.map_reloc->area.n_pt_pages;
  396. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  397. xen_state.max_addr,
  398. page2offset (nr_need_pages));
  399. if (err)
  400. return err;
  401. map->where = get_virtual_current_address (ch);
  402. map->area.pfn_start = 0;
  403. xen_state.max_addr += page2offset (nr_need_pages);
  404. xen_state.state.stack =
  405. xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base;
  406. xen_state.next_start.nr_pt_frames = nr_need_pages;
  407. xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base;
  408. xen_state.pgtbl_end = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  409. xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where +
  410. page2offset (map->area.n_pt_pages));
  411. return GRUB_ERR_NONE;
  412. }
  413. /* Allocate all not yet allocated areas mapped by initial page tables. */
  414. static grub_err_t
  415. grub_xen_alloc_boot_data (void)
  416. {
  417. grub_err_t err;
  418. if (!xen_state.xen_inf.has_p2m_base)
  419. {
  420. err = grub_xen_p2m_alloc ();
  421. if (err)
  422. return err;
  423. }
  424. err = grub_xen_special_alloc ();
  425. if (err)
  426. return err;
  427. err = grub_xen_pt_alloc ();
  428. if (err)
  429. return err;
  430. return GRUB_ERR_NONE;
  431. }
  432. static grub_err_t
  433. grub_xen_boot (void)
  434. {
  435. grub_err_t err;
  436. grub_uint64_t nr_pages;
  437. struct gnttab_set_version gnttab_setver;
  438. grub_size_t i;
  439. if (grub_xen_n_allocated_shared_pages)
  440. return grub_error (GRUB_ERR_BUG, "active grants");
  441. err = grub_xen_alloc_boot_data ();
  442. if (err)
  443. return err;
  444. if (xen_state.xen_inf.has_p2m_base)
  445. {
  446. err = grub_xen_p2m_alloc ();
  447. if (err)
  448. return err;
  449. }
  450. err = set_mfns (xen_state.console_pfn);
  451. if (err)
  452. return err;
  453. nr_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  454. grub_dprintf ("xen", "bootstrap domain %llx+%llx\n",
  455. (unsigned long long) xen_state.xen_inf.virt_base,
  456. (unsigned long long) page2offset (nr_pages));
  457. xen_state.map_reloc->area.pfn_start = nr_pages;
  458. generate_page_table (xen_state.virt_mfn_list);
  459. xen_state.state.entry_point = xen_state.xen_inf.entry_point;
  460. *xen_state.virt_start_info = xen_state.next_start;
  461. grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver));
  462. gnttab_setver.version = 1;
  463. grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1);
  464. for (i = 0; i < ARRAY_SIZE (grub_xen_shared_info->evtchn_pending); i++)
  465. grub_xen_shared_info->evtchn_pending[i] = 0;
  466. return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages,
  467. xen_state.xen_inf.virt_base <
  468. GRUB_PAGE_SIZE ? page2offset (nr_pages) : 0,
  469. xen_state.pgtbl_end - 1,
  470. page2offset (xen_state.pgtbl_end - 1) +
  471. xen_state.xen_inf.virt_base);
  472. }
  473. static void
  474. grub_xen_reset (void)
  475. {
  476. grub_relocator_unload (xen_state.relocator);
  477. grub_memset (&xen_state, 0, sizeof (xen_state));
  478. }
  479. static grub_err_t
  480. grub_xen_unload (void)
  481. {
  482. grub_xen_reset ();
  483. grub_dl_unref (my_mod);
  484. return GRUB_ERR_NONE;
  485. }
  486. #define HYPERCALL_INTERFACE_SIZE 32
  487. #ifdef __x86_64__
  488. static grub_uint8_t template[] =
  489. {
  490. 0x51, /* push %rcx */
  491. 0x41, 0x53, /* push %r11 */
  492. 0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */
  493. 0x0f, 0x05, /* syscall */
  494. 0x41, 0x5b, /* pop %r11 */
  495. 0x59, /* pop %rcx */
  496. 0xc3 /* ret */
  497. };
  498. static grub_uint8_t template_iret[] =
  499. {
  500. 0x51, /* push %rcx */
  501. 0x41, 0x53, /* push %r11 */
  502. 0x50, /* push %rax */
  503. 0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */
  504. 0x0f, 0x05 /* syscall */
  505. };
  506. #define CALLNO_OFFSET 6
  507. #else
  508. static grub_uint8_t template[] =
  509. {
  510. 0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */
  511. 0xcd, 0x82, /* int $0x82 */
  512. 0xc3 /* ret */
  513. };
  514. static grub_uint8_t template_iret[] =
  515. {
  516. 0x50, /* push %eax */
  517. 0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */
  518. 0xcd, 0x82, /* int $0x82 */
  519. };
  520. #define CALLNO_OFFSET 1
  521. #endif
  522. static void
  523. set_hypercall_interface (grub_uint8_t *tgt, unsigned callno)
  524. {
  525. if (callno == 0x17)
  526. {
  527. grub_memcpy (tgt, template_iret, ARRAY_SIZE (template_iret));
  528. grub_memset (tgt + ARRAY_SIZE (template_iret), 0xcc,
  529. HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template_iret));
  530. return;
  531. }
  532. grub_memcpy (tgt, template, ARRAY_SIZE (template));
  533. grub_memset (tgt + ARRAY_SIZE (template), 0xcc,
  534. HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template));
  535. tgt[CALLNO_OFFSET] = callno & 0xff;
  536. tgt[CALLNO_OFFSET + 1] = callno >> 8;
  537. }
  538. #ifdef __x86_64__
  539. #define grub_elfXX_load grub_elf64_load
  540. #else
  541. #define grub_elfXX_load grub_elf32_load
  542. #endif
  543. static grub_err_t
  544. grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
  545. int argc, char *argv[])
  546. {
  547. grub_file_t file;
  548. grub_elf_t elf;
  549. grub_err_t err;
  550. void *kern_chunk_src;
  551. grub_relocator_chunk_t ch;
  552. grub_addr_t kern_start;
  553. grub_addr_t kern_end;
  554. grub_size_t sz;
  555. if (argc == 0)
  556. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  557. /* Call grub_loader_unset early to avoid it being called by grub_loader_set */
  558. grub_loader_unset ();
  559. grub_xen_reset ();
  560. err = grub_create_loader_cmdline (argc - 1, argv + 1,
  561. (char *) xen_state.next_start.cmd_line,
  562. sizeof (xen_state.next_start.cmd_line) - 1,
  563. GRUB_VERIFY_KERNEL_CMDLINE);
  564. if (err)
  565. return err;
  566. file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
  567. if (!file)
  568. return grub_errno;
  569. elf = grub_xen_file (file);
  570. if (!elf)
  571. goto fail;
  572. err = grub_xen_get_info (elf, &xen_state.xen_inf);
  573. if (err)
  574. goto fail;
  575. #ifdef __x86_64__
  576. if (xen_state.xen_inf.arch != GRUB_XEN_FILE_X86_64)
  577. #else
  578. if (xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE
  579. && xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
  580. #endif
  581. {
  582. grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d",
  583. xen_state.xen_inf.arch);
  584. goto fail;
  585. }
  586. if (xen_state.xen_inf.virt_base & (GRUB_PAGE_SIZE - 1))
  587. {
  588. grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base");
  589. goto fail;
  590. }
  591. grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n",
  592. (unsigned long long) xen_state.xen_inf.virt_base,
  593. (unsigned long long) xen_state.xen_inf.entry_point);
  594. xen_state.relocator = grub_relocator_new ();
  595. if (!xen_state.relocator)
  596. goto fail;
  597. kern_start = xen_state.xen_inf.kern_start - xen_state.xen_inf.paddr_offset;
  598. kern_end = xen_state.xen_inf.kern_end - xen_state.xen_inf.paddr_offset;
  599. if (xen_state.xen_inf.has_hypercall_page)
  600. {
  601. grub_dprintf ("xen", "hypercall page at 0x%llx\n",
  602. (unsigned long long) xen_state.xen_inf.hypercall_page);
  603. kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page -
  604. xen_state.xen_inf.virt_base);
  605. kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page -
  606. xen_state.xen_inf.virt_base + GRUB_PAGE_SIZE);
  607. }
  608. xen_state.max_addr = ALIGN_UP (kern_end, GRUB_PAGE_SIZE);
  609. if (grub_sub (kern_end, kern_start, &sz))
  610. {
  611. err = GRUB_ERR_OUT_OF_RANGE;
  612. goto fail;
  613. }
  614. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start, sz);
  615. if (err)
  616. goto fail;
  617. kern_chunk_src = get_virtual_current_address (ch);
  618. grub_dprintf ("xen", "paddr_offset = 0x%llx\n",
  619. (unsigned long long) xen_state.xen_inf.paddr_offset);
  620. grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n",
  621. (unsigned long long) xen_state.xen_inf.kern_start,
  622. (unsigned long long) xen_state.xen_inf.kern_end);
  623. err = grub_elfXX_load (elf, argv[0],
  624. (grub_uint8_t *) kern_chunk_src - kern_start
  625. - xen_state.xen_inf.paddr_offset, 0, 0, 0);
  626. if (xen_state.xen_inf.has_hypercall_page)
  627. {
  628. unsigned i;
  629. for (i = 0; i < GRUB_PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++)
  630. set_hypercall_interface ((grub_uint8_t *) kern_chunk_src +
  631. i * HYPERCALL_INTERFACE_SIZE +
  632. xen_state.xen_inf.hypercall_page -
  633. xen_state.xen_inf.virt_base - kern_start, i);
  634. }
  635. if (err)
  636. goto fail;
  637. grub_dl_ref (my_mod);
  638. xen_state.loaded = 1;
  639. grub_loader_set (grub_xen_boot, grub_xen_unload, 0);
  640. goto fail;
  641. fail:
  642. /* grub_errno might be clobbered by further calls, save the error reason. */
  643. err = grub_errno;
  644. if (elf)
  645. grub_elf_close (elf);
  646. else if (file)
  647. grub_file_close (file);
  648. if (err != GRUB_ERR_NONE)
  649. grub_xen_reset ();
  650. return err;
  651. }
  652. static grub_err_t
  653. grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
  654. int argc, char *argv[])
  655. {
  656. grub_size_t size = 0;
  657. grub_err_t err;
  658. struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
  659. grub_relocator_chunk_t ch;
  660. if (argc == 0)
  661. {
  662. grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  663. goto fail;
  664. }
  665. if (!xen_state.loaded)
  666. {
  667. grub_error (GRUB_ERR_BAD_ARGUMENT,
  668. N_("you need to load the kernel first"));
  669. goto fail;
  670. }
  671. if (xen_state.next_start.mod_start || xen_state.next_start.mod_len)
  672. {
  673. grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
  674. goto fail;
  675. }
  676. if (xen_state.xen_inf.unmapped_initrd)
  677. {
  678. err = grub_xen_alloc_boot_data ();
  679. if (err)
  680. goto fail;
  681. }
  682. if (grub_initrd_init (argc, argv, &initrd_ctx))
  683. goto fail;
  684. size = grub_get_initrd_size (&initrd_ctx);
  685. if (size)
  686. {
  687. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  688. xen_state.max_addr, size);
  689. if (err)
  690. goto fail;
  691. if (grub_initrd_load (&initrd_ctx, get_virtual_current_address (ch)))
  692. goto fail;
  693. }
  694. xen_state.next_start.mod_len = size;
  695. if (xen_state.xen_inf.unmapped_initrd)
  696. {
  697. xen_state.next_start.flags |= SIF_MOD_START_PFN;
  698. xen_state.next_start.mod_start = xen_state.max_addr >> GRUB_PAGE_SHIFT;
  699. }
  700. else
  701. xen_state.next_start.mod_start =
  702. xen_state.max_addr + xen_state.xen_inf.virt_base;
  703. grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n",
  704. (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base),
  705. (unsigned) size);
  706. xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE);
  707. fail:
  708. grub_initrd_close (&initrd_ctx);
  709. return grub_errno;
  710. }
  711. static grub_err_t
  712. grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
  713. int argc, char *argv[])
  714. {
  715. grub_size_t size = 0;
  716. grub_err_t err;
  717. grub_relocator_chunk_t ch;
  718. grub_size_t cmdline_len;
  719. int nounzip = 0;
  720. grub_file_t file;
  721. if (argc == 0)
  722. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  723. if (grub_strcmp (argv[0], "--nounzip") == 0)
  724. {
  725. argv++;
  726. argc--;
  727. nounzip = 1;
  728. }
  729. if (argc == 0)
  730. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  731. if (!xen_state.loaded)
  732. {
  733. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  734. N_("you need to load the kernel first"));
  735. }
  736. if ((xen_state.next_start.mod_start || xen_state.next_start.mod_len) &&
  737. !xen_state.module_info_page)
  738. {
  739. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
  740. }
  741. /* Leave one space for terminator. */
  742. if (xen_state.n_modules >= MAX_MODULES - 1)
  743. {
  744. return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules");
  745. }
  746. if (!xen_state.module_info_page)
  747. {
  748. xen_state.xen_inf.unmapped_initrd = 0;
  749. xen_state.n_modules = 0;
  750. xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE);
  751. xen_state.modules_target_start = xen_state.max_addr;
  752. xen_state.next_start.mod_start =
  753. xen_state.max_addr + xen_state.xen_inf.virt_base;
  754. xen_state.next_start.flags |= SIF_MULTIBOOT_MOD;
  755. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  756. xen_state.max_addr, MAX_MODULES
  757. *
  758. sizeof (xen_state.module_info_page
  759. [0]));
  760. if (err)
  761. return err;
  762. xen_state.module_info_page = get_virtual_current_address (ch);
  763. grub_memset (xen_state.module_info_page, 0, MAX_MODULES
  764. * sizeof (xen_state.module_info_page[0]));
  765. xen_state.max_addr +=
  766. MAX_MODULES * sizeof (xen_state.module_info_page[0]);
  767. }
  768. xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE);
  769. file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD |
  770. (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE));
  771. if (!file)
  772. return grub_errno;
  773. size = grub_file_size (file);
  774. cmdline_len = grub_loader_cmdline_size (argc - 1, argv + 1);
  775. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  776. xen_state.max_addr, cmdline_len);
  777. if (err)
  778. goto fail;
  779. err = grub_create_loader_cmdline (argc - 1, argv + 1,
  780. get_virtual_current_address (ch), cmdline_len,
  781. GRUB_VERIFY_MODULE_CMDLINE);
  782. if (err)
  783. goto fail;
  784. xen_state.module_info_page[xen_state.n_modules].cmdline =
  785. xen_state.max_addr - xen_state.modules_target_start;
  786. xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, GRUB_PAGE_SIZE);
  787. if (size)
  788. {
  789. err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
  790. xen_state.max_addr, size);
  791. if (err)
  792. goto fail;
  793. if (grub_file_read (file, get_virtual_current_address (ch), size)
  794. != (grub_ssize_t) size)
  795. {
  796. if (!grub_errno)
  797. grub_error (GRUB_ERR_FILE_READ_ERROR,
  798. N_("premature end of file %s"), argv[0]);
  799. goto fail;
  800. }
  801. }
  802. xen_state.next_start.mod_len =
  803. xen_state.max_addr + size - xen_state.modules_target_start;
  804. xen_state.module_info_page[xen_state.n_modules].mod_start =
  805. xen_state.max_addr - xen_state.modules_target_start;
  806. xen_state.module_info_page[xen_state.n_modules].mod_end =
  807. xen_state.max_addr + size - xen_state.modules_target_start;
  808. xen_state.n_modules++;
  809. grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n",
  810. (unsigned) xen_state.max_addr, (unsigned) size);
  811. xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE);
  812. fail:
  813. grub_file_close (file);
  814. return grub_errno;
  815. }
  816. static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot;
  817. GRUB_MOD_INIT (xen)
  818. {
  819. cmd_xen = grub_register_command ("linux", grub_cmd_xen,
  820. 0, N_("Load Linux."));
  821. cmd_multiboot = grub_register_command ("multiboot", grub_cmd_xen,
  822. 0, N_("Load Linux."));
  823. cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
  824. 0, N_("Load initrd."));
  825. cmd_module = grub_register_command ("module", grub_cmd_module,
  826. 0, N_("Load module."));
  827. my_mod = mod;
  828. }
  829. GRUB_MOD_FINI (xen)
  830. {
  831. grub_unregister_command (cmd_xen);
  832. grub_unregister_command (cmd_initrd);
  833. grub_unregister_command (cmd_multiboot);
  834. grub_unregister_command (cmd_module);
  835. }