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