Allocate memory pages for the guest from the host's free page queue.
It is no longer necessary to hard-partition the memory between the host and guests at boot time.
This commit is contained in:
parent
a16232b6c3
commit
7ce04d0ad9
@ -230,6 +230,14 @@ amd_iommu_create_mapping(void *domain, vm_paddr_t gpa, vm_paddr_t hpa,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
amd_iommu_remove_mapping(void *domain, vm_paddr_t gpa, uint64_t len)
|
||||
{
|
||||
|
||||
printf("amd_iommu_remove_mapping: not implemented\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
amd_iommu_add_device(void *domain, int bus, int slot, int func)
|
||||
{
|
||||
@ -244,6 +252,13 @@ amd_iommu_remove_device(void *domain, int bus, int slot, int func)
|
||||
printf("amd_iommu_remove_device: not implemented\n");
|
||||
}
|
||||
|
||||
static void
|
||||
amd_iommu_invalidate_tlb(void *domain)
|
||||
{
|
||||
|
||||
printf("amd_iommu_invalidate_tlb: not implemented\n");
|
||||
}
|
||||
|
||||
struct iommu_ops iommu_ops_amd = {
|
||||
amd_iommu_init,
|
||||
amd_iommu_cleanup,
|
||||
@ -252,6 +267,8 @@ struct iommu_ops iommu_ops_amd = {
|
||||
amd_iommu_create_domain,
|
||||
amd_iommu_destroy_domain,
|
||||
amd_iommu_create_mapping,
|
||||
amd_iommu_remove_mapping,
|
||||
amd_iommu_add_device,
|
||||
amd_iommu_remove_device,
|
||||
amd_iommu_invalidate_tlb,
|
||||
};
|
||||
|
@ -444,8 +444,12 @@ vtd_remove_device(void *arg, int bus, int slot, int func)
|
||||
}
|
||||
}
|
||||
|
||||
#define CREATE_MAPPING 0
|
||||
#define REMOVE_MAPPING 1
|
||||
|
||||
static uint64_t
|
||||
vtd_create_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
|
||||
vtd_update_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len,
|
||||
int remove)
|
||||
{
|
||||
struct domain *dom;
|
||||
int i, spshift, ptpshift, ptpindex, nlevels;
|
||||
@ -513,16 +517,50 @@ vtd_create_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
|
||||
panic("gpa 0x%lx and ptpshift %d mismatch", gpa, ptpshift);
|
||||
|
||||
/*
|
||||
* Create a 'gpa' -> 'hpa' mapping
|
||||
* Update the 'gpa' -> 'hpa' mapping
|
||||
*/
|
||||
ptp[ptpindex] = hpa | VTD_PTE_RD | VTD_PTE_WR;
|
||||
if (remove) {
|
||||
ptp[ptpindex] = 0;
|
||||
} else {
|
||||
ptp[ptpindex] = hpa | VTD_PTE_RD | VTD_PTE_WR;
|
||||
|
||||
if (nlevels > 0)
|
||||
ptp[ptpindex] |= VTD_PTE_SUPERPAGE;
|
||||
if (nlevels > 0)
|
||||
ptp[ptpindex] |= VTD_PTE_SUPERPAGE;
|
||||
}
|
||||
|
||||
return (1UL << ptpshift);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
vtd_create_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
|
||||
{
|
||||
|
||||
return (vtd_update_mapping(arg, gpa, hpa, len, CREATE_MAPPING));
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
vtd_remove_mapping(void *arg, vm_paddr_t gpa, uint64_t len)
|
||||
{
|
||||
|
||||
return (vtd_update_mapping(arg, gpa, 0, len, REMOVE_MAPPING));
|
||||
}
|
||||
|
||||
static void
|
||||
vtd_invalidate_tlb(void *dom)
|
||||
{
|
||||
int i;
|
||||
struct vtdmap *vtdmap;
|
||||
|
||||
/*
|
||||
* Invalidate the IOTLB.
|
||||
* XXX use domain-selective invalidation for IOTLB
|
||||
*/
|
||||
for (i = 0; i < drhd_num; i++) {
|
||||
vtdmap = vtdmaps[i];
|
||||
vtd_iotlb_global_invalidate(vtdmap);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
vtd_create_domain(vm_paddr_t maxaddr)
|
||||
{
|
||||
@ -632,6 +670,8 @@ struct iommu_ops iommu_ops_intel = {
|
||||
vtd_create_domain,
|
||||
vtd_destroy_domain,
|
||||
vtd_create_mapping,
|
||||
vtd_remove_mapping,
|
||||
vtd_add_device,
|
||||
vtd_remove_device,
|
||||
vtd_invalidate_tlb,
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/md_var.h>
|
||||
|
||||
#include "vmm_util.h"
|
||||
#include "vmm_mem.h"
|
||||
#include "iommu.h"
|
||||
|
||||
static boolean_t iommu_avail;
|
||||
@ -90,6 +91,16 @@ IOMMU_CREATE_MAPPING(void *domain, vm_paddr_t gpa, vm_paddr_t hpa, uint64_t len)
|
||||
return (len); /* XXX */
|
||||
}
|
||||
|
||||
static __inline uint64_t
|
||||
IOMMU_REMOVE_MAPPING(void *domain, vm_paddr_t gpa, uint64_t len)
|
||||
{
|
||||
|
||||
if (ops != NULL && iommu_avail)
|
||||
return ((*ops->remove_mapping)(domain, gpa, len));
|
||||
else
|
||||
return (len); /* XXX */
|
||||
}
|
||||
|
||||
static __inline void
|
||||
IOMMU_ADD_DEVICE(void *domain, int bus, int slot, int func)
|
||||
{
|
||||
@ -106,6 +117,14 @@ IOMMU_REMOVE_DEVICE(void *domain, int bus, int slot, int func)
|
||||
(*ops->remove_device)(domain, bus, slot, func);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
IOMMU_INVALIDATE_TLB(void *domain)
|
||||
{
|
||||
|
||||
if (ops != NULL && iommu_avail)
|
||||
(*ops->invalidate_tlb)(domain);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
IOMMU_ENABLE(void)
|
||||
{
|
||||
@ -146,13 +165,13 @@ iommu_init(void)
|
||||
/*
|
||||
* Create a domain for the devices owned by the host
|
||||
*/
|
||||
maxaddr = ptoa(Maxmem);
|
||||
maxaddr = vmm_mem_maxaddr();
|
||||
host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
|
||||
if (host_domain == NULL)
|
||||
panic("iommu_init: unable to create a host domain");
|
||||
|
||||
/*
|
||||
* Create 1:1 mappings from '0' to 'Maxmem' for devices assigned to
|
||||
* Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
|
||||
* the host
|
||||
*/
|
||||
iommu_create_mapping(host_domain, 0, 0, maxaddr);
|
||||
@ -215,6 +234,27 @@ iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len)
|
||||
{
|
||||
uint64_t unmapped, remaining;
|
||||
|
||||
remaining = len;
|
||||
|
||||
while (remaining > 0) {
|
||||
unmapped = IOMMU_REMOVE_MAPPING(dom, gpa, remaining);
|
||||
gpa += unmapped;
|
||||
remaining -= unmapped;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
iommu_host_domain(void)
|
||||
{
|
||||
|
||||
return (host_domain);
|
||||
}
|
||||
|
||||
void
|
||||
iommu_add_device(void *dom, int bus, int slot, int func)
|
||||
{
|
||||
@ -228,3 +268,10 @@ iommu_remove_device(void *dom, int bus, int slot, int func)
|
||||
|
||||
IOMMU_REMOVE_DEVICE(dom, bus, slot, func);
|
||||
}
|
||||
|
||||
void
|
||||
iommu_invalidate_tlb(void *domain)
|
||||
{
|
||||
|
||||
IOMMU_INVALIDATE_TLB(domain);
|
||||
}
|
||||
|
@ -37,8 +37,11 @@ typedef void *(*iommu_create_domain_t)(vm_paddr_t maxaddr);
|
||||
typedef void (*iommu_destroy_domain_t)(void *domain);
|
||||
typedef uint64_t (*iommu_create_mapping_t)(void *domain, vm_paddr_t gpa,
|
||||
vm_paddr_t hpa, uint64_t len);
|
||||
typedef uint64_t (*iommu_remove_mapping_t)(void *domain, vm_paddr_t gpa,
|
||||
uint64_t len);
|
||||
typedef void (*iommu_add_device_t)(void *domain, int bus, int slot, int func);
|
||||
typedef void (*iommu_remove_device_t)(void *dom, int bus, int slot, int func);
|
||||
typedef void (*iommu_invalidate_tlb_t)(void *dom);
|
||||
|
||||
struct iommu_ops {
|
||||
iommu_init_func_t init; /* module wide */
|
||||
@ -49,8 +52,10 @@ struct iommu_ops {
|
||||
iommu_create_domain_t create_domain; /* domain-specific */
|
||||
iommu_destroy_domain_t destroy_domain;
|
||||
iommu_create_mapping_t create_mapping;
|
||||
iommu_remove_mapping_t remove_mapping;
|
||||
iommu_add_device_t add_device;
|
||||
iommu_remove_device_t remove_device;
|
||||
iommu_invalidate_tlb_t invalidate_tlb;
|
||||
};
|
||||
|
||||
extern struct iommu_ops iommu_ops_intel;
|
||||
@ -58,10 +63,13 @@ extern struct iommu_ops iommu_ops_amd;
|
||||
|
||||
void iommu_init(void);
|
||||
void iommu_cleanup(void);
|
||||
void *iommu_host_domain(void);
|
||||
void *iommu_create_domain(vm_paddr_t maxaddr);
|
||||
void iommu_destroy_domain(void *dom);
|
||||
void iommu_create_mapping(void *dom, vm_paddr_t gpa, vm_paddr_t hpa,
|
||||
size_t len);
|
||||
void iommu_remove_mapping(void *dom, vm_paddr_t gpa, size_t len);
|
||||
void iommu_add_device(void *dom, int bus, int slot, int func);
|
||||
void iommu_remove_device(void *dom, int bus, int slot, int func);
|
||||
void iommu_invalidate_tlb(void *domain);
|
||||
#endif
|
||||
|
@ -280,6 +280,9 @@ vm_free_mem_seg(struct vm *vm, struct vm_memory_segment *seg)
|
||||
{
|
||||
size_t len;
|
||||
vm_paddr_t hpa;
|
||||
void *host_domain;
|
||||
|
||||
host_domain = iommu_host_domain();
|
||||
|
||||
len = 0;
|
||||
while (len < seg->len) {
|
||||
@ -289,11 +292,24 @@ vm_free_mem_seg(struct vm *vm, struct vm_memory_segment *seg)
|
||||
"associated with gpa 0x%016lx", seg->gpa + len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the 'gpa' to 'hpa' mapping in VMs domain.
|
||||
* And resurrect the 1:1 mapping for 'hpa' in 'host_domain'.
|
||||
*/
|
||||
iommu_remove_mapping(vm->iommu, seg->gpa + len, PAGE_SIZE);
|
||||
iommu_create_mapping(host_domain, hpa, hpa, PAGE_SIZE);
|
||||
|
||||
vmm_mem_free(hpa, PAGE_SIZE);
|
||||
|
||||
len += PAGE_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate cached translations associated with 'vm->iommu' since
|
||||
* we have now moved some pages from it.
|
||||
*/
|
||||
iommu_invalidate_tlb(vm->iommu);
|
||||
|
||||
bzero(seg, sizeof(struct vm_memory_segment));
|
||||
}
|
||||
|
||||
@ -371,6 +387,7 @@ vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len)
|
||||
int error, available, allocated;
|
||||
struct vm_memory_segment *seg;
|
||||
vm_paddr_t g, hpa;
|
||||
void *host_domain;
|
||||
|
||||
const boolean_t spok = TRUE; /* superpage mappings are ok */
|
||||
|
||||
@ -405,8 +422,11 @@ vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len)
|
||||
if (vm->num_mem_segs >= VM_MAX_MEMORY_SEGMENTS)
|
||||
return (E2BIG);
|
||||
|
||||
host_domain = iommu_host_domain();
|
||||
|
||||
seg = &vm->mem_segs[vm->num_mem_segs];
|
||||
|
||||
error = 0;
|
||||
seg->gpa = gpa;
|
||||
seg->len = 0;
|
||||
while (seg->len < len) {
|
||||
@ -421,16 +441,27 @@ vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len)
|
||||
if (error)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Remove the 1:1 mapping for 'hpa' from the 'host_domain'.
|
||||
* Add mapping for 'gpa + seg->len' to 'hpa' in the VMs domain.
|
||||
*/
|
||||
iommu_remove_mapping(host_domain, hpa, PAGE_SIZE);
|
||||
iommu_create_mapping(vm->iommu, gpa + seg->len, hpa, PAGE_SIZE);
|
||||
|
||||
seg->len += PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (seg->len != len) {
|
||||
if (error) {
|
||||
vm_free_mem_seg(vm, seg);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate cached translations associated with 'host_domain' since
|
||||
* we have now moved some pages from it.
|
||||
*/
|
||||
iommu_invalidate_tlb(host_domain);
|
||||
|
||||
vm->num_mem_segs++;
|
||||
|
||||
return (0);
|
||||
|
@ -471,24 +471,6 @@ sysctl_vmm_create(SYSCTL_HANDLER_ARGS)
|
||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW,
|
||||
NULL, 0, sysctl_vmm_create, "A", NULL);
|
||||
|
||||
static int
|
||||
sysctl_vmm_mem_total(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
size_t val = vmm_mem_get_mem_total();
|
||||
return sysctl_handle_long(oidp, &val, 0, req);
|
||||
}
|
||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, mem_total, CTLTYPE_LONG | CTLFLAG_RD,
|
||||
0, 0, sysctl_vmm_mem_total, "LU", "Amount of Total memory");
|
||||
|
||||
static int
|
||||
sysctl_vmm_mem_free(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
size_t val = vmm_mem_get_mem_free();
|
||||
return sysctl_handle_long(oidp, &val, 0, req);
|
||||
}
|
||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, mem_free, CTLTYPE_LONG | CTLFLAG_RD,
|
||||
0, 0, sysctl_vmm_mem_free, "LU", "Amount of Free memory");
|
||||
|
||||
void
|
||||
vmmdev_init(void)
|
||||
{
|
||||
|
@ -36,9 +36,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <vm/vm_page.h>
|
||||
#include <vm/vm_pageout.h>
|
||||
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/metadata.h>
|
||||
@ -49,265 +52,21 @@ __FBSDID("$FreeBSD$");
|
||||
#include "vmm_util.h"
|
||||
#include "vmm_mem.h"
|
||||
|
||||
static MALLOC_DEFINE(M_VMM_MEM, "vmm memory", "vmm memory");
|
||||
SYSCTL_DECL(_hw_vmm);
|
||||
|
||||
#define MB (1024 * 1024)
|
||||
#define GB (1024 * MB)
|
||||
|
||||
#define VMM_MEM_MAXSEGS 64
|
||||
|
||||
/* protected by vmm_mem_mtx */
|
||||
static struct {
|
||||
vm_paddr_t base;
|
||||
vm_size_t length;
|
||||
} vmm_mem_avail[VMM_MEM_MAXSEGS];
|
||||
|
||||
static int vmm_mem_nsegs;
|
||||
size_t vmm_mem_total_bytes;
|
||||
|
||||
static vm_paddr_t maxaddr;
|
||||
|
||||
static struct mtx vmm_mem_mtx;
|
||||
|
||||
/*
|
||||
* Steal any memory that was deliberately hidden from FreeBSD either by
|
||||
* the use of MAXMEM kernel config option or the hw.physmem loader tunable.
|
||||
*/
|
||||
static int
|
||||
vmm_mem_steal_memory(void)
|
||||
{
|
||||
int nsegs;
|
||||
caddr_t kmdp;
|
||||
uint32_t smapsize;
|
||||
uint64_t base, length;
|
||||
struct bios_smap *smapbase, *smap, *smapend;
|
||||
|
||||
/*
|
||||
* Borrowed from hammer_time() and getmemsize() in machdep.c
|
||||
*/
|
||||
kmdp = preload_search_by_type("elf kernel");
|
||||
if (kmdp == NULL)
|
||||
kmdp = preload_search_by_type("elf64 kernel");
|
||||
|
||||
smapbase = (struct bios_smap *)preload_search_info(kmdp,
|
||||
MODINFO_METADATA | MODINFOMD_SMAP);
|
||||
if (smapbase == NULL)
|
||||
panic("No BIOS smap info from loader!");
|
||||
|
||||
smapsize = *((uint32_t *)smapbase - 1);
|
||||
smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
|
||||
|
||||
vmm_mem_total_bytes = 0;
|
||||
nsegs = 0;
|
||||
for (smap = smapbase; smap < smapend; smap++) {
|
||||
/*
|
||||
* XXX
|
||||
* Assuming non-overlapping, monotonically increasing
|
||||
* memory segments.
|
||||
*/
|
||||
if (smap->type != SMAP_TYPE_MEMORY)
|
||||
continue;
|
||||
if (smap->length == 0)
|
||||
break;
|
||||
|
||||
base = roundup(smap->base, NBPDR);
|
||||
length = rounddown(smap->length, NBPDR);
|
||||
|
||||
/* Skip this segment if FreeBSD is using all of it. */
|
||||
if (base + length <= ptoa(Maxmem))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If FreeBSD is using part of this segment then adjust
|
||||
* 'base' and 'length' accordingly.
|
||||
*/
|
||||
if (base < ptoa(Maxmem)) {
|
||||
uint64_t used;
|
||||
used = roundup(ptoa(Maxmem), NBPDR) - base;
|
||||
base += used;
|
||||
length -= used;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
continue;
|
||||
|
||||
vmm_mem_avail[nsegs].base = base;
|
||||
vmm_mem_avail[nsegs].length = length;
|
||||
vmm_mem_total_bytes += length;
|
||||
|
||||
if (base + length > maxaddr)
|
||||
maxaddr = base + length;
|
||||
|
||||
if (0 && bootverbose) {
|
||||
printf("vmm_mem_populate: index %d, base 0x%0lx, "
|
||||
"length %ld\n",
|
||||
nsegs, vmm_mem_avail[nsegs].base,
|
||||
vmm_mem_avail[nsegs].length);
|
||||
}
|
||||
|
||||
nsegs++;
|
||||
if (nsegs >= VMM_MEM_MAXSEGS) {
|
||||
printf("vmm_mem_populate: maximum number of vmm memory "
|
||||
"segments reached!\n");
|
||||
return (ENOSPC);
|
||||
}
|
||||
}
|
||||
|
||||
vmm_mem_nsegs = nsegs;
|
||||
|
||||
return (0);
|
||||
}
|
||||
static u_long pages_allocated;
|
||||
SYSCTL_ULONG(_hw_vmm, OID_AUTO, pages_allocated, CTLFLAG_RD,
|
||||
&pages_allocated, 0, "4KB pages allocated");
|
||||
|
||||
static void
|
||||
vmm_mem_direct_map(vm_paddr_t start, vm_paddr_t end)
|
||||
update_pages_allocated(int howmany)
|
||||
{
|
||||
vm_paddr_t addr, remaining;
|
||||
int pdpi, pdi, superpage_size;
|
||||
pml4_entry_t *pml4p;
|
||||
pdp_entry_t *pdp;
|
||||
pd_entry_t *pd;
|
||||
uint64_t page_attr_bits;
|
||||
|
||||
if (end >= NBPML4)
|
||||
panic("Cannot map memory beyond %ldGB", NBPML4 / GB);
|
||||
|
||||
if (vmm_supports_1G_pages())
|
||||
superpage_size = NBPDP;
|
||||
else
|
||||
superpage_size = NBPDR;
|
||||
|
||||
/*
|
||||
* Get the page directory pointer page that contains the direct
|
||||
* map address mappings.
|
||||
*/
|
||||
pml4p = kernel_pmap->pm_pml4;
|
||||
pdp = (pdp_entry_t *)PHYS_TO_DMAP(pml4p[DMPML4I] & ~PAGE_MASK);
|
||||
|
||||
page_attr_bits = PG_RW | PG_V | PG_PS | PG_G;
|
||||
addr = start;
|
||||
while (addr < end) {
|
||||
remaining = end - addr;
|
||||
pdpi = addr / NBPDP;
|
||||
if (superpage_size == NBPDP &&
|
||||
remaining >= NBPDP &&
|
||||
addr % NBPDP == 0) {
|
||||
/*
|
||||
* If there isn't a mapping for this address then
|
||||
* create one but if there is one already make sure
|
||||
* it matches what we expect it to be.
|
||||
*/
|
||||
if (pdp[pdpi] == 0) {
|
||||
pdp[pdpi] = addr | page_attr_bits;
|
||||
if (0 && bootverbose) {
|
||||
printf("vmm_mem_populate: mapping "
|
||||
"0x%lx with 1GB page at "
|
||||
"pdpi %d\n", addr, pdpi);
|
||||
}
|
||||
} else {
|
||||
pdp_entry_t pdpe = pdp[pdpi];
|
||||
if ((pdpe & ~PAGE_MASK) != addr ||
|
||||
(pdpe & page_attr_bits) != page_attr_bits) {
|
||||
panic("An invalid mapping 0x%016lx "
|
||||
"already exists for 0x%016lx\n",
|
||||
pdpe, addr);
|
||||
}
|
||||
}
|
||||
addr += NBPDP;
|
||||
} else {
|
||||
if (remaining < NBPDR) {
|
||||
panic("vmm_mem_populate: remaining (%ld) must "
|
||||
"be greater than NBPDR (%d)\n",
|
||||
remaining, NBPDR);
|
||||
}
|
||||
if (pdp[pdpi] == 0) {
|
||||
/*
|
||||
* XXX we lose this memory forever because
|
||||
* we do not keep track of the virtual address
|
||||
* that would be required to free this page.
|
||||
*/
|
||||
pd = malloc(PAGE_SIZE, M_VMM_MEM,
|
||||
M_WAITOK | M_ZERO);
|
||||
if ((uintptr_t)pd & PAGE_MASK) {
|
||||
panic("vmm_mem_populate: page directory"
|
||||
"page not aligned on %d "
|
||||
"boundary\n", PAGE_SIZE);
|
||||
}
|
||||
pdp[pdpi] = vtophys(pd);
|
||||
pdp[pdpi] |= PG_RW | PG_V | PG_U;
|
||||
if (0 && bootverbose) {
|
||||
printf("Creating page directory "
|
||||
"at pdp index %d for 0x%016lx\n",
|
||||
pdpi, addr);
|
||||
}
|
||||
}
|
||||
pdi = (addr % NBPDP) / NBPDR;
|
||||
pd = (pd_entry_t *)PHYS_TO_DMAP(pdp[pdpi] & ~PAGE_MASK);
|
||||
|
||||
/*
|
||||
* Create a new mapping if one doesn't already exist
|
||||
* or validate it if it does.
|
||||
*/
|
||||
if (pd[pdi] == 0) {
|
||||
pd[pdi] = addr | page_attr_bits;
|
||||
if (0 && bootverbose) {
|
||||
printf("vmm_mem_populate: mapping "
|
||||
"0x%lx with 2MB page at "
|
||||
"pdpi %d, pdi %d\n",
|
||||
addr, pdpi, pdi);
|
||||
}
|
||||
} else {
|
||||
pd_entry_t pde = pd[pdi];
|
||||
if ((pde & ~PAGE_MASK) != addr ||
|
||||
(pde & page_attr_bits) != page_attr_bits) {
|
||||
panic("An invalid mapping 0x%016lx "
|
||||
"already exists for 0x%016lx\n",
|
||||
pde, addr);
|
||||
}
|
||||
}
|
||||
addr += NBPDR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vmm_mem_populate(void)
|
||||
{
|
||||
int seg, error;
|
||||
vm_paddr_t start, end;
|
||||
|
||||
/* populate the vmm_mem_avail[] array */
|
||||
error = vmm_mem_steal_memory();
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Now map the memory that was hidden from FreeBSD in
|
||||
* the direct map VA space.
|
||||
*/
|
||||
for (seg = 0; seg < vmm_mem_nsegs; seg++) {
|
||||
start = vmm_mem_avail[seg].base;
|
||||
end = start + vmm_mem_avail[seg].length;
|
||||
if ((start & PDRMASK) != 0 || (end & PDRMASK) != 0) {
|
||||
panic("start (0x%016lx) and end (0x%016lx) must be "
|
||||
"aligned on a %dMB boundary\n",
|
||||
start, end, NBPDR / MB);
|
||||
}
|
||||
vmm_mem_direct_map(start, end);
|
||||
}
|
||||
|
||||
return (0);
|
||||
pages_allocated += howmany; /* XXX locking? */
|
||||
}
|
||||
|
||||
int
|
||||
vmm_mem_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
mtx_init(&vmm_mem_mtx, "vmm_mem_mtx", NULL, MTX_DEF);
|
||||
|
||||
error = vmm_mem_populate();
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -315,122 +74,61 @@ vmm_mem_init(void)
|
||||
vm_paddr_t
|
||||
vmm_mem_alloc(size_t size)
|
||||
{
|
||||
int i;
|
||||
vm_paddr_t addr;
|
||||
int flags;
|
||||
vm_page_t m;
|
||||
vm_paddr_t pa;
|
||||
|
||||
if ((size & PAGE_MASK) != 0) {
|
||||
panic("vmm_mem_alloc: size 0x%0lx must be "
|
||||
"aligned on a 0x%0x boundary\n", size, PAGE_SIZE);
|
||||
}
|
||||
if (size != PAGE_SIZE)
|
||||
panic("vmm_mem_alloc: invalid allocation size %lu", size);
|
||||
|
||||
addr = 0;
|
||||
flags = VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED |
|
||||
VM_ALLOC_ZERO;
|
||||
|
||||
mtx_lock(&vmm_mem_mtx);
|
||||
for (i = 0; i < vmm_mem_nsegs; i++) {
|
||||
if (vmm_mem_avail[i].length >= size) {
|
||||
addr = vmm_mem_avail[i].base;
|
||||
vmm_mem_avail[i].base += size;
|
||||
vmm_mem_avail[i].length -= size;
|
||||
/* remove a zero length segment */
|
||||
if (vmm_mem_avail[i].length == 0) {
|
||||
memmove(&vmm_mem_avail[i],
|
||||
&vmm_mem_avail[i + 1],
|
||||
(vmm_mem_nsegs - (i + 1)) *
|
||||
sizeof(vmm_mem_avail[0]));
|
||||
vmm_mem_nsegs--;
|
||||
}
|
||||
while (1) {
|
||||
/*
|
||||
* XXX need policy to determine when to back off the allocation
|
||||
*/
|
||||
m = vm_page_alloc(NULL, 0, flags);
|
||||
if (m == NULL)
|
||||
VM_WAIT;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&vmm_mem_mtx);
|
||||
|
||||
return (addr);
|
||||
}
|
||||
pa = VM_PAGE_TO_PHYS(m);
|
||||
|
||||
if ((m->flags & PG_ZERO) == 0)
|
||||
pagezero((void *)PHYS_TO_DMAP(pa));
|
||||
|
||||
size_t
|
||||
vmm_mem_get_mem_total(void)
|
||||
{
|
||||
return vmm_mem_total_bytes;
|
||||
}
|
||||
update_pages_allocated(1);
|
||||
|
||||
size_t
|
||||
vmm_mem_get_mem_free(void)
|
||||
{
|
||||
size_t length = 0;
|
||||
int i;
|
||||
|
||||
mtx_lock(&vmm_mem_mtx);
|
||||
for (i = 0; i < vmm_mem_nsegs; i++) {
|
||||
length += vmm_mem_avail[i].length;
|
||||
}
|
||||
mtx_unlock(&vmm_mem_mtx);
|
||||
|
||||
return(length);
|
||||
return (pa);
|
||||
}
|
||||
|
||||
void
|
||||
vmm_mem_free(vm_paddr_t base, size_t length)
|
||||
{
|
||||
int i;
|
||||
vm_page_t m;
|
||||
|
||||
if ((base & PAGE_MASK) != 0 || (length & PAGE_MASK) != 0) {
|
||||
panic("vmm_mem_free: base 0x%0lx and length 0x%0lx must be "
|
||||
"aligned on a 0x%0x boundary\n", base, length, PAGE_SIZE);
|
||||
if (base & PAGE_MASK) {
|
||||
panic("vmm_mem_free: base 0x%0lx must be aligned on a "
|
||||
"0x%0x boundary\n", base, PAGE_SIZE);
|
||||
}
|
||||
|
||||
mtx_lock(&vmm_mem_mtx);
|
||||
if (length != PAGE_SIZE)
|
||||
panic("vmm_mem_free: invalid length %lu", length);
|
||||
|
||||
for (i = 0; i < vmm_mem_nsegs; i++) {
|
||||
if (vmm_mem_avail[i].base > base)
|
||||
break;
|
||||
}
|
||||
m = PHYS_TO_VM_PAGE(base);
|
||||
m->wire_count--;
|
||||
vm_page_free(m);
|
||||
atomic_subtract_int(&cnt.v_wire_count, 1);
|
||||
|
||||
if (vmm_mem_nsegs >= VMM_MEM_MAXSEGS)
|
||||
panic("vmm_mem_free: cannot free any more segments");
|
||||
|
||||
/* Create a new segment at index 'i' */
|
||||
memmove(&vmm_mem_avail[i + 1], &vmm_mem_avail[i],
|
||||
(vmm_mem_nsegs - i) * sizeof(vmm_mem_avail[0]));
|
||||
|
||||
vmm_mem_avail[i].base = base;
|
||||
vmm_mem_avail[i].length = length;
|
||||
|
||||
vmm_mem_nsegs++;
|
||||
|
||||
coalesce_some_more:
|
||||
for (i = 0; i < vmm_mem_nsegs - 1; i++) {
|
||||
if (vmm_mem_avail[i].base + vmm_mem_avail[i].length ==
|
||||
vmm_mem_avail[i + 1].base) {
|
||||
vmm_mem_avail[i].length += vmm_mem_avail[i + 1].length;
|
||||
memmove(&vmm_mem_avail[i + 1], &vmm_mem_avail[i + 2],
|
||||
(vmm_mem_nsegs - (i + 2)) * sizeof(vmm_mem_avail[0]));
|
||||
vmm_mem_nsegs--;
|
||||
goto coalesce_some_more;
|
||||
}
|
||||
}
|
||||
|
||||
mtx_unlock(&vmm_mem_mtx);
|
||||
update_pages_allocated(-1);
|
||||
}
|
||||
|
||||
vm_paddr_t
|
||||
vmm_mem_maxaddr(void)
|
||||
{
|
||||
|
||||
return (maxaddr);
|
||||
}
|
||||
|
||||
void
|
||||
vmm_mem_dump(void)
|
||||
{
|
||||
int i;
|
||||
vm_paddr_t base;
|
||||
vm_size_t length;
|
||||
|
||||
mtx_lock(&vmm_mem_mtx);
|
||||
for (i = 0; i < vmm_mem_nsegs; i++) {
|
||||
base = vmm_mem_avail[i].base;
|
||||
length = vmm_mem_avail[i].length;
|
||||
printf("%-4d0x%016lx 0x%016lx\n", i, base, base + length);
|
||||
}
|
||||
mtx_unlock(&vmm_mem_mtx);
|
||||
return (ptoa(Maxmem));
|
||||
}
|
||||
|
@ -33,9 +33,5 @@ int vmm_mem_init(void);
|
||||
vm_paddr_t vmm_mem_alloc(size_t size);
|
||||
void vmm_mem_free(vm_paddr_t start, size_t size);
|
||||
vm_paddr_t vmm_mem_maxaddr(void);
|
||||
void vmm_mem_dump(void);
|
||||
|
||||
size_t vmm_mem_get_mem_total(void);
|
||||
size_t vmm_mem_get_mem_free(void);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user