261 lines
5.9 KiB
C
261 lines
5.9 KiB
C
/*
|
|
* Copyright (C) 2001-2003 Hewlett-Packard Co.
|
|
* Contributed by Stephane Eranian <eranian@hpl.hp.com>
|
|
* Contributed by Fenghua Yu <Fenghua.Yu@intel.com>
|
|
* Contributed by Bibo Mao <bibo.mao@intel.com>
|
|
* Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
|
|
*
|
|
* This file is part of the ELILO, the EFI Linux boot loader.
|
|
*
|
|
* ELILO is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* ELILO is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with ELILO; see the file COPYING. If not, write to the Free
|
|
* Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Please check out the elilo.txt for complete documentation on how
|
|
* to use this program.
|
|
*/
|
|
|
|
#include <efi.h>
|
|
#include <efilib.h>
|
|
|
|
#include "elilo.h"
|
|
|
|
#define NALLOC 512
|
|
|
|
typedef enum { ALLOC_POOL, ALLOC_PAGES } alloc_types_t;
|
|
|
|
typedef struct _alloc_entry {
|
|
struct _alloc_entry *next;
|
|
struct _alloc_entry *prev;
|
|
VOID *addr;
|
|
UINTN size; /* bytes for pool, page count for pages */
|
|
alloc_types_t type;
|
|
} alloc_entry_t;
|
|
|
|
static alloc_entry_t allocs[NALLOC];
|
|
static alloc_entry_t *free_allocs, *used_allocs;
|
|
|
|
static VOID *kmem_addr;
|
|
static UINTN kmem_pgcnt;
|
|
|
|
/*
|
|
* initializes the free list which is singly linked
|
|
*/
|
|
INTN
|
|
alloc_init(VOID)
|
|
{
|
|
UINTN i;
|
|
|
|
for(i=0; i < NALLOC-1; i++) {
|
|
allocs[i].next = allocs+i+1;
|
|
}
|
|
allocs[i].next = NULL;
|
|
|
|
free_allocs = allocs;
|
|
used_allocs = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static VOID
|
|
alloc_add(VOID * addr, UINTN size, alloc_types_t type)
|
|
{
|
|
alloc_entry_t *alloc;
|
|
|
|
/* remove from freelist */
|
|
alloc = free_allocs;
|
|
free_allocs = free_allocs->next;
|
|
|
|
alloc->prev = NULL;
|
|
alloc->next = used_allocs;
|
|
alloc->addr = addr;
|
|
alloc->type = type;
|
|
alloc->size = size;
|
|
|
|
/* add to used list */
|
|
if (used_allocs) used_allocs->prev = alloc;
|
|
|
|
used_allocs = alloc;
|
|
}
|
|
|
|
VOID *
|
|
alloc(UINTN size, EFI_MEMORY_TYPE type)
|
|
{
|
|
EFI_STATUS status;
|
|
VOID *tmp = 0;
|
|
|
|
/* no more free slots */
|
|
if (free_allocs == NULL) {
|
|
ERR_PRT((L"allocator: no more slots\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if (type == 0) type = EfiLoaderData;
|
|
|
|
status = uefi_call_wrapper(BS->AllocatePool, 3, type, size, &tmp);
|
|
if (EFI_ERROR(status)) {
|
|
ERR_PRT((L"allocator: AllocatePool(%d, %d) failed (%r)\n", type, size, status));
|
|
return NULL;
|
|
}
|
|
alloc_add(tmp, size, ALLOC_POOL);
|
|
#ifdef DEBUG_MEM
|
|
DBG_PRT((L"alloc: allocated %d bytes @[" PTR_FMT "-" PTR_FMT "]\n", size, tmp, tmp+size));
|
|
#endif
|
|
return tmp;
|
|
}
|
|
|
|
/*
|
|
* no possibility to partially free an allocated group of pages
|
|
*/
|
|
VOID *
|
|
alloc_pages(UINTN pgcnt, EFI_MEMORY_TYPE type, EFI_ALLOCATE_TYPE where, VOID *addr)
|
|
{
|
|
EFI_STATUS status;
|
|
EFI_PHYSICAL_ADDRESS tmp = (EFI_PHYSICAL_ADDRESS)addr;
|
|
|
|
/* no more free slots */
|
|
if (free_allocs == NULL) {
|
|
ERR_PRT((L"allocator: no more slots\n"));
|
|
return NULL;
|
|
}
|
|
|
|
status = uefi_call_wrapper(BS->AllocatePages, 4, where, type , pgcnt, &tmp);
|
|
if (EFI_ERROR(status)) {
|
|
VERB_PRT(1, Print(L"allocator: AllocatePages(%d, %d, %d, 0x%lx) failed (%r)\n", where, type, pgcnt, tmp, status));
|
|
return NULL;
|
|
}
|
|
/* XXX: will cause warning on IA-32 */
|
|
addr = (VOID *)tmp;
|
|
|
|
alloc_add(addr, pgcnt, ALLOC_PAGES);
|
|
|
|
DBG_PRT((L"allocator: allocated %d pages @0x%lx\n", pgcnt, tmp));
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* free previously allocated slot
|
|
*/
|
|
VOID
|
|
free(VOID *addr)
|
|
{
|
|
alloc_entry_t *p;
|
|
|
|
/* find allocation record */
|
|
for(p=used_allocs; p ; p = p->next) {
|
|
if (p->addr == addr) goto found;
|
|
}
|
|
/* not found */
|
|
VERB_PRT(1, Print(L"allocator: invalid free @ " PTR_FMT "\n", addr));
|
|
return;
|
|
found:
|
|
#ifdef DEBUG_MEM
|
|
DBG_PRT((L"free: %s @" PTR_FMT " size=%d\n",
|
|
p->type == ALLOC_POOL ? L"Pool": L"Page",
|
|
addr, p->size));
|
|
#endif
|
|
if (p->type == ALLOC_POOL)
|
|
uefi_call_wrapper(BS->FreePool, 1, addr);
|
|
else
|
|
uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)addr, p->size);
|
|
|
|
/* remove from used list */
|
|
if (p->next)
|
|
p->next->prev = p->prev;
|
|
|
|
if (p->prev)
|
|
p->prev->next = p->next;
|
|
else
|
|
used_allocs = p->next;
|
|
|
|
/* put back on free list */
|
|
p->next = free_allocs;
|
|
free_allocs = p;
|
|
}
|
|
|
|
/*
|
|
* garbage collect all used allocations.
|
|
* will put the allocator in initial state
|
|
*/
|
|
VOID
|
|
free_all(VOID)
|
|
{
|
|
alloc_entry_t *tmp;
|
|
|
|
while(used_allocs) {
|
|
#ifdef DEBUG_MEM
|
|
DBG_PRT((L"free_all %a @ " PTR_FMT "\n", used_allocs->type == ALLOC_POOL ? "pool" : "pages", used_allocs->addr));
|
|
#endif
|
|
if (used_allocs->type == ALLOC_POOL)
|
|
uefi_call_wrapper(BS->FreePool, 1, used_allocs->addr);
|
|
else
|
|
uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)used_allocs->addr, used_allocs->size);
|
|
|
|
tmp = used_allocs->next;
|
|
|
|
/* put back on free list */
|
|
used_allocs->next = free_allocs;
|
|
free_allocs = used_allocs;
|
|
|
|
used_allocs = tmp;
|
|
}
|
|
}
|
|
|
|
INTN
|
|
alloc_kmem_anywhere(VOID **start_addr, UINTN pgcnt)
|
|
{
|
|
void * tmp;
|
|
if ((tmp = alloc_pages(pgcnt, EfiLoaderData, AllocateAnyPages, *start_addr)) == 0) return -1;
|
|
|
|
kmem_addr = tmp;
|
|
kmem_pgcnt = pgcnt;
|
|
*start_addr = tmp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
INTN
|
|
alloc_kmem(VOID *start_addr, UINTN pgcnt)
|
|
{
|
|
if (alloc_pages(pgcnt, EfiLoaderData, AllocateAddress, start_addr) == 0) return -1;
|
|
|
|
kmem_addr = start_addr;
|
|
kmem_pgcnt = pgcnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
free_kmem(VOID)
|
|
{
|
|
#ifdef DEBUG_MEM
|
|
DBG_PRT((L"free_kmem before (" PTR_FMT ", %d)\n", kmem_addr, kmem_pgcnt));
|
|
#endif
|
|
if (kmem_addr && kmem_pgcnt != 0) {
|
|
free(kmem_addr);
|
|
kmem_addr = NULL;
|
|
kmem_pgcnt = 0;
|
|
}
|
|
#ifdef DEBUG_MEM
|
|
DBG_PRT((L"free_kmem after (" PTR_FMT ", %d)\n", kmem_addr, kmem_pgcnt));
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
free_all_memory(VOID)
|
|
{
|
|
free_all();
|
|
free_kmem();
|
|
}
|