Imported Debian patch 0.4.9-0.sarge.2
[debian/efibootmgr] / src / lib / efi.c
1 /*
2   efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
3
4   Copyright (C) 2001,2003 Dell Computer Corporation <Matt_Domsch@dell.com>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define _FILE_OFFSET_BITS 64
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdint.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <linux/sockios.h>
37 #include <net/if.h>
38
39 typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */
40 typedef __uint32_t u32;         /* ditto */
41 typedef __uint16_t u16;         /* ditto */
42 typedef __uint8_t u8;           /* ditto */
43
44 #include <linux/ethtool.h>
45 #include "efi.h"
46 #include "efichar.h"
47 #include "scsi_ioctls.h"
48 #include "disk.h"
49 #include "efibootmgr.h"
50 #include "efivars_procfs.h"
51 #include "efivars_sysfs.h"
52
53 static struct efivar_kernel_calls *fs_kernel_calls;
54
55 EFI_DEVICE_PATH *
56 load_option_path(EFI_LOAD_OPTION *option)
57 {
58         char *p = (char *) option;
59         return (EFI_DEVICE_PATH *)
60                 (p + sizeof(uint32_t) /* Attributes */
61                  + sizeof(uint16_t)   /* FilePathListLength*/
62                  + efichar_strsize(option->description)); /* Description */
63 }
64
65 char *
66 efi_guid_unparse(efi_guid_t *guid, char *out)
67 {
68         sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
69                 guid->b[3], guid->b[2], guid->b[1], guid->b[0],
70                 guid->b[5], guid->b[4], guid->b[7], guid->b[6],
71                 guid->b[8], guid->b[9], guid->b[10], guid->b[11],
72                 guid->b[12], guid->b[13], guid->b[14], guid->b[15]);
73         return out;
74 }
75
76 void
77 set_fs_kernel_calls()
78 {
79         char name[PATH_MAX];
80         DIR *dir;
81         snprintf(name, PATH_MAX, "%s", SYSFS_DIR_EFI_VARS);
82         dir = opendir(name);
83         if (dir) {
84                 closedir(dir);
85                 fs_kernel_calls = &sysfs_kernel_calls;
86                 return;
87         }
88
89         snprintf(name, PATH_MAX, "%s", PROCFS_DIR_EFI_VARS);
90         dir = opendir(name);
91         if (dir) {
92                 closedir(dir);
93                 fs_kernel_calls = &procfs_kernel_calls;
94                 return;
95         }
96         fprintf(stderr, "Fatal: Couldn't open either sysfs or procfs directories for accessing EFI variables.\n");
97         fprintf(stderr, "Try 'modprobe efivars' as root.\n");
98         exit(1);
99 }
100
101
102
103 static efi_status_t
104 write_variable_to_file(efi_variable_t *var)
105 {
106         int fd, byteswritten;
107         if (!var || !opts.testfile) return EFI_INVALID_PARAMETER;
108
109         printf("Test mode: Writing to %s\n", opts.testfile);
110         fd = creat(opts.testfile, S_IRWXU);
111         if (fd == -1) {
112                 perror("Couldn't write to testfile");
113                 return EFI_INVALID_PARAMETER;
114         }
115
116         byteswritten = write(fd, var, sizeof(*var));
117         if (byteswritten == -1) {
118                 perror("Writing to testfile");
119
120         }
121         close(fd);
122         return EFI_SUCCESS;
123 }
124
125 efi_status_t
126 read_variable(const char *name, efi_variable_t *var)
127 {
128         if (!name || !var) return EFI_INVALID_PARAMETER;
129         return fs_kernel_calls->read(name, var);
130 }
131
132 efi_status_t
133 create_variable(efi_variable_t *var)
134 {
135         if (!var) return EFI_INVALID_PARAMETER;
136         if (opts.testfile) return write_variable_to_file(var);
137         return fs_kernel_calls->create(var);
138 }
139
140 efi_status_t
141 delete_variable(efi_variable_t *var)
142 {
143         if (!var) return EFI_INVALID_PARAMETER;
144         if (opts.testfile) return write_variable_to_file(var);
145         return fs_kernel_calls->delete(var);
146 }
147
148
149 efi_status_t
150 edit_variable(efi_variable_t *var)
151 {
152         char name[PATH_MAX];
153         if (!var) return EFI_INVALID_PARAMETER;
154         if (opts.testfile) return write_variable_to_file(var);
155
156         variable_to_name(var, name);
157         return fs_kernel_calls->edit(name, var);
158 }
159
160 efi_status_t
161 create_or_edit_variable(efi_variable_t *var)
162 {
163         efi_variable_t testvar;
164         char name[PATH_MAX];
165
166         memcpy(&testvar, var, sizeof(*var));
167         variable_to_name(var, name);
168
169         if (read_variable(name, &testvar) == EFI_SUCCESS)
170                 return edit_variable(var);
171         else
172                 return create_variable(var);
173 }
174
175 static int
176 select_boot_var_names(const struct dirent *d)
177 {
178         int num, rc;
179         rc = sscanf(d->d_name, "Boot0%03x-%*s", &num);
180         return rc;
181 }
182
183 int
184 read_boot_var_names(struct dirent ***namelist)
185 {
186         if (!fs_kernel_calls || !namelist) return -1;
187         return scandir(fs_kernel_calls->path,
188                        namelist, select_boot_var_names,
189                        alphasort);
190 }
191
192
193 static int
194 get_edd_version()
195 {
196         efi_status_t status;
197         efi_variable_t var;
198         efi_guid_t guid = BLKX_UNKNOWN_GUID;
199         char name[80], text_guid[40];
200         ACPI_DEVICE_PATH *path = (ACPI_DEVICE_PATH *)&(var.Data);
201         int rc = 0;
202
203         /* Allow global user option override */
204
205         switch (opts.edd_version)
206         {
207         case 0: /* No EDD information */
208                 return 0;
209                 break;
210         case 1: /* EDD 1.0 */
211                 return 1;
212                 break;
213         case 3: /* EDD 3.0 */
214                 return 3;
215                 break;
216         default:
217                 break;
218         }
219
220
221         memset(&var, 0, sizeof(efi_variable_t));
222         efi_guid_unparse(&guid, text_guid);
223         sprintf(name, "blk0-%s", text_guid);
224
225         status = read_variable(name, &var);
226         if (status != EFI_SUCCESS) {
227                 return 0;
228         }
229         if (path->type == 2 && path->subtype == 1) rc = 3;
230         else rc = 1;
231         return rc;
232 }
233
234 /*
235   EFI_DEVICE_PATH, 0x01 (Hardware), 0x04 (Vendor), length 0x0018
236   This needs to know what EFI device has the boot device.
237 */
238 static uint16_t
239 make_edd10_device_path(void *dest, uint32_t hardware_device)
240 {
241         VENDOR_DEVICE_PATH *hw;
242         char buffer[EDD10_HARDWARE_VENDOR_PATH_LENGTH];
243         efi_guid_t guid = EDD10_HARDWARE_VENDOR_PATH_GUID;
244         uint32_t *data;
245         memset(buffer, 0, sizeof(buffer));
246         hw = (VENDOR_DEVICE_PATH *)buffer;
247         data = (uint32_t *)hw->data;
248         hw->type = 0x01; /* Hardware Device Path */
249         hw->subtype = 0x04; /* Vendor */
250         hw->length = EDD10_HARDWARE_VENDOR_PATH_LENGTH;
251         memcpy(&(hw->vendor_guid), &guid, sizeof(guid));
252         *data = hardware_device;
253         memcpy(dest, buffer, hw->length);
254         return hw->length;
255 }
256
257 static uint16_t
258 make_end_device_path(void *dest)
259 {
260         END_DEVICE_PATH p;
261         memset(&p, 0, sizeof(p));
262         p.type = 0x7F; /* End of Hardware Device Path */
263         p.subtype = 0xFF; /* End Entire Device Path */
264         p.length = sizeof(p);
265         memcpy(dest, &p, p.length);
266         return p.length;
267 }
268
269 static uint16_t
270 make_acpi_device_path(void *dest, uint32_t _HID, uint32_t _UID)
271 {
272         ACPI_DEVICE_PATH p;
273         memset(&p, 0, sizeof(p));
274         p.type = 2;
275         p.subtype = 1;
276         p.length = sizeof(p);
277         p._HID = _HID;
278         p._UID = _UID;
279         memcpy(dest, &p, p.length);
280         return p.length;
281 }
282
283 static uint16_t
284 make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype)
285 {
286
287         int i;
288         MAC_ADDR_DEVICE_PATH p;
289         memset(&p, 0, sizeof(p));
290         p.type = 3;
291         p.subtype = 11;
292         p.length = sizeof(p);
293         for (i=0; i < 14; i++) {
294                 p.macaddr[i] = mac[i];
295         }
296         p.iftype = iftype;
297         memcpy(dest, &p, p.length);
298         return p.length;
299 }
300
301 static uint16_t
302 make_pci_device_path(void *dest, uint8_t device, uint8_t function)
303 {
304         PCI_DEVICE_PATH p;
305         memset(&p, 0, sizeof(p));
306         p.type = 1;
307         p.subtype = 1;
308         p.length   = sizeof(p);
309         p.device   = device;
310         p.function = function;
311         memcpy(dest, &p, p.length);
312         return p.length;
313 }
314
315 static uint16_t
316 make_scsi_device_path(void *dest, uint16_t id, uint16_t lun)
317 {
318         SCSI_DEVICE_PATH p;
319         memset(&p, 0, sizeof(p));
320         p.type = 3;
321         p.subtype = 2;
322         p.length   = sizeof(p);
323         p.id       = id;
324         p.lun      = lun;
325         memcpy(dest, &p, p.length);
326         return p.length;
327 }
328
329 static uint16_t
330 make_harddrive_device_path(void *dest, uint32_t num, uint64_t start, uint64_t size,
331                            uint8_t *signature,
332                            uint8_t mbr_type, uint8_t signature_type)
333 {
334         HARDDRIVE_DEVICE_PATH p;
335         memset(&p, 0, sizeof(p));
336         p.type = 4;
337         p.subtype = 1;
338         p.length   = sizeof(p);
339         p.part_num = num;
340         p.start = start;
341         p.size = size;
342         if (signature) memcpy(p.signature, signature, 16);
343         p.mbr_type = mbr_type;
344         p.signature_type = signature_type;
345         memcpy(dest, &p, p.length);
346         return p.length;
347 }
348
349 static uint16_t
350 make_file_path_device_path(void *dest, efi_char16_t *name)
351 {
352         FILE_PATH_DEVICE_PATH *p;
353         char buffer[1024];
354         int namelen  = efichar_strlen(name, -1);
355         int namesize = efichar_strsize(name);
356
357         memset(buffer, 0, sizeof(buffer));
358         p = (FILE_PATH_DEVICE_PATH *)buffer;
359         p->type      = 4;
360         p->subtype   = 4;
361         p->length    = 4 + namesize;
362         efichar_strncpy(p->path_name,
363                         name, namelen);
364
365         memcpy(dest, buffer, p->length);
366         return p->length;
367
368 }
369
370
371
372 static long
373 make_edd30_device_path(int fd, void *buffer)
374 {
375         int rc=0;
376         unsigned char bus=0, device=0, function=0;
377         Scsi_Idlun idlun;
378         unsigned char host=0, channel=0, id=0, lun=0;
379         char *p = buffer;
380
381
382         rc = disk_get_pci(fd, &bus, &device, &function);
383         if (rc) return 0;
384
385         memset(&idlun, 0, sizeof(idlun));
386         rc = get_scsi_idlun(fd, &idlun);
387         if (rc) return 0;
388         idlun_to_components(&idlun, &host, &channel, &id, &lun);
389
390         p += make_acpi_device_path      (p, EISAID_PNP0A03, bus);
391         p += make_pci_device_path       (p, device, function);
392         p += make_scsi_device_path      (p, id, lun);
393         return ((void *)p - buffer);
394 }
395
396 /**
397  * make_disk_load_option()
398  * @disk disk
399  *
400  * Returns 0 on error, length of load option created on success.
401  */
402 char *make_disk_load_option(char *p, char *disk)
403 {
404     int disk_fd=0;
405     char buffer[80];
406     char signature[16];
407     int rc, edd_version=0;
408     uint8_t mbr_type=0, signature_type=0;
409     uint64_t start=0, size=0;
410     efi_char16_t os_loader_path[40];
411
412     memset(signature, 0, sizeof(signature));
413
414     disk_fd = open(opts.disk, O_RDWR);
415     if (disk_fd == -1) {
416         sprintf(buffer, "Could not open disk %s", opts.disk);
417         perror(buffer);
418         return 0;
419     }
420
421     if (opts.edd_version) {
422         edd_version = get_edd_version();
423
424         if (edd_version == 3) {
425             p += make_edd30_device_path(disk_fd, p);
426         }
427         else if (edd_version == 1) {
428             p += make_edd10_device_path(p, opts.edd10_devicenum);
429         }
430     }
431
432     rc = disk_get_partition_info (disk_fd, opts.part,
433                                   &start, &size, signature,
434                                   &mbr_type, &signature_type);
435
436     close(disk_fd);
437
438     if (rc) {
439         fprintf(stderr, "Error: no partition information on disk %s.\n"
440                 "       Cowardly refusing to create a boot option.\n",
441                 opts.disk);
442         return 0;
443     }
444
445     p += make_harddrive_device_path (p, opts.part,
446                                      start, size,
447                                      signature,
448                                      mbr_type, signature_type);
449
450     efichar_from_char(os_loader_path, opts.loader, sizeof(os_loader_path));
451     p += make_file_path_device_path (p, os_loader_path);
452     p += make_end_device_path       (p);
453
454     return(p);
455 }
456
457 /**
458  * make_net_load_option()
459  * @data - load option returned
460  *
461  * Returns 0 on error, length of load option created on success.
462  */
463 char *make_net_load_option(char *p, char *iface)
464 {
465     /* copied pretty much verbatim from the ethtool source */
466     int fd = 0, err;
467     int bus, slot, func;
468     struct ifreq ifr;
469     struct ethtool_drvinfo drvinfo;
470
471     memset(&ifr, 0, sizeof(ifr));
472     strcpy(ifr.ifr_name, iface);
473     drvinfo.cmd = ETHTOOL_GDRVINFO;
474     ifr.ifr_data = (caddr_t)&drvinfo;
475     /* Open control socket */
476     fd = socket(AF_INET, SOCK_DGRAM, 0);
477     if (fd < 0) {
478         perror("Cannot get control socket");
479     }
480     err = ioctl(fd, SIOCETHTOOL, &ifr);
481     if (err < 0) {
482         perror("Cannot get driver information");
483     }
484
485     err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func);
486     if (err == 0) {
487         perror("Couldn't parse device location string.");
488     }
489
490     p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid);
491     p += make_pci_device_path(p, (uint8_t)slot, (uint8_t)func);
492
493     err = ioctl(fd, SIOCGIFHWADDR, &ifr);
494     if (err < 0) {
495         perror("Cannot get hardware address.");
496     }
497
498     p += make_mac_addr_device_path(p, ifr.ifr_ifru.ifru_hwaddr.sa_data, 0);
499     p += make_end_device_path       (p);
500
501     return(p);
502 }
503
504 /**
505  * make_linux_load_option()
506  * @data - load option returned
507  *
508  * Returns 0 on error, length of load option created on success.
509  */
510 static unsigned long
511 make_linux_load_option(void *data)
512 {
513         EFI_LOAD_OPTION *load_option = data;
514         char *p = data, *q;
515         efi_char16_t description[64];
516         unsigned long datasize=0;
517
518         /* Write Attributes */
519         if (opts.active) load_option->attributes = LOAD_OPTION_ACTIVE;
520         else             load_option->attributes = 0;
521
522         p += sizeof(uint32_t);
523         /* skip writing file_path_list_length */
524         p += sizeof(uint16_t);
525         /* Write description.  This is the text that appears on the screen for the load option. */
526         memset(description, 0, sizeof(description));
527         efichar_from_char(description, opts.label, sizeof(description));
528         efichar_strncpy(load_option->description, description, sizeof(description));
529         p += efichar_strsize(load_option->description);
530
531         q = p;
532
533         if (opts.iface) {
534               p = (char *)make_net_load_option(p, opts.iface);
535         }
536
537         else {
538               p = (char *)make_disk_load_option(p, opts.iface);
539         }
540
541         load_option->file_path_list_length = p - q;
542
543         datasize = (uint8_t *)p - (uint8_t *)data;
544         return datasize;
545 }
546
547 /*
548  * append_extra_args()
549  * appends all arguments from argv[] not snarfed by getopt
550  * as one long string onto data, up to maxchars.  allow for nulls
551  */
552
553 static unsigned long
554 append_extra_args_ascii(void *data, unsigned long maxchars)
555 {
556         char *p = data;
557         int i, appended=0;
558         unsigned long usedchars=0;
559         if (!data) return 0;
560
561
562         for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
563                 p = strncpy(p, opts.argv[i], maxchars-usedchars-1);
564                 p += strlen(p);
565                 appended=1;
566
567                 usedchars = p - (char *)data;
568
569                 /* Put a space between args */
570                 if (i < (opts.argc-1)) {
571
572                         p = strncpy(p, " ", maxchars-usedchars-1);
573                         p += strlen(p);
574                         usedchars = p - (char *)data;
575                 }
576
577         }
578         /* Remember the NULL */
579         if (appended) return strlen(data) + 1;
580         return 0;
581 }
582
583 static unsigned long
584 append_extra_args_unicode(void *data, unsigned long maxchars)
585 {
586         char *p = data;
587         int i, appended=0;
588         unsigned long usedchars=0;
589         if (!data) return 0;
590
591
592         for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
593                 p += efichar_from_char((efi_char16_t *)p, opts.argv[i],
594                                        maxchars-usedchars);
595                 usedchars = efichar_strsize(data) - sizeof(efi_char16_t);
596                 appended=1;
597
598                 /* Put a space between args */
599                 if (i < (opts.argc-1)) {
600                         p += efichar_from_char((efi_char16_t *)p, " ",
601                                                maxchars-usedchars);
602                         usedchars = efichar_strsize(data) -
603                                 sizeof(efi_char16_t);
604                 }
605         }
606
607         if (appended) return efichar_strsize( (efi_char16_t *)data );
608         return 0;
609 }
610
611
612 static unsigned long
613 append_extra_args(void *data, unsigned long maxchars)
614 {
615         if (opts.unicode)
616           return append_extra_args_unicode(data, maxchars);
617         else
618           return append_extra_args_ascii(data, maxchars);
619 }
620
621
622
623 int
624 make_linux_efi_variable(efi_variable_t *var,
625                         unsigned int free_number)
626 {
627         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
628         char buffer[16];
629         unsigned char *optional_data=NULL;
630         unsigned long load_option_size = 0, opt_data_size=0;
631
632         memset(buffer,    0, sizeof(buffer));
633
634         /* VariableName needs to be BootXXXX */
635         sprintf(buffer, "Boot%04x", free_number);
636
637         efichar_from_char(var->VariableName, buffer, 1024);
638
639         memcpy(&(var->VendorGuid), &guid, sizeof(guid));
640         var->Attributes =
641                 EFI_VARIABLE_NON_VOLATILE |
642                 EFI_VARIABLE_BOOTSERVICE_ACCESS |
643                 EFI_VARIABLE_RUNTIME_ACCESS;
644
645         /* Set Data[] and DataSize */
646
647         load_option_size =  make_linux_load_option(var->Data);
648
649         if (!load_option_size) return 0;
650
651         /* Set OptionalData (passed as binary to the called app) */
652         optional_data = var->Data + load_option_size;
653         opt_data_size = append_extra_args(optional_data,
654                                   sizeof(var->Data) - load_option_size);
655         var->DataSize = load_option_size + opt_data_size;
656         return var->DataSize;
657 }
658
659
660 int
661 variable_to_name(efi_variable_t *var, char *name)
662 {
663         char *p = name;
664         efichar_to_char(p, var->VariableName, PATH_MAX);
665         p += strlen(p);
666         p += sprintf(p, "-");
667         efi_guid_unparse(&var->VendorGuid, p);
668         return strlen(name);
669 }