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