diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c index bdc9748793..5dfb3f3c41 100644 --- a/xen/arch/x86/e820.c +++ b/xen/arch/x86/e820.c @@ -373,7 +373,13 @@ static void __init clip_to_limit(uint64_t limit, char *warnmsg) ((e820.map[i].addr + e820.map[i].size) <= limit) ) continue; old_limit = e820.map[i].addr + e820.map[i].size; - if ( e820.map[i].addr < limit ) + if ( e820_change_range_type(&e820, max(e820.map[i].addr, limit), + old_limit, E820_RAM, E820_UNUSABLE) ) + { + /* Start again now e820 map must have changed. */ + i = 0; + } + else if ( e820.map[i].addr < limit ) { e820.map[i].size = limit - e820.map[i].addr; } diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index c65fb6a661..7e6001c466 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -3962,6 +3962,37 @@ long do_update_descriptor(u64 pa, u64 desc) typedef struct e820entry e820entry_t; DEFINE_XEN_GUEST_HANDLE(e820entry_t); +struct memory_map_context +{ + unsigned int n; + unsigned long s; + struct xen_memory_map map; +}; + +static int handle_iomem_range(unsigned long s, unsigned long e, void *p) +{ + struct memory_map_context *ctxt = p; + + if ( s > ctxt->s ) + { + e820entry_t ent; + XEN_GUEST_HANDLE(e820entry_t) buffer; + + if ( ctxt->n + 1 >= ctxt->map.nr_entries ) + return -EINVAL; + ent.addr = (uint64_t)ctxt->s << PAGE_SHIFT; + ent.size = (uint64_t)(s - ctxt->s) << PAGE_SHIFT; + ent.type = E820_RESERVED; + buffer = guest_handle_cast(ctxt->map.buffer, e820entry_t); + if ( __copy_to_guest_offset(buffer, ctxt->n, &ent, 1) < 0 ) + return -EFAULT; + ctxt->n++; + } + ctxt->s = e + 1; + + return 0; +} + long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg) { struct page_info *page = NULL; @@ -4123,9 +4154,9 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg) case XENMEM_machine_memory_map: { - struct xen_memory_map memmap; + struct memory_map_context ctxt; XEN_GUEST_HANDLE(e820entry_t) buffer; - int count; + unsigned int i; int rc; if ( !IS_PRIV(current->domain) ) @@ -4135,20 +4166,49 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg) if ( rc ) return rc; - if ( copy_from_guest(&memmap, arg, 1) ) + if ( copy_from_guest(&ctxt.map, arg, 1) ) return -EFAULT; - if ( memmap.nr_entries < e820.nr_map + 1 ) + if ( ctxt.map.nr_entries < e820.nr_map + 1 ) return -EINVAL; - buffer = guest_handle_cast(memmap.buffer, e820entry_t); - - count = min((unsigned int)e820.nr_map, memmap.nr_entries); - if ( copy_to_guest(buffer, e820.map, count) < 0 ) + buffer = guest_handle_cast(ctxt.map.buffer, e820entry_t); + if ( !guest_handle_okay(buffer, ctxt.map.nr_entries) ) return -EFAULT; - memmap.nr_entries = count; + for ( i = 0, ctxt.n = 0, ctxt.s = 0; i < e820.nr_map; ++i, ++ctxt.n ) + { + unsigned long s = PFN_DOWN(e820.map[i].addr); - if ( copy_to_guest(arg, &memmap, 1) ) + if ( s ) + { + rc = rangeset_report_ranges(current->domain->iomem_caps, + ctxt.s, s - 1, + handle_iomem_range, &ctxt); + if ( !rc ) + rc = handle_iomem_range(s, s, &ctxt); + if ( rc ) + return rc; + } + if ( ctxt.map.nr_entries <= ctxt.n + (e820.nr_map - i) ) + return -EINVAL; + if ( __copy_to_guest_offset(buffer, ctxt.n, e820.map + i, 1) < 0 ) + return -EFAULT; + ctxt.s = PFN_UP(e820.map[i].addr + e820.map[i].size); + } + + if ( ctxt.s ) + { + rc = rangeset_report_ranges(current->domain->iomem_caps, ctxt.s, + ~0UL, handle_iomem_range, &ctxt); + if ( !rc && ctxt.s ) + rc = handle_iomem_range(~0UL, ~0UL, &ctxt); + if ( rc ) + return rc; + } + + ctxt.map.nr_entries = ctxt.n; + + if ( copy_to_guest(arg, &ctxt.map, 1) ) return -EFAULT; return 0; diff --git a/xen/common/rangeset.c b/xen/common/rangeset.c index befeff23dc..78d0647acc 100644 --- a/xen/common/rangeset.c +++ b/xen/common/rangeset.c @@ -251,6 +251,24 @@ int rangeset_contains_range( return contains; } +int rangeset_report_ranges( + struct rangeset *r, unsigned long s, unsigned long e, + int (*cb)(unsigned long s, unsigned long e, void *), void *ctxt) +{ + struct range *x; + int rc = 0; + + spin_lock(&r->lock); + + for ( x = find_range(r, s); x && (x->s <= e) && !rc; x = next_range(r, x) ) + if ( x->e >= s ) + rc = cb(max(x->s, s), min(x->e, e), ctxt); + + spin_unlock(&r->lock); + + return rc; +} + int rangeset_add_singleton( struct rangeset *r, unsigned long s) { diff --git a/xen/include/xen/rangeset.h b/xen/include/xen/rangeset.h index d4a8e00393..4219c079ef 100644 --- a/xen/include/xen/rangeset.h +++ b/xen/include/xen/rangeset.h @@ -53,6 +53,9 @@ int __must_check rangeset_remove_range( struct rangeset *r, unsigned long s, unsigned long e); int __must_check rangeset_contains_range( struct rangeset *r, unsigned long s, unsigned long e); +int rangeset_report_ranges( + struct rangeset *r, unsigned long s, unsigned long e, + int (*cb)(unsigned long s, unsigned long e, void *), void *ctxt); /* Add/remove/query a single number. */ int __must_check rangeset_add_singleton(