Imported Debian patch 3.4-9
[debian/elilo] / util.c
1 /*
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *
5  *  Copyright (C) 2001 Silicon Graphics, Inc.
6  *      Contributed by Brent Casavant <bcasavan@sgi.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 TENTH_SEC               1000000         /* 1/10th second in 100ns unit */
35 #define READ_BLOCK_SIZE         (4*EFI_PAGE_SIZE) /* block size for read_file */
36
37 #define is_cr(k)                (((k)==CHAR_LINEFEED)||((k)==CHAR_CARRIAGE_RETURN))
38 #define CHAR_SPACE              L' '
39
40 static INTN
41 read_keypress(EFI_INPUT_KEY *key)
42 {
43         return systab->ConIn->ReadKeyStroke(systab->ConIn, key);
44 }
45
46
47 EFI_STATUS
48 check_abort(VOID)
49 {
50         EFI_INPUT_KEY key;
51
52         return read_keypress(&key);
53 }
54
55 inline VOID
56 reset_input(VOID)
57 {
58         systab->ConIn->Reset(systab->ConIn, 1);
59 }
60
61 #if 0
62 INTN
63 wait_keypress_abort(VOID)
64 {
65         SIMPLE_INPUT_INTERFACE *conin = systab->ConIn;
66         EFI_INPUT_KEY key;
67         EFI_STATUS status;
68
69         reset_input();
70
71         Print(L"Hit ENTER to continue or ANY other key to cancel");
72
73         /* cleanup buffer first */
74         while (conin->ReadKeyStroke(conin, &key) == EFI_SUCCESS);
75
76         while ((status=conin->ReadKeyStroke(conin, &key)) == EFI_NOT_READY );
77
78         if (EFI_ERROR(status)) return ELILO_LOAD_ERROR;
79
80         Print(L"\n");
81
82         return is_cr(key.UnicodeChar) ? ELILO_LOAD_SUCCESS: ELILO_BOOT_ABORTED;
83 }
84 #endif
85
86 /*
87  * wait for timeout to expire or keypress
88  * Return:
89  *      0 : timeout expired
90  *      1 : a key was pressed (still input stream to process)
91  *      -1: an error occured
92  */
93 INTN
94 wait_timeout(UINTN timeout)
95 {
96         EFI_STATUS status;
97         EFI_EVENT timer;
98         EFI_EVENT list[2];
99         UINTN idx;
100
101
102         if (timeout == 0) return 0;
103
104         /* Create a timeout timer */
105         status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer);
106         if (EFI_ERROR(status)) {
107                 ERR_PRT((L" waitkey CreateEvent failed %r", status));
108                 return -1;
109         }
110         /* In 100ns increments */
111         status = BS->SetTimer(timer, TimerPeriodic, TENTH_SEC);
112         if (EFI_ERROR(status)) {
113                 ERR_PRT((L"waitkey SetTimer failed %r", status));
114                 return -1;
115         }
116
117         list[0] = timer;
118         list[1] = systab->ConIn->WaitForKey;
119
120         do {
121                 status = BS->WaitForEvent(2, list, &idx);
122                 if (EFI_ERROR(status)) {
123                         ERR_PRT((L"waitkey WaitForEvent failed %r", status));
124                         return -1;
125                 }
126
127         } while (timeout-- && idx == 0);        
128
129         /*
130          * SetTimer(timer, TimerCancel, 0) is causing problems on IA-32 and gcc3
131          * I do not know why it dies with EFI12.35. So let's fake a key stroke.
132          */
133         status = BS->SetTimer(timer, TimerCancel, 0);
134         if (EFI_ERROR(status)) {
135                 ERR_PRT((L"waitkey SetTimer(TimerCancel) failed %r", status));
136                 return -1;
137         }
138
139         BS->CloseEvent(timer);
140
141         return idx ? 1 : 0;
142 }
143
144 INTN
145 argify(CHAR16 *buf, UINTN len, CHAR16 **argv)   
146 {
147
148         UINTN     i=0, j=0;
149         CHAR16   *p = buf;
150         
151         if (buf == 0) { 
152                 argv[0] = NULL;
153                 return 0;
154         }
155         /* len represents the number of bytes, not the number of 16 bytes chars */
156         len = len >> 1;
157
158         /*
159          * Here we use CHAR_NULL as the terminator rather than the length
160          * because it seems like the EFI shell return rather bogus values for it.
161          * Apparently, we are guaranteed to find the '\0' character in the buffer
162          * where the real input arguments stop, so we use it instead.
163          */
164         for(;;) {
165                 while (buf[i] == CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;
166
167                 if (buf[i] == CHAR_NULL || i == len) goto end;
168
169                 p = buf+i;
170                 i++;
171
172                 while (buf[i] != CHAR_SPACE && buf[i] != CHAR_NULL && i < len) i++;
173
174                 argv[j++] = p;
175
176                 if (buf[i] == CHAR_NULL) goto end;
177
178                 buf[i]  = CHAR_NULL;
179
180                 if (i == len)  goto end;
181
182                 i++;
183
184                 if (j == MAX_ARGS-1) {
185                         ERR_PRT((L"too many arguments (%d) truncating", j));
186                         goto end;
187                 }
188         }
189 end:
190 #if 0
191         if (i != len) {
192                 ERR_PRT((L"ignoring trailing %d characters on command line", len-i));
193         }
194 #endif
195         argv[j] = NULL;
196         return j;
197 }
198
199 VOID
200 unargify(CHAR16 **argv, CHAR16 **args)
201 {
202         if ( *argv == 0 ) {
203                 *args = L"";
204                 return;
205         }
206         *args = *argv;
207         while ( argv[1] ) {
208                 (*argv)[StrLen(*argv)] = CHAR_SPACE;
209                 argv++;
210         }
211 }
212
213 VOID
214 split_args(CHAR16 *buffer, CHAR16 *kname, CHAR16 *args)
215 {
216         CHAR16 *tmp;
217
218         /* find beginning of kernel name */
219         while (*buffer && *buffer == CHAR_SPACE) buffer++;
220
221         tmp = buffer;
222         
223         /* scan through kernel name */  
224         while (*buffer && *buffer != CHAR_SPACE) buffer++;
225
226         if (*buffer) {
227                 *buffer++ = CHAR_NULL;
228                 StrCpy(kname, tmp);
229         }
230
231         /* skip space between kernel and args */        
232         while (*buffer && *buffer == CHAR_SPACE) buffer++;
233
234         StrCpy(args, buffer);
235 }
236
237 INTN
238 read_file(UINTN fd, UINTN total_size, CHAR8 *buffer)
239 {
240         INTN size, j=0;
241         EFI_STATUS status;
242         CHAR16 helicopter[4] = { L'|' , L'/' , L'-' , L'\\' };
243         INTN ret = ELILO_LOAD_SUCCESS;
244         UINTN sum = 0;
245         /*
246          * We load by chunks rather than a single big read because
247          * early versions of EFI had troubles loading files
248          * from floppies in a single big request.  Breaking
249          * the read down into chunks of 4KB fixed that
250          * problem. While this problem has been fixed, we still prefer
251          * this method because it tells us whether or not we're making
252          * forward progress.
253          */
254
255         while (total_size > 0) {
256                 size = total_size < READ_BLOCK_SIZE? total_size : READ_BLOCK_SIZE;
257
258                 status = fops_read(fd, buffer, &size);
259                 if (EFI_ERROR(status)) {
260                         ERR_PRT((L"read_file failed %r", status));
261                         return ELILO_LOAD_ERROR;
262                 }
263                 sum += size;
264
265                 Print(L"%c\b",helicopter[j++%4]);
266
267                 buffer     += size;
268                 total_size -= size; 
269
270                 if (check_abort() == EFI_SUCCESS) {
271                         ret = ELILO_LOAD_ABORTED;
272                         break;
273                 }
274         }
275         return ret;
276 }
277
278 INTN
279 get_memmap(mmap_desc_t *desc)
280 {
281 #define ELILO_MEMMAP_SIZE_DEFAULT       EFI_PAGE_SIZE
282 #define ELILO_MEMMAP_INC                (sizeof(EFI_MEMORY_DESCRIPTOR)<<1)
283
284         EFI_STATUS status;
285
286         desc->map_size = ELILO_MEMMAP_SIZE_DEFAULT;
287
288         for(;;) {
289                 desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);
290
291                 if (desc->md == NULL) {
292                         ERR_PRT((L"failed to allocate memory map buffer"));
293                         return -1;
294                 }
295                 status = (*BS->GetMemoryMap)(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
296                 if (status == EFI_SUCCESS) break;
297
298                 free(desc->md);
299
300                 if (status != EFI_BUFFER_TOO_SMALL) {
301                         ERR_PRT((L"failed to obtain memory map %r"));
302                         return -1;
303                 }
304                 desc->map_size += ELILO_MEMMAP_INC;
305         }
306         DBG_PRT((L"final get_memmap map_size=%ld", desc->map_size));
307
308         return 0;
309 }
310
311 #if 0
312 INTN
313 get_memmap(mmap_desc_t *desc)
314 {
315         EFI_STATUS status;
316
317         /* will get the right size in return */
318         desc->map_size = 0;
319
320         status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
321         if (status != EFI_BUFFER_TOO_SMALL) return -1;
322
323         desc->md = (EFI_MEMORY_DESCRIPTOR *)alloc(desc->map_size, EfiLoaderData);
324         if (desc->md == NULL) {
325                 ERR_PRT((L"failed to allocate memory map buffer"));
326                 return -1;
327         }
328
329
330         status = BS->GetMemoryMap(&desc->map_size, desc->md, &desc->cookie, &desc->desc_size, &desc->desc_version);
331         if (EFI_ERROR(status)) {
332                 ERR_PRT((L"failed to obtain memory map %d: %r", desc->map_size, status));
333                 free(desc->md);
334                 return -1;
335         }
336         DBG_PRT((L"final get_memmap map_size=%d", desc->map_size));
337
338         return 0;
339 }
340 #endif
341
342
343 VOID
344 free_memmap(mmap_desc_t *desc)
345 {
346         if (desc->md) {
347                 free(desc->md);
348                 desc->md = NULL;
349         }
350 }
351
352 VOID
353 print_memmap(mmap_desc_t *desc)
354 {
355         EFI_MEMORY_DESCRIPTOR *md;
356         UINTN desc_size;
357         VOID *p;
358         VOID *md_end;
359         INT8 printed;
360         UINTN ntypes;
361         CHAR16* str;
362
363         static CHAR16 *memtypes[]={
364                 L"ReservedMemoryType",
365                 L"LoaderCode",
366                 L"LoaderData",
367                 L"BootServicesCode",
368                 L"BootServicesData",
369                 L"RuntimeServicesCode",
370                 L"RuntimeServicesData",
371                 L"ConventionalMemory",
372                 L"UnusableMemory",
373                 L"ACPIReclaimMemory",
374                 L"ACPIMemoryNVS",
375                 L"MemoryMappedIO",
376                 L"MemoryMappedIOPortSpace",
377                 L"PalCode"
378         };
379
380
381         md_end = ((VOID *)desc->md)+desc->map_size;
382         desc_size = desc->desc_size;
383
384         ntypes = sizeof(memtypes)/sizeof(CHAR16 *);
385
386         for(p = desc->md; p < md_end; p += desc_size) {
387                 md = p;
388
389                 str = md->Type < ntypes ? memtypes[md->Type] : L"Unknown";
390
391                 Print(L"%24s %lx-%lx %8lx", str, md->PhysicalStart,
392                                 md->PhysicalStart+(md->NumberOfPages<<EFI_PAGE_SHIFT),
393                                 md->NumberOfPages);
394
395                 printed=0;
396 #define P_FLG(f)        { \
397         Print(L" %s %s", printed ? L"|":L"", f); \
398         printed=1; \
399 }
400
401                 if (md->Attribute & EFI_MEMORY_UC) {
402                         P_FLG(L"UC");
403                 }
404                 if (md->Attribute & EFI_MEMORY_WC) {
405                         P_FLG(L"WC");
406                 }
407                 if (md->Attribute & EFI_MEMORY_WT) {
408                         P_FLG(L"WT");
409                 }
410                 if (md->Attribute & EFI_MEMORY_WB) {
411                         P_FLG(L"WB");
412                 }
413                 if (md->Attribute & EFI_MEMORY_UCE) {
414                         P_FLG(L"UCE");
415                 }
416                 if (md->Attribute & EFI_MEMORY_WP) {
417                         P_FLG(L"WP");
418                 }
419                 if (md->Attribute & EFI_MEMORY_RP) {
420                         P_FLG(L"RP");
421                 }
422                 if (md->Attribute & EFI_MEMORY_XP) {
423                         P_FLG(L"XP");
424                 }
425                 if (md->Attribute & EFI_MEMORY_RUNTIME) {
426                         P_FLG(L"RT");
427                 }
428                 Print(L"\n");
429         }
430 }
431
432 INTN
433 find_kernel_memory(VOID* low_addr, VOID* max_addr, UINTN alignment, VOID** start)
434 {       
435 #define HIGHEST_ADDR (VOID*)(~0)
436         mmap_desc_t mdesc;
437         EFI_MEMORY_DESCRIPTOR *md;
438         UINT64 size;
439         VOID *p, *addr;
440         VOID *desc_end, *md_end, *best_addr = HIGHEST_ADDR;
441
442         /*
443          * first get up-to-date memory map
444          *
445          * XXX: is there a danger of not seeing the latest version if interrupted
446          * during our scan ?
447          *
448          */
449         if (get_memmap(&mdesc) == -1) {
450                 ERR_PRT((L"find_kernel_memory :GetMemoryMap() failed"));
451                 return -1;
452         }
453
454         desc_end = ((VOID *)mdesc.md) + mdesc.map_size;
455         size     = max_addr - low_addr;
456         /*
457          * Find memory which covers the desired range
458          */
459         for(p = mdesc.md; p < desc_end; p += mdesc.desc_size) {
460                 md = p;
461
462                 /*
463                  * restrict to decent memory types. 
464                  *
465                  * the EFI memory map report where memory is and how it is currently used
466                  * using types.
467                  *
468                  * EfiLoaderData which is used by the AllocatePages() cannot be used
469                  * here because it may hold some valid information. Same thing for most
470                  * of the memory types with the exception of EfiConventional which 
471                  * can be assumed as being free to use. 
472                  */
473                 if (md->Type != EfiConventionalMemory) continue;
474
475                 /* 
476                  * compute aligned address and upper boundary for range
477                  */
478                 md_end = (VOID*)(md->PhysicalStart + md->NumberOfPages * EFI_PAGE_SIZE);        
479                 addr   = (VOID*)ROUNDUP(md->PhysicalStart, alignment);
480
481                 /*
482                  * need to check if:
483                  * - aligned address still in the range
484                  * - the range [addr-addr+size) still fits into memory range
485                  * if so we have a match. We do not assume that the memory ranges
486                  * are sorted by EFI, therefore we must record the match and only
487                  * keep the lowest possible one.
488                  */
489                 if (addr < best_addr && addr < md_end && addr+size <= md_end) best_addr = addr;
490         }
491         if (best_addr == HIGHEST_ADDR) {
492                 free_memmap(&mdesc);
493                 ERR_PRT((L"Could not find memory suitable for loading image"));
494                 return -1;
495         }
496
497         *start = best_addr;
498
499         free_memmap(&mdesc);
500
501         return 0;
502 }
503