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