/* **************************************************************************** * (C) 2006 - Cambridge University **************************************************************************** * * File: gnttab.c * Author: Steven Smith (sos22@cam.ac.uk) * Changes: Grzegorz Milos (gm281@cam.ac.uk) * * Date: July 2006 * * Environment: Xen Minimal OS * Description: Simple grant tables implementation. About as stupid as it's * possible to be and still work. * **************************************************************************** */ #include #include #include #define NR_RESERVED_ENTRIES 8 #define NR_GRANT_FRAMES 4 #define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(grant_entry_t)) #define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1) static grant_entry_t *gnttab_table; static grant_ref_t gnttab_list[NR_GRANT_ENTRIES]; static grant_ref_t gnttab_free_head; static grant_ref_t get_free_entries(int count) { grant_ref_t ref; grant_ref_t head; ref = head = gnttab_free_head; while (count-- > 1) head = gnttab_list[head]; gnttab_free_head = gnttab_list[head]; gnttab_list[head] = GNTTAB_LIST_END; return ref; } static void put_free_entry(grant_ref_t gref) { gnttab_list[gref] = gnttab_free_head; gnttab_free_head = gref; } grant_ref_t gnttab_grant_access(domid_t domid, unsigned long frame, int readonly) { grant_ref_t ref; ref = get_free_entries(1); gnttab_table[ref].frame = frame; gnttab_table[ref].domid = domid; wmb(); readonly *= GTF_readonly; gnttab_table[ref].flags = GTF_permit_access | readonly; return ref; } grant_ref_t gnttab_grant_transfer(domid_t domid, unsigned long pfn) { grant_ref_t ref; ref = get_free_entries(1); gnttab_table[ref].frame = pfn; gnttab_table[ref].domid = domid; wmb(); gnttab_table[ref].flags = GTF_accept_transfer; return ref; } int gnttab_end_access(grant_ref_t ref) { u16 flags, nflags; nflags = gnttab_table[ref].flags; do { if ((flags = nflags) & (GTF_reading|GTF_writing)) { printk("WARNING: g.e. still in use!\n"); return 0; } } while ((nflags = synch_cmpxchg(&gnttab_table[ref].flags, flags, 0)) != flags); put_free_entry(ref); return 1; } unsigned long gnttab_end_transfer(grant_ref_t ref) { unsigned long frame; u16 flags; while (!((flags = gnttab_table[ref].flags) & GTF_transfer_committed)) { if (synch_cmpxchg(&gnttab_table[ref].flags, flags, 0) == flags) { printk("Release unused transfer grant.\n"); put_free_entry(ref); return 0; } } /* If a transfer is in progress then wait until it is completed. */ while (!(flags & GTF_transfer_completed)) { flags = gnttab_table[ref].flags; } /* Read the frame number /after/ reading completion status. */ rmb(); frame = gnttab_table[ref].frame; put_free_entry(ref); return frame; } grant_ref_t gnttab_alloc_and_grant(void **map) { unsigned long mfn; grant_ref_t gref; *map = (void *)alloc_page(); mfn = virt_to_mfn(*map); gref = gnttab_grant_access(0, mfn, 0); return gref; } static const char *gnttabop_error_msgs[] = GNTTABOP_error_msgs; const char * gnttabop_error(int16_t status) { status = -status; if (status < 0 || status >= ARRAY_SIZE(gnttabop_error_msgs)) return "bad status"; else return gnttabop_error_msgs[status]; } void init_gnttab(void) { struct gnttab_setup_table setup; unsigned long frames[NR_GRANT_FRAMES]; int i; for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++) gnttab_list[i] = i + 1; gnttab_free_head = NR_RESERVED_ENTRIES; setup.dom = DOMID_SELF; setup.nr_frames = NR_GRANT_FRAMES; set_xen_guest_handle(setup.frame_list, frames); HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); gnttab_table = map_frames(frames, NR_GRANT_FRAMES); printk("gnttab_table mapped at %p.\n", gnttab_table); }