orphan
[debian/elilo] / fileops.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 #include "fileops.h"
34
35 /*
36  * import filesystems
37  */
38 #ifdef CONFIG_LOCALFS
39 #include "glue_localfs.h"
40 #endif
41 #ifdef CONFIG_NETFS
42 #include "glue_netfs.h"
43 #endif
44
45 #ifdef CONFIG_EXT2FS
46 #include "glue_ext2fs.h"
47 #endif
48
49 /*
50  * table of device naming schemes.
51  * we will probe each one in sequential order and stop at first match.
52  */
53 extern devname_scheme_t simple_devname_scheme;
54
55 static devname_scheme_t *devname_scheme_tab[]={
56         &simple_devname_scheme,
57         NULL
58 };
59
60
61 /*
62  * Once we are able to create Boot Services drivers we won't need
63  * this hack and we'll be able to load the driver from the boot manager
64  * or EFI shell. So for now we explicitely invoke the probe routine
65  * of each driver that we know about
66  */
67
68 typedef EFI_STATUS (fops_fixups_t)(EFI_HANDLE dev, device_t *d);
69
70 /*
71  * List of filesystem we currently support
72  */
73
74
75 static fileops_fs_t *fs_tab[]={
76 #ifdef CONFIG_LOCALFS
77         &localfs_glue, 
78 #endif
79 #ifdef CONFIG_EXT2FS
80         &ext2fs_glue,
81 #endif
82 #ifdef CONFIG_NETFS
83         &netfs_glue, 
84 #endif
85         NULL
86 };
87
88 static device_t *dev_tab;
89 static UINTN ndev, ndev_boot;
90 static device_t *boot_dev;
91
92 typedef struct _fdesc_t {
93         struct _fdesc_t *next;  /* pointer to next free (when in free list) */
94         fileops_t       *fops;  /* pointer to filesystem-specific glue interface */
95         UINTN           fh;     /* file descriptor from lower level protocol */
96 } fdesc_t;
97
98 #define FILEOPS_FD_MAX  16
99
100 static fdesc_t fd_tab[FILEOPS_FD_MAX];
101 static fdesc_t *free_fd;
102
103 static devname_scheme_t *current_devname_scheme;
104
105 #define FDESC_IDX(f)    (fops_fd_t)(f-fd_tab)
106 #define FDESC_INVALID_FD(fd)    (fd >= FILEOPS_FD_MAX || fd_tab[(fd)].fops == NULL)
107 #define FDESC_F(fd)             (fd_tab+(fd))
108
109 static fdesc_t *
110 fd_alloc(VOID)
111 {
112         fdesc_t *tmp = NULL;
113
114         if (free_fd == NULL) {
115                 ERR_PRT((L"out of file descriptor"));
116         } else {
117                 tmp = free_fd;
118                 free_fd = free_fd->next;
119         }
120         return tmp;
121 }
122
123 static VOID
124 fd_free(fdesc_t *fd)
125 {
126         if (fd == NULL) {
127                 ERR_PRT((L"invalid fd"));
128         }
129         fd->fops = NULL; /* mark as invalid */
130         fd->next = free_fd;
131         free_fd = fd;
132 }
133
134 static fileops_t *
135 glue_filesystem(EFI_GUID *proto, EFI_HANDLE dev, fops_fs_glue_t glue)
136 {
137         fileops_t *fops;
138         VOID *intf = NULL;
139         EFI_STATUS status;
140
141         status = uefi_call_wrapper(BS->HandleProtocol, 3, dev, proto, &intf);
142         if (EFI_ERROR(status)) {
143                 ERR_PRT((L"unable to locate %g: should not happen", proto));
144                 return NULL; /* should not happen */
145         }
146         fops = (fileops_t *)alloc(sizeof(*fops), EfiLoaderData);
147         if (fops == NULL) {
148                 ERR_PRT((L"failed to allocate fileops"));
149                 return NULL; /* XXX uninstall protocol */
150         }
151         Memset(fops, 0, sizeof(*fops));
152
153         fops->dev = dev;
154
155         /* initialize private interface now */
156         glue(fops, intf);
157
158         return fops;
159 }
160
161 INTN
162 fops_split_path(CHAR16 *path, CHAR16 *dev)
163 {
164         CHAR16 *p, *d = dev;
165         UINTN len = FILEOPS_DEVNAME_MAXLEN;
166 #       define CHAR_COLON       L':'
167
168         p = StrChr(path, CHAR_COLON);
169         if (p == NULL) {
170                 *dev = CHAR_NULL; 
171                 return 0;
172         }
173         /* copy device part of the name */
174         for (d = dev ; path != p ;) {
175                 if (--len == 0) return -1; /* too long */
176                 *d++ = *path++;
177         }
178         *d = CHAR_NULL;
179         return 0;
180 }
181
182 static device_t *
183 find_device_byname(CHAR16 *dev)
184 {
185         UINTN i;
186         for(i=0; i < ndev; i++) {
187                 if (!StrCmp(dev, dev_tab[i].name)) return dev_tab+i;
188         }
189         return NULL;
190 }
191
192
193 /*
194  * This function is used to walk through the device list and get names.
195  * arguments:
196  *      - pidx = opaque cookie returned by previous call (must be zero for initial call)
197  *      - type is the type of filesystem to look for, e.g., vfat or ext2fs. NULL means ANY
198  *      - maxlen is the size in bytes of the returned string buffer
199  *      - idx is the opaque cookie returned (can be reused by next call)
200  *      - name a string buffer to hold the name of the next matching device
201  *      - dev will receive the EFI_HANDLE corresponding to the device  if not NULL
202  *
203  * return:
204  *      - EFI_INVALID_PARAMETER: when NULL values are passed for required arguments.
205  *      - EFI_NOT_FOUND: when reached the end of the list of invalid cookie
206  *      - EFI_BUFFER_TOO_SMALL is device name does not fit into string buffer
207  *      - EFI_SUCCESS otherwise
208  */
209 EFI_STATUS
210 fops_get_next_device(UINTN pidx, CHAR16 *type, UINTN maxlen, UINTN *idx, CHAR16 *name, EFI_HANDLE *dev)
211 {
212         UINTN i;
213
214         if (name == NULL || idx == NULL) return EFI_INVALID_PARAMETER;
215
216         for(i=pidx; i < ndev; i++) {
217                 if (type == NULL || !StrCmp(dev_tab[i].fops->name, type)) goto found;
218         }
219         return EFI_NOT_FOUND;
220 found:
221         if (StrLen(dev_tab[i].name) >= maxlen) {
222                 *idx = pidx; /* will restart from same place! */
223                 return EFI_BUFFER_TOO_SMALL;
224         }
225         StrCpy(name, dev_tab[i].name);
226         *idx = i+1;
227         if (dev) *dev = dev_tab[i].dev; 
228
229         return EFI_SUCCESS;
230 }
231
232 EFI_STATUS
233 fops_get_device_handle(CHAR16 *name, EFI_HANDLE *dev)
234 {
235         UINTN i;
236
237         if (name == NULL || dev == NULL) return EFI_INVALID_PARAMETER;
238
239         for(i=0; i < ndev; i++) {
240                 if (!StrCmp(dev_tab[i].name, name)) goto found;
241         }
242         return EFI_NOT_FOUND;
243 found:
244         *dev = dev_tab[i].dev; 
245
246         return EFI_SUCCESS;
247 }
248
249
250 EFI_STATUS
251 fops_open(CHAR16 *fullname, fops_fd_t *fd)
252 {
253         fdesc_t *f;
254         EFI_STATUS status;
255         fileops_t *fops;
256         device_t *dev;
257         CHAR16 dev_name[FILEOPS_DEVNAME_MAXLEN];
258         CHAR16 *name;
259
260
261         if (fd == NULL || fullname == NULL || *fullname == CHAR_NULL || fops_split_path(fullname, dev_name) == -1) 
262                 return EFI_INVALID_PARAMETER;
263
264         DBG_PRT((L"fops_open(%s), dev:%s:", fullname ? fullname : L"(NULL)", dev_name));
265
266         name = fullname;
267         if (dev_name[0] == CHAR_NULL) {
268                 if (boot_dev == NULL) return EFI_INVALID_PARAMETER;
269                 fops = boot_dev->fops;
270         } else {
271                 if ((dev=find_device_byname(dev_name)) == NULL) return EFI_INVALID_PARAMETER;
272
273                 name += StrLen(dev_name)+1;
274
275                 fops = dev->fops;
276         }
277
278         if (fops == NULL) return EFI_INVALID_PARAMETER;
279
280         if ((f=fd_alloc()) == NULL) return EFI_OUT_OF_RESOURCES;
281
282         DBG_PRT((L"dev:%s: fullname:%s: name:%s: f=%d", dev_name, fullname, name, f - fd_tab));
283
284         status = fops->open(fops->intf, name, &f->fh);
285         if (EFI_ERROR(status)) goto error;
286
287         f->fops = fops;
288
289         *fd = FDESC_IDX(f);
290
291         return EFI_SUCCESS;
292 error:
293         fd_free(f);
294         return status;
295 }
296
297 EFI_STATUS
298 fops_read(fops_fd_t fd, VOID *buf, UINTN *size)
299 {
300         fdesc_t *f;
301
302         if (buf == NULL || FDESC_INVALID_FD(fd) || size == NULL) return EFI_INVALID_PARAMETER;
303
304         f = FDESC_F(fd);
305
306         return f->fops->read(f->fops->intf, f->fh, buf, size);
307 }
308
309 EFI_STATUS
310 fops_close(fops_fd_t fd)
311 {
312         fdesc_t *f;
313         EFI_STATUS status;
314
315         if (FDESC_INVALID_FD(fd)) return EFI_INVALID_PARAMETER;
316
317         f = FDESC_F(fd);
318
319         status = f->fops->close(f->fops->intf, f->fh);
320
321         fd_free(f);
322
323         return status;
324 }
325
326 EFI_STATUS
327 fops_infosize(fops_fd_t fd, UINT64 *size)
328 {
329         fdesc_t *f;
330
331         if (FDESC_INVALID_FD(fd) || size == NULL) return EFI_INVALID_PARAMETER;
332
333         f = FDESC_F(fd);
334
335         return f->fops->infosize(f->fops->intf, f->fh, size);
336 }
337
338 EFI_STATUS
339 fops_seek(fops_fd_t fd, UINT64 newpos)
340 {
341         fdesc_t *f;
342
343         if (FDESC_INVALID_FD(fd)) return EFI_INVALID_PARAMETER;
344
345         f = FDESC_F(fd);
346
347         return f->fops->seek(f->fops->intf, f->fh, newpos);
348 }
349
350 EFI_STATUS
351 fops_setdefaults(struct config_file *defconf, CHAR16 *kname, UINTN maxlen, CHAR16 *devpath)
352 {
353     INTN i;
354
355 /*
356  * The first default config file is architecture dependent. This is useful
357  * in case of network booting where the server is used for both types of
358  * architectures.
359  */
360 #if defined(CONFIG_ia64)
361 #define FILEOPS_ARCH_DEFAULT_CONFIG     L"elilo-ia64.conf"
362 #elif defined (CONFIG_ia32)
363 #define FILEOPS_ARCH_DEFAULT_CONFIG     L"elilo-ia32.conf"
364 #elif defined (CONFIG_x86_64)
365 #define FILEOPS_ARCH_DEFAULT_CONFIG    L"elilo-x86_64.conf"
366 #else
367 #error "You need to specfy your default arch config file"
368 #endif
369
370 /* 
371  * last resort config file. Common to all architectures
372  */
373 #define FILEOPS_DEFAULT_CONFIG  L"elilo.conf"
374
375 #define FILEOPS_DEFAULT_KERNEL          L"vmlinux"
376
377 #ifdef ELILO_DEBUG
378         if (defconf == NULL || kname == NULL) return EFI_INVALID_PARAMETER;
379 #endif
380
381         for (i=0; i<MAX_DEFAULT_CONFIGS; i++) {
382             defconf[i].fname[0] = CHAR_NULL;
383         }
384
385         if (boot_dev == NULL || boot_dev->fops == NULL) {
386                 if (boot_dev == NULL) 
387                         Print(L"Warning boot device not recognized\n");
388                 else
389                         Print(L"Unknown filesystem on boot device\n");
390
391                 Print(L"Using builtin defaults for kernel and config file\n");
392
393                 StrnCpy(kname, FILEOPS_DEFAULT_KERNEL, maxlen-1);
394         }
395         else {
396                 boot_dev->fops->setdefaults(boot_dev->fops->intf, defconf, kname, maxlen, devpath);
397         }
398         i=0; while (i<MAX_DEFAULT_CONFIGS && defconf[i].fname[0] != CHAR_NULL) i += 1;
399 #ifdef ELILO_DEBUG
400         if ((i+3) >= MAX_DEFAULT_CONFIGS) {
401             Print(L"ERROR: i = %d, MAX_DEFAULT_CONFIGS is not large enough\n", i);
402             return EFI_INVALID_PARAMETER;
403         }
404 #endif
405         StrnCpy(defconf[i].fname, FILEOPS_ARCH_DEFAULT_CONFIG, maxlen-1);
406         StrnCpy(defconf[i+1].fname, FILEOPS_DEFAULT_CONFIG, maxlen-1);
407
408 #ifdef ELILO_DEBUG
409         VERB_PRT(3,Print(L"Default config filename list:\n"));
410         for (i=0; i<MAX_DEFAULT_CONFIGS; i++) {
411                 if (defconf[i].fname[0] == CHAR_NULL) { break; }
412                         VERB_PRT(3,Print(L"\t%s\n", defconf[i].fname));
413         }
414 #endif
415         
416         return EFI_SUCCESS;
417 }
418
419 EFI_STATUS
420 fops_getdefault_path(CHAR16 *path, UINTN maxlen)
421 {
422         if (path == NULL || maxlen == 0) return EFI_INVALID_PARAMETER;
423
424         /*
425          * if underlying filesystem implements the call, then we call
426          * otherwise we return an empty string
427          */
428         if (boot_dev->fops->getdefault_path) 
429                 return boot_dev->fops->getdefault_path(path, maxlen);
430
431         path[0] = CHAR_NULL;
432
433         return EFI_SUCCESS;
434 }
435
436 CHAR16 *
437 fops_bootdev_name(VOID)
438 {
439         return boot_dev ? boot_dev->name : L"not supported";
440 }
441
442 static INTN
443 add_dev_tab(EFI_GUID *proto, EFI_HANDLE boot_handle, UINTN size, fops_fs_glue_t glue)
444 {
445         EFI_STATUS status;
446         EFI_HANDLE *tab;
447         UINTN i;
448         static UINTN idx;
449
450         if (size == 0) return 0;
451
452         tab = (EFI_HANDLE *)alloc(size, EfiLoaderData);
453         if (tab == NULL) {
454                 ERR_PRT((L"failed to allocate handle table"));
455                 return -1;
456         }
457
458         Memset(tab, 0, size);
459
460         /*
461          * get the actual device handles now
462          */
463         status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, proto, NULL, &size, tab);
464         if (status != EFI_SUCCESS) {
465                 ERR_PRT((L"failed to get handles for proto %g size=%d: %r", proto, size, status));
466                 free(tab);
467                 return -1;
468         }
469         size /= sizeof(EFI_HANDLE);
470
471         for(i=0; i < size; i++) {
472                 dev_tab[idx].dev  = tab[i];
473                 dev_tab[idx].fops = glue_filesystem(proto, tab[i], glue);
474
475                 if (tab[i] == boot_handle) boot_dev = dev_tab+idx;
476
477                 /* count the ones we recognized */
478                 if (dev_tab[idx].fops) ndev_boot++;
479
480                 /* assign a generic name for now */
481                 dev_tab[idx].name[0] = L'd';
482                 dev_tab[idx].name[1] = L'e';
483                 dev_tab[idx].name[2] = L'v';
484                 dev_tab[idx].name[3] = L'0' + idx/100;
485                 dev_tab[idx].name[4] = L'0' + (idx%100)/10;
486                 dev_tab[idx].name[5] = L'0' + (idx%100) % 10;
487                 dev_tab[idx].name[6] = CHAR_NULL;
488  
489 #ifdef ELILO_DEBUG
490                 if (elilo_opt.debug) {
491                         EFI_DEVICE_PATH *dp;
492                         CHAR16 *str, *str2;
493                                 
494                         str = NULL;
495                         dp = DevicePathFromHandle(dev_tab[idx].dev);
496                         if (dp) str = DevicePathToStr(dp);
497
498                         str2 = str == NULL ? L"Unknown" : str;
499
500                         DBG_PRT((L"%s : %-8s : %s", dev_tab[idx].name,
501                                 (dev_tab[idx].fops ? dev_tab[idx].fops->name: L"N/A"), str2));
502
503                         if (str) FreePool(str);
504                 }
505 #endif
506
507                 idx++;
508         }
509         free(tab);
510
511         /* remember actual number of bootable devices */
512         ndev = idx;
513
514         return 0;
515 }
516
517 static INTN
518 probe_devname_schemes(device_t *dev_tab, INTN ndev)
519 {
520         devname_scheme_t **p;
521
522         for (p = devname_scheme_tab; *p ; p++) {
523                 if ((*p)->install_scheme(dev_tab, ndev) == 0) goto found;
524         }
525         ERR_PRT((L"No devname schemes worked, using builtin\n"));
526         return -1;
527 found:
528         VERB_PRT(3, Print(L"devname scheme: %s\n", (*p)->name));
529         current_devname_scheme = *p;
530         return 0;
531 }
532
533 static INTN
534 find_filesystems(EFI_HANDLE boot_handle)
535 {
536         UINTN size, total = 0;
537         fileops_fs_t **fs;
538
539         /*
540          * 1st pass, figure out how big a table we need
541          */
542         for(fs = fs_tab; *fs; fs++) {
543                 size = 0;
544                 uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &(*fs)->proto, NULL, &size, NULL);
545                 total += size;
546         }
547         if (total == 0) {
548                 ERR_PRT((L"No useable filesystem found"));
549                 return -1;
550         }
551         total /= sizeof(EFI_HANDLE);
552         DBG_PRT((L"found %d filesystems", total));
553
554         dev_tab = (device_t *)alloc(total*sizeof(device_t), EfiLoaderData);
555         if (dev_tab == NULL) {
556                 ERR_PRT((L"failed to allocate handle table"));
557                 return -1;
558         }
559
560         Memset(dev_tab, 0, total*sizeof(device_t));
561
562         /* 
563          * do a 2nd pass to initialize the table now
564          */
565         for(fs = fs_tab; *fs; fs++) {
566                 size = 0;
567
568                 uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &(*fs)->proto, NULL, &size, NULL);
569                 if (size == 0) continue;
570
571                 add_dev_tab(&(*fs)->proto, boot_handle, size, (*fs)->glue);
572         }
573         probe_devname_schemes(dev_tab, ndev);
574
575         return 0;
576 }
577
578 static INTN
579 fileops_init(VOID)
580 {
581         UINTN i;
582
583         for (i=0; i < FILEOPS_FD_MAX-1; i++) {
584                 fd_tab[i].next = &fd_tab[i+1];
585         }
586         fd_tab[i].next = NULL;
587         free_fd        = fd_tab;
588
589         return 0;
590 }
591
592 /*
593  * both functions will go away once we go with boottime drivers
594  */
595 static INTN
596 install_filesystems(VOID)
597 {
598         fileops_fs_t **fs;
599
600         for(fs = fs_tab; *fs; fs++) (*fs)->install();
601
602         return 0;
603 }
604
605 static INTN
606 uninstall_filesystems(VOID)
607 {
608         fileops_fs_t **fs;
609
610         for(fs = fs_tab; *fs; fs++) (*fs)->uninstall();
611
612         return 0;
613 }
614
615 INTN
616 init_devices(EFI_HANDLE boot_handle)
617 {
618         /* simulate driver installation */
619         install_filesystems();
620
621         /*
622          * now let's do our part
623          */
624         fileops_init();
625
626         return find_filesystems(boot_handle);
627 }
628
629 INTN
630 close_devices(VOID)
631 {
632         INTN i;
633
634         for(i=0; i < FILEOPS_FD_MAX; i++) {
635                 fops_close(i);
636         }
637         free(dev_tab);
638
639         /*
640          * simulate driver removal
641          */
642         uninstall_filesystems();
643
644         return 0;
645 }
646
647 VOID
648 print_devices(VOID)
649 {
650         UINTN idx;
651         EFI_DEVICE_PATH *dp;
652         CHAR16 *str, *str2;
653
654         for(idx=0; idx< ndev; idx++) {
655                 str = NULL;
656
657                 dp = DevicePathFromHandle(dev_tab[idx].dev);
658                 if (dp) str = DevicePathToStr(dp);
659
660                 str2 = str == NULL ? L"Unknown" : str;
661
662                 Print(L"%8s : %-6s : %s\n", dev_tab[idx].name,
663                                 (dev_tab[idx].fops ? dev_tab[idx].fops->name: L"N/A"), str2);
664
665                 if (str) FreePool(str);
666         }
667         Print(L"%d devices available for booting\n", ndev_boot);
668
669         if (boot_dev == NULL) {
670                 Print(L"boot device not detected\n");
671         } else {
672                 Print(L"boot device %s: %s\n", boot_dev->name,
673                                 (boot_dev->fops ? boot_dev->fops->name : L"No file access"));
674         }
675 }