1efd8e5aaSPaolo Bonzini /* 2efd8e5aaSPaolo Bonzini * Copyright (C) 2012, 2017, Red Hat Inc. 3efd8e5aaSPaolo Bonzini * 4efd8e5aaSPaolo Bonzini * This allocator provides contiguous physical addresses with page 5efd8e5aaSPaolo Bonzini * granularity. 6efd8e5aaSPaolo Bonzini */ 7efd8e5aaSPaolo Bonzini 8efd8e5aaSPaolo Bonzini #include "libcflat.h" 9efd8e5aaSPaolo Bonzini #include "asm/spinlock.h" 10efd8e5aaSPaolo Bonzini #include "asm/page.h" 11937e2392SPaolo Bonzini #include "asm/io.h" 12dcda215bSPaolo Bonzini #include "alloc.h" 13937e2392SPaolo Bonzini #include "alloc_phys.h" 14937e2392SPaolo Bonzini #include "alloc_page.h" 150d622fcbSClaudio Imbrenda #include <bitops.h> 16937e2392SPaolo Bonzini #include "vmalloc.h" 17efd8e5aaSPaolo Bonzini 183f6fee0dSClaudio Imbrenda #define VM_MAGIC 0x7E57C0DE 193f6fee0dSClaudio Imbrenda 203f6fee0dSClaudio Imbrenda #define GET_METADATA(x) (((struct metadata *)(x)) - 1) 213f6fee0dSClaudio Imbrenda #define GET_MAGIC(x) (*((unsigned long *)(x) - 1)) 223f6fee0dSClaudio Imbrenda 233f6fee0dSClaudio Imbrenda struct metadata { 243f6fee0dSClaudio Imbrenda unsigned long npages; 253f6fee0dSClaudio Imbrenda unsigned long magic; 263f6fee0dSClaudio Imbrenda }; 273f6fee0dSClaudio Imbrenda 28efd8e5aaSPaolo Bonzini static struct spinlock lock; 29efd8e5aaSPaolo Bonzini static void *vfree_top = 0; 30937e2392SPaolo Bonzini static void *page_root; 31efd8e5aaSPaolo Bonzini 320d622fcbSClaudio Imbrenda /* 330d622fcbSClaudio Imbrenda * Allocate a certain number of pages from the virtual address space (without 340d622fcbSClaudio Imbrenda * physical backing). 350d622fcbSClaudio Imbrenda * 360d622fcbSClaudio Imbrenda * nr is the number of pages to allocate 370d622fcbSClaudio Imbrenda * alignment_pages is the alignment of the allocation *in pages* 383f6fee0dSClaudio Imbrenda * metadata indicates whether an extra (unaligned) page needs to be allocated 393f6fee0dSClaudio Imbrenda * right before the main (aligned) allocation. 403f6fee0dSClaudio Imbrenda * 413f6fee0dSClaudio Imbrenda * The return value points to the first allocated virtual page, which will 423f6fee0dSClaudio Imbrenda * be the (potentially unaligned) metadata page if the metadata flag is 433f6fee0dSClaudio Imbrenda * specified. 440d622fcbSClaudio Imbrenda */ 453f6fee0dSClaudio Imbrenda static void *do_alloc_vpages(ulong nr, unsigned int align_order, bool metadata) 46efd8e5aaSPaolo Bonzini { 474aabe7c0SClaudio Imbrenda uintptr_t ptr; 484aabe7c0SClaudio Imbrenda 49efd8e5aaSPaolo Bonzini spin_lock(&lock); 504aabe7c0SClaudio Imbrenda ptr = (uintptr_t)vfree_top; 514aabe7c0SClaudio Imbrenda ptr -= PAGE_SIZE * nr; 520d622fcbSClaudio Imbrenda ptr &= GENMASK_ULL(63, PAGE_SHIFT + align_order); 533f6fee0dSClaudio Imbrenda if (metadata) 543f6fee0dSClaudio Imbrenda ptr -= PAGE_SIZE; 554aabe7c0SClaudio Imbrenda vfree_top = (void *)ptr; 56efd8e5aaSPaolo Bonzini spin_unlock(&lock); 574aabe7c0SClaudio Imbrenda 584aabe7c0SClaudio Imbrenda /* Cannot return vfree_top here, we are outside the lock! */ 594aabe7c0SClaudio Imbrenda return (void *)ptr; 60efd8e5aaSPaolo Bonzini } 61efd8e5aaSPaolo Bonzini 623f6fee0dSClaudio Imbrenda void *alloc_vpages_aligned(ulong nr, unsigned int align_order) 633f6fee0dSClaudio Imbrenda { 643f6fee0dSClaudio Imbrenda return do_alloc_vpages(nr, align_order, false); 653f6fee0dSClaudio Imbrenda } 663f6fee0dSClaudio Imbrenda 670d622fcbSClaudio Imbrenda void *alloc_vpages(ulong nr) 680d622fcbSClaudio Imbrenda { 690d622fcbSClaudio Imbrenda return alloc_vpages_aligned(nr, 0); 700d622fcbSClaudio Imbrenda } 710d622fcbSClaudio Imbrenda 72efd8e5aaSPaolo Bonzini void *alloc_vpage(void) 73efd8e5aaSPaolo Bonzini { 74efd8e5aaSPaolo Bonzini return alloc_vpages(1); 75efd8e5aaSPaolo Bonzini } 76efd8e5aaSPaolo Bonzini 77dcda215bSPaolo Bonzini void *vmap(phys_addr_t phys, size_t size) 78dcda215bSPaolo Bonzini { 79dcda215bSPaolo Bonzini void *mem, *p; 803874bb46SClaudio Imbrenda size_t pages; 81dcda215bSPaolo Bonzini 826ea7326aSClaudio Imbrenda size = PAGE_ALIGN(size); 83dcda215bSPaolo Bonzini pages = size / PAGE_SIZE; 84dcda215bSPaolo Bonzini mem = p = alloc_vpages(pages); 85dcda215bSPaolo Bonzini 86dcda215bSPaolo Bonzini phys &= ~(unsigned long long)(PAGE_SIZE - 1); 87dcda215bSPaolo Bonzini while (pages--) { 88dcda215bSPaolo Bonzini install_page(page_root, phys, p); 89dcda215bSPaolo Bonzini phys += PAGE_SIZE; 90dcda215bSPaolo Bonzini p += PAGE_SIZE; 91dcda215bSPaolo Bonzini } 92dcda215bSPaolo Bonzini return mem; 93dcda215bSPaolo Bonzini } 94dcda215bSPaolo Bonzini 950d622fcbSClaudio Imbrenda /* 963f6fee0dSClaudio Imbrenda * Allocate one page, for an object with specified alignment. 973f6fee0dSClaudio Imbrenda * The resulting pointer will be aligned to the required alignment, but 983f6fee0dSClaudio Imbrenda * intentionally not page-aligned. 993f6fee0dSClaudio Imbrenda * The metadata for single pages allocation is just the magic value, 1003f6fee0dSClaudio Imbrenda * which is placed right before the pointer, like for bigger allocations. 1013f6fee0dSClaudio Imbrenda */ 1023f6fee0dSClaudio Imbrenda static void *vm_alloc_one_page(size_t alignment) 1033f6fee0dSClaudio Imbrenda { 1043f6fee0dSClaudio Imbrenda void *p; 1053f6fee0dSClaudio Imbrenda 1063f6fee0dSClaudio Imbrenda /* this guarantees that there will be space for the magic value */ 1073f6fee0dSClaudio Imbrenda assert(alignment >= sizeof(uintptr_t)); 1083f6fee0dSClaudio Imbrenda assert(alignment < PAGE_SIZE); 1093f6fee0dSClaudio Imbrenda p = alloc_vpage(); 1103f6fee0dSClaudio Imbrenda install_page(page_root, virt_to_phys(alloc_page()), p); 1113f6fee0dSClaudio Imbrenda p = (void *)((uintptr_t)p + alignment); 1123f6fee0dSClaudio Imbrenda /* write the magic value right before the returned address */ 1133f6fee0dSClaudio Imbrenda GET_MAGIC(p) = VM_MAGIC; 1143f6fee0dSClaudio Imbrenda return p; 1153f6fee0dSClaudio Imbrenda } 1163f6fee0dSClaudio Imbrenda 1173f6fee0dSClaudio Imbrenda /* 1180d622fcbSClaudio Imbrenda * Allocate virtual memory, with the specified minimum alignment. 1193f6fee0dSClaudio Imbrenda * If the allocation fits in one page, only one page is allocated. Otherwise 1203f6fee0dSClaudio Imbrenda * enough pages are allocated for the object, plus one to keep metadata 1213f6fee0dSClaudio Imbrenda * information about the allocation. 1220d622fcbSClaudio Imbrenda */ 123dcda215bSPaolo Bonzini static void *vm_memalign(size_t alignment, size_t size) 124dcda215bSPaolo Bonzini { 1253f6fee0dSClaudio Imbrenda struct metadata *m; 1260d622fcbSClaudio Imbrenda phys_addr_t pa; 1273f6fee0dSClaudio Imbrenda uintptr_t p; 1283f6fee0dSClaudio Imbrenda void *mem; 1293f6fee0dSClaudio Imbrenda size_t i; 130dcda215bSPaolo Bonzini 1313f6fee0dSClaudio Imbrenda if (!size) 1323f6fee0dSClaudio Imbrenda return NULL; 1330d622fcbSClaudio Imbrenda assert(is_power_of_2(alignment)); 1340d622fcbSClaudio Imbrenda 1353f6fee0dSClaudio Imbrenda if (alignment < sizeof(uintptr_t)) 1363f6fee0dSClaudio Imbrenda alignment = sizeof(uintptr_t); 1373f6fee0dSClaudio Imbrenda /* it fits in one page, allocate only one page */ 1383f6fee0dSClaudio Imbrenda if (alignment + size <= PAGE_SIZE) 1393f6fee0dSClaudio Imbrenda return vm_alloc_one_page(alignment); 1400d622fcbSClaudio Imbrenda size = PAGE_ALIGN(size) / PAGE_SIZE; 1410d622fcbSClaudio Imbrenda alignment = get_order(PAGE_ALIGN(alignment) / PAGE_SIZE); 1423f6fee0dSClaudio Imbrenda mem = do_alloc_vpages(size, alignment, true); 1433f6fee0dSClaudio Imbrenda p = (uintptr_t)mem; 1443f6fee0dSClaudio Imbrenda /* skip the metadata page */ 1453f6fee0dSClaudio Imbrenda mem = (void *)(p + PAGE_SIZE); 1463f6fee0dSClaudio Imbrenda /* 1473f6fee0dSClaudio Imbrenda * time to actually allocate the physical pages to back our virtual 1483f6fee0dSClaudio Imbrenda * allocation; note that we need to allocate one extra page (for the 1493f6fee0dSClaudio Imbrenda * metadata), hence the <= 1503f6fee0dSClaudio Imbrenda */ 1513f6fee0dSClaudio Imbrenda for (i = 0; i <= size; i++, p += PAGE_SIZE) { 1520d622fcbSClaudio Imbrenda pa = virt_to_phys(alloc_page()); 1530d622fcbSClaudio Imbrenda assert(pa); 1543f6fee0dSClaudio Imbrenda install_page(page_root, pa, (void *)p); 155dcda215bSPaolo Bonzini } 1563f6fee0dSClaudio Imbrenda m = GET_METADATA(mem); 1573f6fee0dSClaudio Imbrenda m->npages = size; 1583f6fee0dSClaudio Imbrenda m->magic = VM_MAGIC; 159dcda215bSPaolo Bonzini return mem; 160dcda215bSPaolo Bonzini } 161dcda215bSPaolo Bonzini 162f90ddba3SClaudio Imbrenda static void vm_free(void *mem) 163dcda215bSPaolo Bonzini { 1643f6fee0dSClaudio Imbrenda struct metadata *m; 16512513346SClaudio Imbrenda uintptr_t ptr, page, i; 1663f6fee0dSClaudio Imbrenda 16712513346SClaudio Imbrenda if (!mem) 16812513346SClaudio Imbrenda return; 1693f6fee0dSClaudio Imbrenda /* the pointer is not page-aligned, it was a single-page allocation */ 1703f6fee0dSClaudio Imbrenda if (!IS_ALIGNED((uintptr_t)mem, PAGE_SIZE)) { 1713f6fee0dSClaudio Imbrenda assert(GET_MAGIC(mem) == VM_MAGIC); 17212513346SClaudio Imbrenda page = virt_to_pte_phys(page_root, mem) & PAGE_MASK; 17312513346SClaudio Imbrenda assert(page); 17412513346SClaudio Imbrenda free_page(phys_to_virt(page)); 1753f6fee0dSClaudio Imbrenda return; 176dcda215bSPaolo Bonzini } 1773f6fee0dSClaudio Imbrenda 1783f6fee0dSClaudio Imbrenda /* the pointer is page-aligned, it was a multi-page allocation */ 1793f6fee0dSClaudio Imbrenda m = GET_METADATA(mem); 1803f6fee0dSClaudio Imbrenda assert(m->magic == VM_MAGIC); 1813f6fee0dSClaudio Imbrenda assert(m->npages > 0); 18212513346SClaudio Imbrenda assert(m->npages < BIT_ULL(BITS_PER_LONG - PAGE_SHIFT)); 1833f6fee0dSClaudio Imbrenda /* free all the pages including the metadata page */ 18412513346SClaudio Imbrenda ptr = (uintptr_t)m & PAGE_MASK; 18512513346SClaudio Imbrenda for (i = 0 ; i < m->npages + 1; i++, ptr += PAGE_SIZE) { 18612513346SClaudio Imbrenda page = virt_to_pte_phys(page_root, (void *)ptr) & PAGE_MASK; 18712513346SClaudio Imbrenda assert(page); 18812513346SClaudio Imbrenda free_page(phys_to_virt(page)); 18912513346SClaudio Imbrenda } 190dcda215bSPaolo Bonzini } 191dcda215bSPaolo Bonzini 192dcda215bSPaolo Bonzini static struct alloc_ops vmalloc_ops = { 193dcda215bSPaolo Bonzini .memalign = vm_memalign, 194dcda215bSPaolo Bonzini .free = vm_free, 195dcda215bSPaolo Bonzini }; 196dcda215bSPaolo Bonzini 19748a0145fSPaolo Bonzini void __attribute__((__weak__)) find_highmem(void) 19848a0145fSPaolo Bonzini { 19948a0145fSPaolo Bonzini } 20048a0145fSPaolo Bonzini 20117b9f93eSClaudio Imbrenda void init_alloc_vpage(void *top) 20217b9f93eSClaudio Imbrenda { 20317b9f93eSClaudio Imbrenda spin_lock(&lock); 20417b9f93eSClaudio Imbrenda assert(alloc_ops != &vmalloc_ops); 20517b9f93eSClaudio Imbrenda vfree_top = top; 20617b9f93eSClaudio Imbrenda spin_unlock(&lock); 20717b9f93eSClaudio Imbrenda } 20817b9f93eSClaudio Imbrenda 209*0a5b31d2SSean Christopherson void __setup_vm(void *opaque) 210937e2392SPaolo Bonzini { 211937e2392SPaolo Bonzini phys_addr_t base, top; 212dcda215bSPaolo Bonzini 213dcda215bSPaolo Bonzini if (alloc_ops == &vmalloc_ops) 214dcda215bSPaolo Bonzini return; 215dcda215bSPaolo Bonzini 216937e2392SPaolo Bonzini phys_alloc_get_unused(&base, &top); 217bf62a925SAndrew Jones assert(base != top || page_alloc_initialized()); 21848a0145fSPaolo Bonzini /* 21948a0145fSPaolo Bonzini * Give low memory immediately to the page allocator, 22048a0145fSPaolo Bonzini * so that it can be used to allocate page tables. 22148a0145fSPaolo Bonzini */ 222bf62a925SAndrew Jones if (!page_alloc_initialized()) { 2238131e91aSClaudio Imbrenda base = PAGE_ALIGN(base) >> PAGE_SHIFT; 2248131e91aSClaudio Imbrenda top = top >> PAGE_SHIFT; 2258131e91aSClaudio Imbrenda page_alloc_init_area(AREA_ANY_NUMBER, base, top); 2268131e91aSClaudio Imbrenda page_alloc_ops_enable(); 227bf62a925SAndrew Jones } 22848a0145fSPaolo Bonzini 22948a0145fSPaolo Bonzini find_highmem(); 23048a0145fSPaolo Bonzini phys_alloc_get_unused(&base, &top); 231*0a5b31d2SSean Christopherson page_root = setup_mmu(top, opaque); 23248a0145fSPaolo Bonzini if (base != top) { 2338131e91aSClaudio Imbrenda base = PAGE_ALIGN(base) >> PAGE_SHIFT; 2348131e91aSClaudio Imbrenda top = top >> PAGE_SHIFT; 2358131e91aSClaudio Imbrenda page_alloc_init_area(AREA_ANY_NUMBER, base, top); 23648a0145fSPaolo Bonzini } 23748a0145fSPaolo Bonzini 23817b9f93eSClaudio Imbrenda spin_lock(&lock); 23917b9f93eSClaudio Imbrenda assert(alloc_ops != &vmalloc_ops); 240dcda215bSPaolo Bonzini alloc_ops = &vmalloc_ops; 24117b9f93eSClaudio Imbrenda spin_unlock(&lock); 242937e2392SPaolo Bonzini } 243*0a5b31d2SSean Christopherson 244*0a5b31d2SSean Christopherson void setup_vm(void) 245*0a5b31d2SSean Christopherson { 246*0a5b31d2SSean Christopherson __setup_vm(NULL); 247*0a5b31d2SSean Christopherson } 248