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 18*3f6fee0dSClaudio Imbrenda #define VM_MAGIC 0x7E57C0DE 19*3f6fee0dSClaudio Imbrenda 20*3f6fee0dSClaudio Imbrenda #define GET_METADATA(x) (((struct metadata *)(x)) - 1) 21*3f6fee0dSClaudio Imbrenda #define GET_MAGIC(x) (*((unsigned long *)(x) - 1)) 22*3f6fee0dSClaudio Imbrenda 23*3f6fee0dSClaudio Imbrenda struct metadata { 24*3f6fee0dSClaudio Imbrenda unsigned long npages; 25*3f6fee0dSClaudio Imbrenda unsigned long magic; 26*3f6fee0dSClaudio Imbrenda }; 27*3f6fee0dSClaudio 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* 38*3f6fee0dSClaudio Imbrenda * metadata indicates whether an extra (unaligned) page needs to be allocated 39*3f6fee0dSClaudio Imbrenda * right before the main (aligned) allocation. 40*3f6fee0dSClaudio Imbrenda * 41*3f6fee0dSClaudio Imbrenda * The return value points to the first allocated virtual page, which will 42*3f6fee0dSClaudio Imbrenda * be the (potentially unaligned) metadata page if the metadata flag is 43*3f6fee0dSClaudio Imbrenda * specified. 440d622fcbSClaudio Imbrenda */ 45*3f6fee0dSClaudio 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); 53*3f6fee0dSClaudio Imbrenda if (metadata) 54*3f6fee0dSClaudio 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 62*3f6fee0dSClaudio Imbrenda void *alloc_vpages_aligned(ulong nr, unsigned int align_order) 63*3f6fee0dSClaudio Imbrenda { 64*3f6fee0dSClaudio Imbrenda return do_alloc_vpages(nr, align_order, false); 65*3f6fee0dSClaudio Imbrenda } 66*3f6fee0dSClaudio 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 /* 96*3f6fee0dSClaudio Imbrenda * Allocate one page, for an object with specified alignment. 97*3f6fee0dSClaudio Imbrenda * The resulting pointer will be aligned to the required alignment, but 98*3f6fee0dSClaudio Imbrenda * intentionally not page-aligned. 99*3f6fee0dSClaudio Imbrenda * The metadata for single pages allocation is just the magic value, 100*3f6fee0dSClaudio Imbrenda * which is placed right before the pointer, like for bigger allocations. 101*3f6fee0dSClaudio Imbrenda */ 102*3f6fee0dSClaudio Imbrenda static void *vm_alloc_one_page(size_t alignment) 103*3f6fee0dSClaudio Imbrenda { 104*3f6fee0dSClaudio Imbrenda void *p; 105*3f6fee0dSClaudio Imbrenda 106*3f6fee0dSClaudio Imbrenda /* this guarantees that there will be space for the magic value */ 107*3f6fee0dSClaudio Imbrenda assert(alignment >= sizeof(uintptr_t)); 108*3f6fee0dSClaudio Imbrenda assert(alignment < PAGE_SIZE); 109*3f6fee0dSClaudio Imbrenda p = alloc_vpage(); 110*3f6fee0dSClaudio Imbrenda install_page(page_root, virt_to_phys(alloc_page()), p); 111*3f6fee0dSClaudio Imbrenda p = (void *)((uintptr_t)p + alignment); 112*3f6fee0dSClaudio Imbrenda /* write the magic value right before the returned address */ 113*3f6fee0dSClaudio Imbrenda GET_MAGIC(p) = VM_MAGIC; 114*3f6fee0dSClaudio Imbrenda return p; 115*3f6fee0dSClaudio Imbrenda } 116*3f6fee0dSClaudio Imbrenda 117*3f6fee0dSClaudio Imbrenda /* 1180d622fcbSClaudio Imbrenda * Allocate virtual memory, with the specified minimum alignment. 119*3f6fee0dSClaudio Imbrenda * If the allocation fits in one page, only one page is allocated. Otherwise 120*3f6fee0dSClaudio Imbrenda * enough pages are allocated for the object, plus one to keep metadata 121*3f6fee0dSClaudio Imbrenda * information about the allocation. 1220d622fcbSClaudio Imbrenda */ 123dcda215bSPaolo Bonzini static void *vm_memalign(size_t alignment, size_t size) 124dcda215bSPaolo Bonzini { 125*3f6fee0dSClaudio Imbrenda struct metadata *m; 1260d622fcbSClaudio Imbrenda phys_addr_t pa; 127*3f6fee0dSClaudio Imbrenda uintptr_t p; 128*3f6fee0dSClaudio Imbrenda void *mem; 129*3f6fee0dSClaudio Imbrenda size_t i; 130dcda215bSPaolo Bonzini 131*3f6fee0dSClaudio Imbrenda if (!size) 132*3f6fee0dSClaudio Imbrenda return NULL; 1330d622fcbSClaudio Imbrenda assert(is_power_of_2(alignment)); 1340d622fcbSClaudio Imbrenda 135*3f6fee0dSClaudio Imbrenda if (alignment < sizeof(uintptr_t)) 136*3f6fee0dSClaudio Imbrenda alignment = sizeof(uintptr_t); 137*3f6fee0dSClaudio Imbrenda /* it fits in one page, allocate only one page */ 138*3f6fee0dSClaudio Imbrenda if (alignment + size <= PAGE_SIZE) 139*3f6fee0dSClaudio 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); 142*3f6fee0dSClaudio Imbrenda mem = do_alloc_vpages(size, alignment, true); 143*3f6fee0dSClaudio Imbrenda p = (uintptr_t)mem; 144*3f6fee0dSClaudio Imbrenda /* skip the metadata page */ 145*3f6fee0dSClaudio Imbrenda mem = (void *)(p + PAGE_SIZE); 146*3f6fee0dSClaudio Imbrenda /* 147*3f6fee0dSClaudio Imbrenda * time to actually allocate the physical pages to back our virtual 148*3f6fee0dSClaudio Imbrenda * allocation; note that we need to allocate one extra page (for the 149*3f6fee0dSClaudio Imbrenda * metadata), hence the <= 150*3f6fee0dSClaudio Imbrenda */ 151*3f6fee0dSClaudio Imbrenda for (i = 0; i <= size; i++, p += PAGE_SIZE) { 1520d622fcbSClaudio Imbrenda pa = virt_to_phys(alloc_page()); 1530d622fcbSClaudio Imbrenda assert(pa); 154*3f6fee0dSClaudio Imbrenda install_page(page_root, pa, (void *)p); 155dcda215bSPaolo Bonzini } 156*3f6fee0dSClaudio Imbrenda m = GET_METADATA(mem); 157*3f6fee0dSClaudio Imbrenda m->npages = size; 158*3f6fee0dSClaudio Imbrenda m->magic = VM_MAGIC; 159dcda215bSPaolo Bonzini return mem; 160dcda215bSPaolo Bonzini } 161dcda215bSPaolo Bonzini 162dcda215bSPaolo Bonzini static void vm_free(void *mem, size_t size) 163dcda215bSPaolo Bonzini { 164*3f6fee0dSClaudio Imbrenda struct metadata *m; 165*3f6fee0dSClaudio Imbrenda uintptr_t ptr, end; 166*3f6fee0dSClaudio Imbrenda 167*3f6fee0dSClaudio Imbrenda /* the pointer is not page-aligned, it was a single-page allocation */ 168*3f6fee0dSClaudio Imbrenda if (!IS_ALIGNED((uintptr_t)mem, PAGE_SIZE)) { 169*3f6fee0dSClaudio Imbrenda assert(GET_MAGIC(mem) == VM_MAGIC); 170*3f6fee0dSClaudio Imbrenda ptr = virt_to_pte_phys(page_root, mem) & PAGE_MASK; 171*3f6fee0dSClaudio Imbrenda free_page(phys_to_virt(ptr)); 172*3f6fee0dSClaudio Imbrenda return; 173dcda215bSPaolo Bonzini } 174*3f6fee0dSClaudio Imbrenda 175*3f6fee0dSClaudio Imbrenda /* the pointer is page-aligned, it was a multi-page allocation */ 176*3f6fee0dSClaudio Imbrenda m = GET_METADATA(mem); 177*3f6fee0dSClaudio Imbrenda assert(m->magic == VM_MAGIC); 178*3f6fee0dSClaudio Imbrenda assert(m->npages > 0); 179*3f6fee0dSClaudio Imbrenda /* free all the pages including the metadata page */ 180*3f6fee0dSClaudio Imbrenda ptr = (uintptr_t)mem - PAGE_SIZE; 181*3f6fee0dSClaudio Imbrenda end = ptr + m->npages * PAGE_SIZE; 182*3f6fee0dSClaudio Imbrenda for ( ; ptr < end; ptr += PAGE_SIZE) 183*3f6fee0dSClaudio Imbrenda free_page(phys_to_virt(virt_to_pte_phys(page_root, (void *)ptr))); 184*3f6fee0dSClaudio Imbrenda /* free the last one separately to avoid overflow issues */ 185*3f6fee0dSClaudio Imbrenda free_page(phys_to_virt(virt_to_pte_phys(page_root, (void *)ptr))); 186dcda215bSPaolo Bonzini } 187dcda215bSPaolo Bonzini 188dcda215bSPaolo Bonzini static struct alloc_ops vmalloc_ops = { 189dcda215bSPaolo Bonzini .memalign = vm_memalign, 190dcda215bSPaolo Bonzini .free = vm_free, 191dcda215bSPaolo Bonzini .align_min = PAGE_SIZE, 192dcda215bSPaolo Bonzini }; 193dcda215bSPaolo Bonzini 19448a0145fSPaolo Bonzini void __attribute__((__weak__)) find_highmem(void) 19548a0145fSPaolo Bonzini { 19648a0145fSPaolo Bonzini } 19748a0145fSPaolo Bonzini 19817b9f93eSClaudio Imbrenda void init_alloc_vpage(void *top) 19917b9f93eSClaudio Imbrenda { 20017b9f93eSClaudio Imbrenda spin_lock(&lock); 20117b9f93eSClaudio Imbrenda assert(alloc_ops != &vmalloc_ops); 20217b9f93eSClaudio Imbrenda vfree_top = top; 20317b9f93eSClaudio Imbrenda spin_unlock(&lock); 20417b9f93eSClaudio Imbrenda } 20517b9f93eSClaudio Imbrenda 206937e2392SPaolo Bonzini void setup_vm() 207937e2392SPaolo Bonzini { 208937e2392SPaolo Bonzini phys_addr_t base, top; 209dcda215bSPaolo Bonzini 210dcda215bSPaolo Bonzini if (alloc_ops == &vmalloc_ops) 211dcda215bSPaolo Bonzini return; 212dcda215bSPaolo Bonzini 213937e2392SPaolo Bonzini phys_alloc_get_unused(&base, &top); 214bf62a925SAndrew Jones assert(base != top || page_alloc_initialized()); 21548a0145fSPaolo Bonzini /* 21648a0145fSPaolo Bonzini * Give low memory immediately to the page allocator, 21748a0145fSPaolo Bonzini * so that it can be used to allocate page tables. 21848a0145fSPaolo Bonzini */ 219bf62a925SAndrew Jones if (!page_alloc_initialized()) { 2206ea7326aSClaudio Imbrenda base = PAGE_ALIGN(base); 221937e2392SPaolo Bonzini top = top & -PAGE_SIZE; 222937e2392SPaolo Bonzini free_pages(phys_to_virt(base), top - base); 223bf62a925SAndrew Jones } 22448a0145fSPaolo Bonzini 22548a0145fSPaolo Bonzini find_highmem(); 22648a0145fSPaolo Bonzini phys_alloc_get_unused(&base, &top); 227937e2392SPaolo Bonzini page_root = setup_mmu(top); 22848a0145fSPaolo Bonzini if (base != top) { 2296ea7326aSClaudio Imbrenda base = PAGE_ALIGN(base); 23048a0145fSPaolo Bonzini top = top & -PAGE_SIZE; 23148a0145fSPaolo Bonzini free_pages(phys_to_virt(base), top - base); 23248a0145fSPaolo Bonzini } 23348a0145fSPaolo Bonzini 23417b9f93eSClaudio Imbrenda spin_lock(&lock); 23517b9f93eSClaudio Imbrenda assert(alloc_ops != &vmalloc_ops); 236dcda215bSPaolo Bonzini alloc_ops = &vmalloc_ops; 23717b9f93eSClaudio Imbrenda spin_unlock(&lock); 238937e2392SPaolo Bonzini } 239