Imported Upstream version 3.10
[debian/elilo] / alloc.c
1 /*
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *      Contributed by Fenghua Yu <Fenghua.Yu@intel.com>
5  *      Contributed by Bibo Mao <bibo.mao@intel.com>
6  *      Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
7  *
8  * This file is part of the ELILO, the EFI Linux boot loader.
9  *
10  *  ELILO is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  ELILO is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with ELILO; see the file COPYING.  If not, write to the Free
22  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23  *  02111-1307, USA.
24  *
25  * Please check out the elilo.txt for complete documentation on how
26  * to use this program.
27  */
28
29 #include <efi.h>
30 #include <efilib.h>
31
32 #include "elilo.h"
33
34 #define NALLOC  512
35
36 typedef enum { ALLOC_POOL, ALLOC_PAGES } alloc_types_t;
37
38 typedef struct _alloc_entry {
39         struct _alloc_entry     *next;
40         struct _alloc_entry     *prev;
41         VOID                    *addr;
42         UINTN                   size; /* bytes for pool, page count for pages */
43         alloc_types_t           type;
44 } alloc_entry_t;
45
46 static alloc_entry_t allocs[NALLOC];
47 static alloc_entry_t *free_allocs, *used_allocs;
48
49 static VOID *kmem_addr;
50 static UINTN kmem_pgcnt;
51
52 /*
53  * initializes the free list which is singly linked
54  */
55 INTN
56 alloc_init(VOID)
57 {
58         UINTN i;
59
60         for(i=0; i < NALLOC-1; i++) {
61                 allocs[i].next = allocs+i+1;
62         }
63         allocs[i].next = NULL;
64
65         free_allocs = allocs;
66         used_allocs = NULL;
67
68         return 0;
69 }
70
71 static VOID
72 alloc_add(VOID * addr, UINTN size, alloc_types_t type)
73 {
74         alloc_entry_t *alloc;
75
76         /* remove from freelist */
77         alloc       = free_allocs;
78         free_allocs = free_allocs->next;
79         
80         alloc->prev = NULL;
81         alloc->next = used_allocs;
82         alloc->addr = addr;
83         alloc->type = type;
84         alloc->size = size;
85
86         /* add to used list */
87         if (used_allocs) used_allocs->prev = alloc;
88
89         used_allocs = alloc;
90 }
91
92 VOID *
93 alloc(UINTN size, EFI_MEMORY_TYPE type)
94 {
95         EFI_STATUS status;
96         VOID *tmp = 0;
97
98         /* no more free slots */
99         if (free_allocs == NULL) {
100                 ERR_PRT((L"allocator:  no more slots\n"));
101                 return NULL;
102         }
103
104         if (type == 0) type = EfiLoaderData;
105
106         status = uefi_call_wrapper(BS->AllocatePool, 3, type, size, &tmp);
107         if (EFI_ERROR(status)) {
108                 ERR_PRT((L"allocator: AllocatePool(%d, %d) failed (%r)\n", type, size, status));
109                 return NULL;
110         }
111         alloc_add(tmp, size, ALLOC_POOL);
112
113         DBG_PRT((L"alloc: allocated %d bytes @[" PTR_FMT "-" PTR_FMT "]\n", size, tmp, tmp+size));
114
115         return tmp;
116 }
117
118 /*
119  *  no possibility to partially free an allocated group of pages
120  */
121 VOID *
122 alloc_pages(UINTN pgcnt, EFI_MEMORY_TYPE type, EFI_ALLOCATE_TYPE where, VOID *addr)
123 {
124         EFI_STATUS status;
125         EFI_PHYSICAL_ADDRESS tmp = (EFI_PHYSICAL_ADDRESS)addr;
126
127         /* no more free slots */
128         if (free_allocs == NULL) {
129                 ERR_PRT((L"allocator:  no more slots\n"));
130                 return NULL;
131         }
132
133         status = uefi_call_wrapper(BS->AllocatePages, 4, where, type , pgcnt, &tmp);
134         if (EFI_ERROR(status)) {
135                 VERB_PRT(1, Print(L"allocator: AllocatePages(%d, %d, %d, 0x%lx) failed (%r)\n", where, type, pgcnt, tmp, status));
136                 return NULL;
137         }
138         /* XXX: will cause warning on IA-32 */
139         addr = (VOID *)tmp;
140
141         alloc_add(addr, pgcnt, ALLOC_PAGES);
142
143         DBG_PRT((L"allocator: allocated %d pages @0x%lx\n", pgcnt, tmp));
144
145         return addr;
146 }
147
148 /*
149  * free previously allocated slot
150  */
151 VOID
152 free(VOID *addr)
153 {
154         alloc_entry_t *p;
155
156         /* find allocation record */
157         for(p=used_allocs; p ; p = p->next) {
158                 if (p->addr == addr) goto found;
159         }
160         /* not found */
161         VERB_PRT(1, Print(L"allocator: invalid free @ " PTR_FMT "\n", addr));
162         return; 
163 found:
164         DBG_PRT((L"free: %s @" PTR_FMT " size=%d\n", 
165                 p->type == ALLOC_POOL ? L"Pool": L"Page", 
166                 addr, p->size));
167
168         if (p->type == ALLOC_POOL) 
169                 uefi_call_wrapper(BS->FreePool, 1, addr);
170         else
171                 uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)addr, p->size);
172
173         /* remove from used list */
174         if (p->next) 
175                 p->next->prev = p->prev;
176                 
177         if (p->prev) 
178                 p->prev->next = p->next;
179         else
180                 used_allocs = p->next;
181
182         /* put back on free list */
183         p->next     = free_allocs;
184         free_allocs = p;
185 }
186
187 /*
188  * garbage collect all used allocations.
189  * will put the allocator in initial state 
190  */
191 VOID
192 free_all(VOID)
193 {
194         alloc_entry_t *tmp;
195
196         while(used_allocs) {
197
198                 DBG_PRT((L"free_all %a @ " PTR_FMT "\n", used_allocs->type == ALLOC_POOL ? "pool" : "pages", used_allocs->addr));
199         
200                 if (used_allocs->type == ALLOC_POOL)
201                         uefi_call_wrapper(BS->FreePool, 1, used_allocs->addr);
202                 else
203                         uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)used_allocs->addr, used_allocs->size);
204
205                 tmp = used_allocs->next;
206
207                 /* put back on free list */
208                 used_allocs->next = free_allocs;
209                 free_allocs = used_allocs;
210
211                 used_allocs = tmp;
212         }
213 }
214
215 INTN
216 alloc_kmem_anywhere(VOID **start_addr, UINTN pgcnt)
217 {
218         void * tmp;
219         if ((tmp = alloc_pages(pgcnt, EfiLoaderData, AllocateAnyPages, *start_addr)) == 0) return -1;
220
221         kmem_addr  = tmp;
222         kmem_pgcnt = pgcnt;
223         *start_addr = tmp;
224
225         return 0;
226 }
227
228 INTN
229 alloc_kmem(VOID *start_addr, UINTN pgcnt)
230 {
231         if (alloc_pages(pgcnt, EfiLoaderData, AllocateAddress, start_addr) == 0) return -1;
232
233         kmem_addr  = start_addr;
234         kmem_pgcnt = pgcnt;
235
236         return 0;
237 }
238
239 VOID
240 free_kmem(VOID)
241 {
242         DBG_PRT((L"free_kmem before (" PTR_FMT ", %d)\n", kmem_addr, kmem_pgcnt));
243         if (kmem_addr && kmem_pgcnt != 0) {
244                 free(kmem_addr);
245                 kmem_addr  = NULL;
246                 kmem_pgcnt = 0;
247         }
248         DBG_PRT((L"free_kmem after (" PTR_FMT ", %d)\n", kmem_addr, kmem_pgcnt));
249 }
250
251 VOID
252 free_all_memory(VOID)
253 {
254         free_all();
255         free_kmem();
256 }