123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2011 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/xen.h>
- #include <grub/term.h>
- #include <grub/misc.h>
- #include <grub/env.h>
- #include <grub/mm.h>
- #include <grub/kernel.h>
- #include <grub/offsets.h>
- #include <grub/memory.h>
- #include <grub/i386/tsc.h>
- #include <grub/term.h>
- #include <grub/loader.h>
- grub_addr_t grub_modbase;
- struct start_info *grub_xen_start_page_addr;
- volatile struct xencons_interface *grub_xen_xcons;
- volatile struct shared_info *grub_xen_shared_info;
- volatile struct xenstore_domain_interface *grub_xen_xenstore;
- volatile grant_entry_v1_t *grub_xen_grant_table;
- static const grub_size_t total_grants =
- GRUB_XEN_PAGE_SIZE / sizeof (grub_xen_grant_table[0]);
- grub_size_t grub_xen_n_allocated_shared_pages;
- static grub_xen_mfn_t
- grub_xen_ptr2mfn (void *ptr)
- {
- #ifdef GRUB_MACHINE_XEN
- grub_xen_mfn_t *mfn_list =
- (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list;
- return mfn_list[(grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE];
- #else
- return (grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE;
- #endif
- }
- void *
- grub_xen_alloc_shared_page (domid_t dom, grub_xen_grant_t * grnum)
- {
- void *ret;
- grub_xen_mfn_t mfn;
- volatile grant_entry_v1_t *entry;
- /* Avoid 0. */
- for (entry = grub_xen_grant_table;
- entry < grub_xen_grant_table + total_grants; entry++)
- if (!entry->flags)
- break;
- if (entry == grub_xen_grant_table + total_grants)
- {
- grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of grant entries");
- return NULL;
- }
- ret = grub_memalign (GRUB_XEN_PAGE_SIZE, GRUB_XEN_PAGE_SIZE);
- if (!ret)
- return NULL;
- mfn = grub_xen_ptr2mfn (ret);
- entry->frame = mfn;
- entry->domid = dom;
- mb ();
- entry->flags = GTF_permit_access;
- mb ();
- *grnum = entry - grub_xen_grant_table;
- grub_xen_n_allocated_shared_pages++;
- return ret;
- }
- void
- grub_xen_free_shared_page (void *ptr)
- {
- grub_xen_mfn_t mfn;
- volatile grant_entry_v1_t *entry;
- mfn = grub_xen_ptr2mfn (ptr);
- for (entry = grub_xen_grant_table + 1;
- entry < grub_xen_grant_table + total_grants; entry++)
- if (entry->flags && entry->frame == mfn)
- {
- mb ();
- entry->flags = 0;
- mb ();
- entry->frame = 0;
- mb ();
- }
- grub_xen_n_allocated_shared_pages--;
- }
- void
- grub_machine_get_bootlocation (char **device __attribute__ ((unused)),
- char **path __attribute__ ((unused)))
- {
- }
- void
- grub_xen_store_send (const void *buf_, grub_size_t len)
- {
- const grub_uint8_t *buf = buf_;
- struct evtchn_send send;
- int event_sent = 0;
- while (len)
- {
- grub_size_t avail, inbuf;
- grub_size_t prod, cons;
- mb ();
- prod = grub_xen_xenstore->req_prod;
- cons = grub_xen_xenstore->req_cons;
- if (prod >= cons + sizeof (grub_xen_xenstore->req))
- {
- if (!event_sent)
- {
- send.port = grub_xen_start_page_addr->store_evtchn;
- grub_xen_event_channel_op (EVTCHNOP_send, &send);
- event_sent = 1;
- }
- grub_xen_sched_op (SCHEDOP_yield, 0);
- continue;
- }
- event_sent = 0;
- avail = cons + sizeof (grub_xen_xenstore->req) - prod;
- inbuf = (~prod & (sizeof (grub_xen_xenstore->req) - 1)) + 1;
- if (avail > inbuf)
- avail = inbuf;
- if (avail > len)
- avail = len;
- grub_memcpy ((void *) &grub_xen_xenstore->req[prod & (sizeof (grub_xen_xenstore->req) - 1)],
- buf, avail);
- buf += avail;
- len -= avail;
- mb ();
- grub_xen_xenstore->req_prod += avail;
- mb ();
- if (!event_sent)
- {
- send.port = grub_xen_start_page_addr->store_evtchn;
- grub_xen_event_channel_op (EVTCHNOP_send, &send);
- event_sent = 1;
- }
- grub_xen_sched_op (SCHEDOP_yield, 0);
- }
- }
- void
- grub_xen_store_recv (void *buf_, grub_size_t len)
- {
- grub_uint8_t *buf = buf_;
- struct evtchn_send send;
- int event_sent = 0;
- while (len)
- {
- grub_size_t avail, inbuf;
- grub_size_t prod, cons;
- mb ();
- prod = grub_xen_xenstore->rsp_prod;
- cons = grub_xen_xenstore->rsp_cons;
- if (prod <= cons)
- {
- if (!event_sent)
- {
- send.port = grub_xen_start_page_addr->store_evtchn;
- grub_xen_event_channel_op (EVTCHNOP_send, &send);
- event_sent = 1;
- }
- grub_xen_sched_op (SCHEDOP_yield, 0);
- continue;
- }
- event_sent = 0;
- avail = prod - cons;
- inbuf = (~cons & (sizeof (grub_xen_xenstore->req) - 1)) + 1;
- if (avail > inbuf)
- avail = inbuf;
- if (avail > len)
- avail = len;
- grub_memcpy (buf,
- (void *) &grub_xen_xenstore->rsp[cons & (sizeof (grub_xen_xenstore->rsp) - 1)],
- avail);
- buf += avail;
- len -= avail;
- mb ();
- grub_xen_xenstore->rsp_cons += avail;
- mb ();
- if (!event_sent)
- {
- send.port = grub_xen_start_page_addr->store_evtchn;
- grub_xen_event_channel_op(EVTCHNOP_send, &send);
- event_sent = 1;
- }
- grub_xen_sched_op(SCHEDOP_yield, 0);
- }
- }
- void *
- grub_xenstore_get_file (const char *dir, grub_size_t *len)
- {
- struct xsd_sockmsg msg;
- char *buf;
- grub_size_t dirlen = grub_strlen (dir) + 1;
- if (len)
- *len = 0;
- grub_memset (&msg, 0, sizeof (msg));
- msg.type = XS_READ;
- msg.len = dirlen;
- grub_xen_store_send (&msg, sizeof (msg));
- grub_xen_store_send (dir, dirlen);
- grub_xen_store_recv (&msg, sizeof (msg));
- buf = grub_malloc (msg.len + 1);
- if (!buf)
- return NULL;
- grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
- grub_xen_store_recv (buf, msg.len);
- buf[msg.len] = '\0';
- if (msg.type == XS_ERROR)
- {
- grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", dir, buf);
- grub_free (buf);
- return NULL;
- }
- if (len)
- *len = msg.len;
- return buf;
- }
- grub_err_t
- grub_xenstore_write_file (const char *dir, const void *buf, grub_size_t len)
- {
- struct xsd_sockmsg msg;
- grub_size_t dirlen = grub_strlen (dir) + 1;
- char *resp;
- grub_memset (&msg, 0, sizeof (msg));
- msg.type = XS_WRITE;
- msg.len = dirlen + len;
- grub_xen_store_send (&msg, sizeof (msg));
- grub_xen_store_send (dir, dirlen);
- grub_xen_store_send (buf, len);
- grub_xen_store_recv (&msg, sizeof (msg));
- resp = grub_malloc (msg.len + 1);
- if (!resp)
- return grub_errno;
- grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
- grub_xen_store_recv (resp, msg.len);
- resp[msg.len] = '\0';
- if (msg.type == XS_ERROR)
- {
- grub_dprintf ("xen", "error = %s\n", resp);
- grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s",
- dir, resp);
- grub_free (resp);
- return grub_errno;
- }
- grub_free (resp);
- return GRUB_ERR_NONE;
- }
- /* FIXME: error handling. */
- grub_err_t
- grub_xenstore_dir (const char *dir,
- int (*hook) (const char *dir, void *hook_data),
- void *hook_data)
- {
- struct xsd_sockmsg msg;
- char *buf;
- char *ptr;
- grub_size_t dirlen = grub_strlen (dir) + 1;
- grub_memset (&msg, 0, sizeof (msg));
- msg.type = XS_DIRECTORY;
- msg.len = dirlen;
- grub_xen_store_send (&msg, sizeof (msg));
- grub_xen_store_send (dir, dirlen);
- grub_xen_store_recv (&msg, sizeof (msg));
- buf = grub_malloc (msg.len + 1);
- if (!buf)
- return grub_errno;
- grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len);
- grub_xen_store_recv (buf, msg.len);
- buf[msg.len] = '\0';
- if (msg.type == XS_ERROR)
- {
- grub_err_t err;
- err = grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s",
- dir, buf);
- grub_free (buf);
- return err;
- }
- for (ptr = buf; ptr < buf + msg.len; ptr += grub_strlen (ptr) + 1)
- if (hook (ptr, hook_data))
- break;
- grub_free (buf);
- return grub_errno;
- }
- unsigned long gntframe = 0;
- static void
- grub_xen_setup_gnttab (void)
- {
- struct gnttab_set_version gnttab_setver;
- struct gnttab_setup_table gnttab_setup;
- grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver));
- gnttab_setver.version = 1;
- grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1);
- grub_memset (&gnttab_setup, 0, sizeof (gnttab_setup));
- gnttab_setup.dom = DOMID_SELF;
- gnttab_setup.nr_frames = 1;
- gnttab_setup.frame_list.p = &gntframe;
- grub_xen_grant_table_op (GNTTABOP_setup_table, &gnttab_setup, 1);
- }
- #ifdef GRUB_MACHINE_XEN
- static grub_uint8_t window[GRUB_XEN_PAGE_SIZE]
- __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE)));
- #ifdef __x86_64__
- #define NUMBER_OF_LEVELS 4
- #else
- #define NUMBER_OF_LEVELS 3
- #endif
- #define LOG_POINTERS_PER_PAGE 9
- #define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
- #define MAX_N_UNUSABLE_PAGES 4
- static int
- grub_xen_is_page_usable (grub_xen_mfn_t mfn)
- {
- if (mfn == grub_xen_start_page_addr->console.domU.mfn)
- return 0;
- if (mfn == grub_xen_start_page_addr->shared_info)
- return 0;
- if (mfn == grub_xen_start_page_addr->store_mfn)
- return 0;
- if (mfn == gntframe)
- return 0;
- return 1;
- }
- static grub_uint64_t
- page2offset (grub_uint64_t page)
- {
- return page << 12;
- }
- #if defined (__x86_64__) && defined (__code_model_large__)
- #define MAX_TOTAL_PAGES (1LL << (64 - 12))
- #elif defined (__x86_64__)
- #define MAX_TOTAL_PAGES (1LL << (31 - 12))
- #else
- #define MAX_TOTAL_PAGES (1LL << (32 - 12))
- #endif
- static void
- map_all_pages (void)
- {
- grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages;
- grub_uint64_t i, j;
- grub_xen_mfn_t *mfn_list =
- (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list;
- grub_uint64_t *pg = (grub_uint64_t *) window;
- grub_uint64_t oldpgstart, oldpgend;
- grub_size_t n_unusable_pages = 0;
- struct mmu_update m2p_updates[2 * MAX_N_UNUSABLE_PAGES];
- if (total_pages > MAX_TOTAL_PAGES - 4)
- total_pages = MAX_TOTAL_PAGES - 4;
- for (j = 0; j < total_pages - n_unusable_pages; j++)
- while (!grub_xen_is_page_usable (mfn_list[j]))
- {
- grub_xen_mfn_t t;
- if (n_unusable_pages >= MAX_N_UNUSABLE_PAGES)
- {
- struct sched_shutdown arg;
- arg.reason = SHUTDOWN_crash;
- grub_xen_sched_op (SCHEDOP_shutdown, &arg);
- while (1);
- }
- t = mfn_list[j];
- mfn_list[j] = mfn_list[total_pages - n_unusable_pages - 1];
- mfn_list[total_pages - n_unusable_pages - 1] = t;
- m2p_updates[2 * n_unusable_pages].ptr
- = page2offset (mfn_list[j]) | MMU_MACHPHYS_UPDATE;
- m2p_updates[2 * n_unusable_pages].val = j;
- m2p_updates[2 * n_unusable_pages + 1].ptr
- = page2offset (mfn_list[total_pages - n_unusable_pages - 1])
- | MMU_MACHPHYS_UPDATE;
- m2p_updates[2 * n_unusable_pages + 1].val = total_pages
- - n_unusable_pages - 1;
- n_unusable_pages++;
- }
- grub_xen_mmu_update (m2p_updates, 2 * n_unusable_pages, NULL, DOMID_SELF);
- total_pages += 4;
- grub_uint64_t lx[NUMBER_OF_LEVELS], nlx;
- grub_uint64_t paging_start = total_pages - 4 - n_unusable_pages, curpage;
- for (nlx = total_pages, i = 0; i < (unsigned) NUMBER_OF_LEVELS; i++)
- {
- nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE;
- /* PAE wants all 4 root directories present. */
- #ifdef __i386__
- if (i == 1)
- nlx = 4;
- #endif
- lx[i] = nlx;
- paging_start -= nlx;
- }
- oldpgstart = grub_xen_start_page_addr->pt_base >> 12;
- oldpgend = oldpgstart + grub_xen_start_page_addr->nr_pt_frames;
- curpage = paging_start;
- int l;
- for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--)
- {
- for (i = 0; i < lx[l]; i++)
- {
- grub_xen_update_va_mapping (&window,
- page2offset (mfn_list[curpage + i]) | 7,
- UVMF_INVLPG);
- grub_memset (&window, 0, sizeof (window));
- for (j = i * POINTERS_PER_PAGE;
- j < (i + 1) * POINTERS_PER_PAGE && j < lx[l - 1]; j++)
- pg[j - i * POINTERS_PER_PAGE] =
- page2offset (mfn_list[curpage + lx[l] + j])
- #ifdef __x86_64__
- | 4
- #endif
- | 3;
- }
- curpage += lx[l];
- }
- for (i = 0; i < lx[0]; i++)
- {
- grub_xen_update_va_mapping (&window,
- page2offset (mfn_list[curpage + i]) | 7,
- UVMF_INVLPG);
- grub_memset (&window, 0, sizeof (window));
- for (j = i * POINTERS_PER_PAGE;
- j < (i + 1) * POINTERS_PER_PAGE && j < total_pages; j++)
- if (j < paging_start && !(j >= oldpgstart && j < oldpgend))
- pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 0x7;
- else if (j < grub_xen_start_page_addr->nr_pages)
- pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 5;
- else if (j == grub_xen_start_page_addr->nr_pages)
- {
- pg[j - i * POINTERS_PER_PAGE] =
- page2offset (grub_xen_start_page_addr->console.domU.mfn) | 7;
- grub_xen_xcons = (void *) (grub_addr_t) page2offset (j);
- }
- else if (j == grub_xen_start_page_addr->nr_pages + 1)
- {
- pg[j - i * POINTERS_PER_PAGE] =
- grub_xen_start_page_addr->shared_info | 7;
- grub_xen_shared_info = (void *) (grub_addr_t) page2offset (j);
- }
- else if (j == grub_xen_start_page_addr->nr_pages + 2)
- {
- pg[j - i * POINTERS_PER_PAGE] =
- page2offset (grub_xen_start_page_addr->store_mfn) | 7;
- grub_xen_xenstore = (void *) (grub_addr_t) page2offset (j);
- }
- else if (j == grub_xen_start_page_addr->nr_pages + 3)
- {
- pg[j - i * POINTERS_PER_PAGE] = page2offset (gntframe) | 7;
- grub_xen_grant_table = (void *) (grub_addr_t) page2offset (j);
- }
- }
- grub_xen_update_va_mapping (&window, 0, UVMF_INVLPG);
- mmuext_op_t op[3];
- op[0].cmd = MMUEXT_PIN_L1_TABLE + (NUMBER_OF_LEVELS - 1);
- op[0].arg1.mfn = mfn_list[paging_start];
- op[1].cmd = MMUEXT_NEW_BASEPTR;
- op[1].arg1.mfn = mfn_list[paging_start];
- op[2].cmd = MMUEXT_UNPIN_TABLE;
- op[2].arg1.mfn = mfn_list[oldpgstart];
- grub_xen_mmuext_op (op, 3, NULL, DOMID_SELF);
- for (i = oldpgstart; i < oldpgend; i++)
- grub_xen_update_va_mapping ((void *) (grub_addr_t) page2offset (i),
- page2offset (mfn_list[i]) | 7, UVMF_INVLPG);
- void *new_start_page, *new_mfn_list;
- new_start_page = (void *) (grub_addr_t) page2offset (paging_start - 1);
- grub_memcpy (new_start_page, grub_xen_start_page_addr, 4096);
- grub_xen_start_page_addr = new_start_page;
- new_mfn_list = (void *) (grub_addr_t)
- page2offset (paging_start - 1
- - ((grub_xen_start_page_addr->nr_pages
- * sizeof (grub_uint64_t) + 4095) / 4096));
- grub_memcpy (new_mfn_list, mfn_list, grub_xen_start_page_addr->nr_pages
- * sizeof (grub_uint64_t));
- grub_xen_start_page_addr->pt_base = page2offset (paging_start);
- grub_xen_start_page_addr->mfn_list = (grub_addr_t) new_mfn_list;
- grub_addr_t heap_start = grub_modules_get_end ();
- grub_addr_t heap_end = (grub_addr_t) new_mfn_list;
- grub_mm_init_region ((void *) heap_start, heap_end - heap_start);
- }
- grub_err_t
- grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
- {
- grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages;
- grub_uint64_t usable_pages = grub_xen_start_page_addr->pt_base >> 12;
- if (hook (0, page2offset (usable_pages), GRUB_MEMORY_AVAILABLE, hook_data))
- return GRUB_ERR_NONE;
- hook (page2offset (usable_pages), page2offset (total_pages - usable_pages),
- GRUB_MEMORY_RESERVED, hook_data);
- return GRUB_ERR_NONE;
- }
- #endif
- extern char _end[];
- void
- grub_machine_init (void)
- {
- #ifdef GRUB_MACHINE_XEN
- #ifdef __i386__
- grub_xen_vm_assist (VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3);
- #endif
- #endif
- grub_modbase = ALIGN_UP ((grub_addr_t) _end
- + GRUB_KERNEL_MACHINE_MOD_GAP,
- GRUB_KERNEL_MACHINE_MOD_ALIGN);
- #ifdef GRUB_MACHINE_XEN_PVH
- grub_xen_setup_pvh ();
- #endif
- grub_xen_setup_gnttab ();
- #ifdef GRUB_MACHINE_XEN
- map_all_pages ();
- #endif
- grub_console_init ();
- grub_tsc_init ();
- grub_xendisk_init ();
- grub_boot_init ();
- }
- void
- grub_exit (void)
- {
- struct sched_shutdown arg;
- arg.reason = SHUTDOWN_poweroff;
- grub_xen_sched_op (SCHEDOP_shutdown, &arg);
- while (1);
- }
- void
- grub_machine_fini (int flags __attribute__ ((unused)))
- {
- grub_xendisk_fini ();
- grub_boot_fini ();
- }
|