orphan
[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 #ifdef DEBUG_MEM
113         DBG_PRT((L"alloc: allocated %d bytes @[" PTR_FMT "-" PTR_FMT "]", size, tmp, tmp+size));
114 #endif
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 @" PTR_FMT, 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 #ifdef DEBUG_MEM
165         DBG_PRT((L"free: %s @" PTR_FMT " size=%d",
166                 p->type == ALLOC_POOL ? L"Pool": L"Page", 
167                 addr, p->size));
168 #endif
169         if (p->type == ALLOC_POOL) 
170                 uefi_call_wrapper(BS->FreePool, 1, addr);
171         else
172                 uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)addr, p->size);
173
174         /* remove from used list */
175         if (p->next) 
176                 p->next->prev = p->prev;
177                 
178         if (p->prev) 
179                 p->prev->next = p->next;
180         else
181                 used_allocs = p->next;
182
183         /* put back on free list */
184         p->next     = free_allocs;
185         free_allocs = p;
186 }
187
188 /*
189  * garbage collect all used allocations.
190  * will put the allocator in initial state 
191  */
192 VOID
193 free_all(VOID)
194 {
195         alloc_entry_t *tmp;
196
197         while(used_allocs) {
198 #ifdef DEBUG_MEM
199                 DBG_PRT((L"free_all %a @ " PTR_FMT, used_allocs->type == ALLOC_POOL ? "pool" : "pages", used_allocs->addr));
200 #endif  
201                 if (used_allocs->type == ALLOC_POOL)
202                         uefi_call_wrapper(BS->FreePool, 1, used_allocs->addr);
203                 else
204                         uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)used_allocs->addr, used_allocs->size);
205
206                 tmp = used_allocs->next;
207
208                 /* put back on free list */
209                 used_allocs->next = free_allocs;
210                 free_allocs = used_allocs;
211
212                 used_allocs = tmp;
213         }
214 }
215
216 INTN
217 alloc_kmem_anywhere(VOID **start_addr, UINTN pgcnt)
218 {
219         void * tmp;
220         /*
221          * During "AllocateAnyPages" *start_addr will be ignored.
222          * Therefore we can safely subvert it to reuse this function with
223          * an alloc_kmem_anyhwere_below() semantic...
224          */
225         tmp = alloc_pages(pgcnt, EfiLoaderData,
226                         (*start_addr) ? AllocateMaxAddress : AllocateAnyPages,
227                         *start_addr);
228         if (tmp == NULL) return -1;
229
230         kmem_addr  = tmp;
231         kmem_pgcnt = pgcnt;
232         *start_addr = tmp;
233
234         return 0;
235 }
236
237 INTN
238 alloc_kmem(VOID *start_addr, UINTN pgcnt)
239 {
240         if (alloc_pages(pgcnt, EfiLoaderData, AllocateAddress, start_addr) == 0) return -1;
241
242         kmem_addr  = start_addr;
243         kmem_pgcnt = pgcnt;
244
245         return 0;
246 }
247
248 VOID
249 free_kmem(VOID)
250 {
251 #ifdef DEBUG_MEM
252         DBG_PRT((L"free_kmem before (" PTR_FMT ", %d)", kmem_addr, kmem_pgcnt));
253 #endif
254         if (kmem_addr && kmem_pgcnt != 0) {
255                 free(kmem_addr);
256                 kmem_addr  = NULL;
257                 kmem_pgcnt = 0;
258         }
259 #ifdef DEBUG_MEM
260         DBG_PRT((L"free_kmem after (" PTR_FMT ", %d)", kmem_addr, kmem_pgcnt));
261 #endif
262 }
263
264 VOID
265 free_all_memory(VOID)
266 {
267         free_all();
268         free_kmem();
269 }