Spaces:
Runtime error
Runtime error
File size: 3,969 Bytes
8df6da4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
/*
* Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include "alloc.h"
#include "asm/spinlock.h"
#include "asm/io.h"
#define PHYS_ALLOC_NR_REGIONS 256
struct phys_alloc_region {
phys_addr_t base;
phys_addr_t size;
};
static struct phys_alloc_region regions[PHYS_ALLOC_NR_REGIONS];
static int nr_regions;
static struct spinlock lock;
static phys_addr_t base, top, align_min;
void phys_alloc_show(void)
{
int i;
spin_lock(&lock);
printf("phys_alloc minimum alignment: %#" PRIx64 "\n",
(u64)align_min);
for (i = 0; i < nr_regions; ++i)
printf("%016" PRIx64 "-%016" PRIx64 " [%s]\n",
(u64)regions[i].base,
(u64)(regions[i].base + regions[i].size - 1),
"USED");
printf("%016" PRIx64 "-%016" PRIx64 " [%s]\n",
(u64)base, (u64)(top - 1), "FREE");
spin_unlock(&lock);
}
void phys_alloc_init(phys_addr_t base_addr, phys_addr_t size)
{
spin_lock(&lock);
base = base_addr;
top = base + size;
align_min = DEFAULT_MINIMUM_ALIGNMENT;
nr_regions = 0;
spin_unlock(&lock);
}
void phys_alloc_set_minimum_alignment(phys_addr_t align)
{
assert(align && !(align & (align - 1)));
spin_lock(&lock);
align_min = align;
spin_unlock(&lock);
}
static phys_addr_t phys_alloc_aligned_safe(phys_addr_t size,
phys_addr_t align, bool safe)
{
static bool warned = false;
phys_addr_t addr, size_orig = size;
u64 top_safe;
spin_lock(&lock);
top_safe = top;
if (safe && sizeof(long) == 4)
top_safe = MIN(top_safe, 1ULL << 32);
align = MAX(align, align_min);
addr = ALIGN(base, align);
size += addr - base;
if ((top_safe - base) < size) {
printf("phys_alloc: requested=%#" PRIx64
" (align=%#" PRIx64 "), "
"need=%#" PRIx64 ", but free=%#" PRIx64 ". "
"top=%#" PRIx64 ", top_safe=%#" PRIx64 "\n",
(u64)size_orig, (u64)align, (u64)size, top_safe - base,
(u64)top, top_safe);
spin_unlock(&lock);
return INVALID_PHYS_ADDR;
}
base += size;
if (nr_regions < PHYS_ALLOC_NR_REGIONS) {
regions[nr_regions].base = addr;
regions[nr_regions].size = size_orig;
++nr_regions;
} else if (!warned) {
printf("WARNING: phys_alloc: No free log entries, "
"can no longer log allocations...\n");
warned = true;
}
spin_unlock(&lock);
return addr;
}
static phys_addr_t phys_zalloc_aligned_safe(phys_addr_t size,
phys_addr_t align, bool safe)
{
phys_addr_t addr = phys_alloc_aligned_safe(size, align, safe);
if (addr == INVALID_PHYS_ADDR)
return addr;
memset(phys_to_virt(addr), 0, size);
return addr;
}
phys_addr_t phys_alloc_aligned(phys_addr_t size, phys_addr_t align)
{
return phys_alloc_aligned_safe(size, align, false);
}
phys_addr_t phys_zalloc_aligned(phys_addr_t size, phys_addr_t align)
{
return phys_zalloc_aligned_safe(size, align, false);
}
phys_addr_t phys_alloc(phys_addr_t size)
{
return phys_alloc_aligned(size, align_min);
}
phys_addr_t phys_zalloc(phys_addr_t size)
{
return phys_zalloc_aligned(size, align_min);
}
static void *early_malloc(size_t size)
{
phys_addr_t addr = phys_alloc_aligned_safe(size, align_min, true);
if (addr == INVALID_PHYS_ADDR)
return NULL;
return phys_to_virt(addr);
}
static void *early_calloc(size_t nmemb, size_t size)
{
phys_addr_t addr = phys_zalloc_aligned_safe(nmemb * size,
align_min, true);
if (addr == INVALID_PHYS_ADDR)
return NULL;
return phys_to_virt(addr);
}
static void early_free(void *ptr __unused)
{
}
static void *early_memalign(size_t alignment, size_t size)
{
phys_addr_t addr;
assert(alignment && !(alignment & (alignment - 1)));
addr = phys_alloc_aligned_safe(size, alignment, true);
if (addr == INVALID_PHYS_ADDR)
return NULL;
return phys_to_virt(addr);
}
static struct alloc_ops early_alloc_ops = {
.malloc = early_malloc,
.calloc = early_calloc,
.free = early_free,
.memalign = early_memalign,
};
struct alloc_ops *alloc_ops = &early_alloc_ops;
|