efi_loader: check length in CreateDeviceNode()
[oweals/u-boot.git] / lib / efi_loader / efi_memory.c
index 4bb517473e447393f0518b4ec1d86e9007ffc060..987cc6dc5f61aaaec7fea74c8e87ecbd0c927a90 100644 (file)
@@ -15,6 +15,9 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* Magic number identifying memory allocated from pool */
+#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
+
 efi_uintn_t efi_memory_map_key;
 
 struct efi_mem_list {
@@ -33,7 +36,12 @@ LIST_HEAD(efi_mem);
 void *efi_bounce_buffer;
 #endif
 
-/*
+/**
+ * efi_pool_allocation - memory block allocated from pool
+ *
+ * @num_pages: number of pages allocated
+ * @checksum:  checksum
+ *
  * U-Boot services each EFI AllocatePool request as a separate
  * (multiple) page allocation.  We have to track the number of pages
  * to be able to free the correct amount later.
@@ -43,9 +51,26 @@ void *efi_bounce_buffer;
  */
 struct efi_pool_allocation {
        u64 num_pages;
+       u64 checksum;
        char data[] __aligned(ARCH_DMA_MINALIGN);
 };
 
+/**
+ * checksum() - calculate checksum for memory allocated from pool
+ *
+ * @alloc:     allocation header
+ * Return:     checksum, always non-zero
+ */
+static u64 checksum(struct efi_pool_allocation *alloc)
+{
+       u64 addr = (uintptr_t)alloc;
+       u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
+                 EFI_ALLOC_POOL_MAGIC;
+       if (!ret)
+               ++ret;
+       return ret;
+}
+
 /*
  * Sorts the memory list from highest address to lowest address
  *
@@ -168,6 +193,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map,
                        free(map);
                } else {
                        map->desc.physical_start = carve_end;
+                       map->desc.virtual_start = carve_end;
                        map->desc.num_pages = (map_end - carve_end)
                                              >> EFI_PAGE_SHIFT;
                }
@@ -186,6 +212,7 @@ static s64 efi_mem_carve_out(struct efi_mem_list *map,
        newmap = calloc(1, sizeof(*newmap));
        newmap->desc = map->desc;
        newmap->desc.physical_start = carve_start;
+       newmap->desc.virtual_start = carve_start;
        newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT;
        /* Insert before current entry (descending address order) */
        list_add_tail(&newmap->link, &map->link);
@@ -204,8 +231,8 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
        bool carve_again;
        uint64_t carved_pages = 0;
 
-       debug("%s: 0x%llx 0x%llx %d %s\n", __func__,
-             start, pages, memory_type, overlap_only_ram ? "yes" : "no");
+       EFI_PRINT("%s: 0x%llx 0x%llx %d %s\n", __func__,
+                 start, pages, memory_type, overlap_only_ram ? "yes" : "no");
 
        if (memory_type >= EFI_MAX_MEMORY_TYPE)
                return EFI_INVALID_PARAMETER;
@@ -349,6 +376,10 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
        efi_status_t r = EFI_SUCCESS;
        uint64_t addr;
 
+       /* Check import parameters */
+       if (memory_type >= EFI_PERSISTENT_MEMORY_TYPE &&
+           memory_type <= 0x6FFFFFFF)
+               return EFI_INVALID_PARAMETER;
        if (!memory)
                return EFI_INVALID_PARAMETER;
 
@@ -409,17 +440,24 @@ void *efi_alloc(uint64_t len, int memory_type)
        return NULL;
 }
 
-/*
- * Free memory pages.
+/**
+ * efi_free_pages() - free memory pages
  *
- * @memory     start of the memory area to be freed
- * @pages      number of pages to be freed
- * @return     status code
+ * @memory:    start of the memory area to be freed
+ * @pages:     number of pages to be freed
+ * Return:     status code
  */
 efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
 {
        uint64_t r = 0;
 
+       /* Sanity check */
+       if (!memory || (memory & EFI_PAGE_MASK)) {
+               printf("%s: illegal free 0x%llx, 0x%zx\n", __func__,
+                      memory, pages);
+               return EFI_INVALID_PARAMETER;
+       }
+
        r = efi_add_memory_map(memory, pages, EFI_CONVENTIONAL_MEMORY, false);
        /* Merging of adjacent free regions is missing */
 
@@ -429,17 +467,18 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages)
        return EFI_NOT_FOUND;
 }
 
-/*
- * Allocate memory from pool.
+/**
+ * efi_allocate_pool - allocate memory from pool
  *
- * @pool_type  type of the pool from which memory is to be allocated
- * @size       number of bytes to be allocated
- * @buffer     allocated memory
- * @return     status code
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size:      number of bytes to be allocated
+ * @buffer:    allocated memory
+ * Return:     status code
  */
 efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
 {
        efi_status_t r;
+       u64 addr;
        struct efi_pool_allocation *alloc;
        u64 num_pages = efi_size_in_pages(size +
                                          sizeof(struct efi_pool_allocation));
@@ -453,21 +492,22 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer)
        }
 
        r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
-                              (uint64_t *)&alloc);
-
+                              &addr);
        if (r == EFI_SUCCESS) {
+               alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
                alloc->num_pages = num_pages;
+               alloc->checksum = checksum(alloc);
                *buffer = alloc->data;
        }
 
        return r;
 }
 
-/*
- * Free memory from pool.
+/**
+ * efi_free_pool() - free memory from pool
  *
- * @buffer     start of memory to be freed
- * @return     status code
+ * @buffer:    start of memory to be freed
+ * Return:     status code
  */
 efi_status_t efi_free_pool(void *buffer)
 {
@@ -478,8 +518,15 @@ efi_status_t efi_free_pool(void *buffer)
                return EFI_INVALID_PARAMETER;
 
        alloc = container_of(buffer, struct efi_pool_allocation, data);
-       /* Sanity check, was the supplied address returned by allocate_pool */
-       assert(((uintptr_t)alloc & EFI_PAGE_MASK) == 0);
+
+       /* Check that this memory was allocated by efi_allocate_pool() */
+       if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
+           alloc->checksum != checksum(alloc)) {
+               printf("%s: illegal free 0x%p\n", __func__, buffer);
+               return EFI_INVALID_PARAMETER;
+       }
+       /* Avoid double free */
+       alloc->checksum = 0;
 
        r = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
 
@@ -554,6 +601,12 @@ __weak void efi_add_known_memory(void)
        u64 ram_top = board_get_usable_ram_top(0) & ~EFI_PAGE_MASK;
        int i;
 
+       /*
+        * ram_top is just outside mapped memory. So use an offset of one for
+        * mapping the sandbox address.
+        */
+       ram_top = (uintptr_t)map_sysmem(ram_top - 1, 0) + 1;
+
        /* Fix for 32bit targets with ram_top at 4G */
        if (!ram_top)
                ram_top = 0x100000000ULL;