diff --git a/sys/dev/xen/bus/xenpv.c b/sys/dev/xen/bus/xenpv.c --- a/sys/dev/xen/bus/xenpv.c +++ b/sys/dev/xen/bus/xenpv.c @@ -65,6 +65,12 @@ #define LOW_MEM_LIMIT 0 #endif +static struct rman unpopulated_mem = { + .rm_end = ~0, + .rm_type = RMAN_ARRAY, + .rm_descr = "Xen unpopulated memory", +}; + static void xenpv_identify(driver_t *driver, device_t parent) { @@ -91,10 +97,24 @@ return (BUS_PROBE_NOWILDCARD); } +/* Dummy init for arches that don't have a specific implementation. */ +int __weak_symbol +xen_arch_init_physmem(device_t dev, struct rman *mem) +{ + return (0); +} + static int xenpv_attach(device_t dev) { - int error; + int error = rman_init(&unpopulated_mem); + + if (error != 0) + return (error); + + error = xen_arch_init_physmem(dev, &unpopulated_mem); + if (error != 0) + return (error); /* * Let our child drivers identify any child devices that they @@ -110,6 +130,14 @@ return (error); } +static int +release_unpopulated_mem(device_t dev, struct resource *res) +{ + + return (rman_is_region_manager(res, &unpopulated_mem) ? + rman_release_resource(res) : bus_release_resource(dev, res)); +} + static struct resource * xenpv_alloc_physmem(device_t dev, device_t child, int *res_id, size_t size) { @@ -117,17 +145,48 @@ vm_paddr_t phys_addr; void *virt_addr; int error; + const unsigned int flags = RF_ACTIVE | RF_UNMAPPED | + RF_ALIGNMENT_LOG2(PAGE_SHIFT); - res = bus_alloc_resource(child, SYS_RES_MEMORY, res_id, LOW_MEM_LIMIT, - ~0, size, RF_ACTIVE | RF_UNMAPPED); - if (res == NULL) + KASSERT((size & PAGE_MASK) == 0, ("unaligned size requested")); + size = round_page(size); + + /* Attempt to allocate from arch resource manager. */ + res = rman_reserve_resource(&unpopulated_mem, 0, ~0, size, flags, + child); + if (res != NULL) { + rman_set_rid(res, *res_id); + rman_set_type(res, SYS_RES_MEMORY); + } else { + static bool warned = false; + + /* Fallback to generic MMIO allocator. */ + if (__predict_false(!warned)) { + warned = true; + device_printf(dev, + "unable to allocate from arch specific routine, " + "fall back to unused memory areas\n"); + } + res = bus_alloc_resource(child, SYS_RES_MEMORY, res_id, + LOW_MEM_LIMIT, ~0, size, flags); + } + + if (res == NULL) { + device_printf(dev, + "failed to allocate Xen unpopulated memory\n"); return (NULL); + } phys_addr = rman_get_start(res); error = vm_phys_fictitious_reg_range(phys_addr, phys_addr + size, VM_MEMATTR_XEN); if (error) { - bus_release_resource(child, SYS_RES_MEMORY, *res_id, res); + int error = release_unpopulated_mem(child, res); + + if (error != 0) + device_printf(dev, "failed to release resource: %d\n", + error); + return (NULL); } virt_addr = pmap_mapdev_attr(phys_addr, size, VM_MEMATTR_XEN); @@ -150,7 +209,8 @@ pmap_unmapdev(virt_addr, size); vm_phys_fictitious_unreg_range(phys_addr, phys_addr + size); - return (bus_release_resource(child, SYS_RES_MEMORY, res_id, res)); + + return release_unpopulated_mem(child, res); } static device_method_t xenpv_methods[] = { diff --git a/sys/x86/include/xen/xen-os.h b/sys/x86/include/xen/xen-os.h --- a/sys/x86/include/xen/xen-os.h +++ b/sys/x86/include/xen/xen-os.h @@ -39,6 +39,7 @@ #ifndef __ASSEMBLY__ #include +#include /* If non-zero, the hypervisor has been configured to use a direct vector */ extern int xen_vector_callback_enabled; diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c --- a/sys/x86/xen/hvm.c +++ b/sys/x86/xen/hvm.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -548,3 +549,52 @@ return (regs[0] & XEN_HVM_CPUID_IOMMU_MAPPINGS); } + +int +xen_arch_init_physmem(device_t dev, struct rman *mem) +{ + static struct bios_smap smap[128]; + struct xen_memory_map memmap = { + .nr_entries = nitems(smap), + }; + unsigned int i; + int error; + + if (!xen_initial_domain()) + return (0); + + set_xen_guest_handle(memmap.buffer, smap); + error = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap); + if (error != 0) + return (error); + + /* + * Fill with UNUSABLE regions, as it's always fine to use those for + * foreign mappings, they will never be populated. + */ + for (i = 0; i < memmap.nr_entries; i++) { + vm_paddr_t start, end; + + if (smap[i].type != SMAP_TYPE_ACPI_ERROR) + continue; + + start = round_page(smap[i].base); + end = trunc_page(smap[i].base + smap[i].length) - 1; + + if (start >= end) + continue; + + if (bootverbose != 0) + device_printf(dev, + "scratch mapping region @ [%#lx, %#lx]\n", + start, end); + + error = rman_manage_region(mem, start, end); + if (error != 0) + device_printf(dev, + "unable to add scratch region [%#lx, %#lx]: %d\n", + start, end, error); + } + + return (0); +} diff --git a/sys/xen/xen-os.h b/sys/xen/xen-os.h --- a/sys/xen/xen-os.h +++ b/sys/xen/xen-os.h @@ -158,6 +158,9 @@ */ void xen_emergency_print(const char *str, size_t size); +/* Arch-specific helper to init scratch mapping space. */ +int xen_arch_init_physmem(device_t dev, struct rman *mem); + #ifndef xen_mb #define xen_mb() mb() #endif