Imported Upstream version 0.4.2
[debian/efibootmgr] / src / efibootmgr / efibootmgr.c
1 /*
2   efibootmgr.c - Manipulates EFI variables as exported in /proc/efi/vars
3
4   Copyright (C) 2001 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   This must tie the EFI_DEVICE_PATH to /boot/efi/elilo.efi
22   The  EFI_DEVICE_PATH will look something like:
23     ACPI device path, length 12 bytes
24     Hardware Device Path, PCI, length 6 bytes
25     Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ??
26     Media Device Path, Hard Drive, partition XX, length 30 bytes
27     Media Device Path, File Path, length ??
28     End of Hardware Device Path, length 4
29     Arguments passed to elilo, as UCS-2 characters, length ??
30
31 */
32
33 #define _GNU_SOURCE
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <stdint.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <dirent.h>
43 #include <unistd.h>
44 #include <getopt.h>
45 #include "list.h"
46 #include "efi.h"
47 #include "efichar.h"
48 #include "unparse_path.h"
49 #include "disk.h"
50 #include "efibootmgr.h"
51
52 #ifndef EFIBOOTMGR_VERSION
53 #define EFIBOOTMGR_VERSION "unknown (fix Makefile!)"
54 #endif
55
56
57 typedef struct _var_entry {
58         struct dirent *name;
59         uint16_t       num;
60         efi_variable_t var_data;
61         list_t         list;
62 } var_entry_t;
63
64
65 /* global variables */
66 static  LIST_HEAD(boot_entry_list);
67 static  LIST_HEAD(blk_list);
68 efibootmgr_opt_t opts;
69
70 static inline void
71 var_num_from_name(const char *pattern, char *name, uint16_t *num)
72 {
73         sscanf(name, pattern, num);
74 }
75
76 static int
77 select_boot_var_names(const struct dirent *d)
78 {
79         int num, rc;
80         rc = sscanf(d->d_name, "Boot0%03x-%*s", &num);
81         return rc;
82 }
83
84 #if 0
85 static int
86 select_blk_var_names(const struct dirent *d)
87 {
88         int num;
89         return sscanf(d->d_name, "blk%x-%*s", &num);
90 }
91 #endif
92
93 static int
94 read_boot_var_names(struct dirent ***namelist)
95 {
96         int n;
97         n = scandir(PROC_DIR_EFI_VARS, namelist, select_boot_var_names, alphasort);
98         if (n < 0) {
99                 perror("scandir " PROC_DIR_EFI_VARS);
100                 fprintf(stderr, "You must 'modprobe efivars' before running efibootmgr.\n");
101         }
102         return n;
103 }
104
105 #if 0
106 static int
107 read_blk_var_names(struct dirent ***namelist)
108 {
109         int n;
110         n = scandir(PROC_DIR_EFI_VARS, namelist, select_blk_var_names, alphasort);
111         if (n < 0)
112                 perror("scandir");
113         return n;
114 }
115
116 static int
117 dirent_list_length(struct dirent **namelist)
118 {
119         int i;
120         if (!namelist) return 0;
121         for (i=0; namelist[i]; i++);
122         return i;
123 }
124 #endif
125
126 static void
127 read_vars(struct dirent **namelist,
128           int num_boot_names,
129           list_t *head)
130 {
131         efi_status_t status;
132         var_entry_t *entry;
133         int i;
134
135         if (!namelist) return;
136
137         for (i=0; i < num_boot_names; i++)
138         {
139                 if (namelist[i]) {
140                         entry = malloc(sizeof(var_entry_t));
141                         if (!entry) return;
142                         memset(entry, 0, sizeof(var_entry_t));
143
144                         status = read_variable(namelist[i]->d_name,
145                                                &entry->var_data);
146                         if (status != EFI_SUCCESS) break;
147                         entry->name = namelist[i];
148                         list_add_tail(&entry->list, head);
149                 }
150         }
151         return;
152 }
153
154
155
156
157
158 static void
159 free_dirents(struct dirent **ptr, int num_dirents)
160 {
161         int i;
162         if (!ptr) return;
163         for (i=0; i < num_dirents; i++) {
164                 if (ptr[i]) {
165                         free(ptr[i]);
166                         ptr[i] = NULL;
167                 }
168         }
169         free(ptr);
170 }
171
172
173
174 static int
175 compare(const void *a, const void *b)
176 {
177         int rc = -1;
178         uint32_t n1, n2;
179         memcpy(&n1, a, sizeof(n1));
180         memcpy(&n2, b, sizeof(n2));
181         if (n1 < n2) rc = -1;
182         if (n1 == n2) rc = 0;
183         if (n2 > n2) rc = 1;
184         return rc;
185 }
186
187
188 /*
189   Return an available boot variable number,
190   or -1 on failure.
191 */
192 static int
193 find_free_boot_var(list_t *boot_list)
194 {
195         int num_vars=0, i=0, found;
196         uint16_t *vars, free_number;
197         list_t *pos;
198         var_entry_t *boot;
199         list_for_each(pos, boot_list) {
200                 num_vars++;
201         }
202         vars = malloc(sizeof(uint16_t) * num_vars);
203         if (!vars) return -1;
204         memset(vars, 0, sizeof(uint16_t) * num_vars);
205
206         list_for_each(pos, boot_list) {
207                 boot = list_entry(pos, var_entry_t, list);
208                 vars[i] = boot->num;
209                         i++;
210         }
211         qsort(vars, i, sizeof(uint16_t), compare);
212         found = 1;
213
214         num_vars = i;
215         for (free_number = 0; free_number < num_vars && found; free_number++) {
216                 found = 0;
217                 list_for_each(pos, boot_list) {
218                         boot = list_entry(pos, var_entry_t, list);
219                         if (boot->num == free_number) {
220                                 found = 1;
221                                 break;
222                         }
223                 }
224                 if (!found) break;
225         }
226         if (found && num_vars) free_number = vars[num_vars-1] + 1;
227         free(vars);
228         return free_number;
229 }
230
231
232 static void
233 warn_duplicate_name(list_t *boot_list)
234 {
235         list_t *pos;
236         var_entry_t *boot;
237         EFI_LOAD_OPTION *load_option;
238
239         list_for_each(pos, boot_list) {
240                 boot = list_entry(pos, var_entry_t, list);
241                 load_option = (EFI_LOAD_OPTION *)
242                         boot->var_data.Data;
243                 if (!efichar_char_strcmp(opts.label,
244                                          load_option->description)) {
245                         fprintf(stderr, "** Warning ** : Boot%04x has same label %s\n",
246                                boot->num,
247                                opts.label);
248                 }
249         }
250 }
251
252
253 static var_entry_t *
254 make_boot_var(list_t *boot_list)
255 {
256         var_entry_t *boot;
257         int free_number;
258
259         if (opts.bootnum == -1) free_number = find_free_boot_var(boot_list);
260         else                    free_number = opts.bootnum;
261
262         if (free_number == -1) return NULL;
263
264         /* Create a new var_entry_t object
265            and populate it.
266         */
267
268         boot = malloc(sizeof(*boot));
269         if (!boot) return NULL;
270         memset(boot, 0, sizeof(*boot));
271         boot->num = free_number;
272         if (!make_linux_efi_variable(&boot->var_data, free_number)) {
273                 free(boot);
274                 return NULL;
275         }
276         write_variable(&boot->var_data);
277         list_add_tail(&boot->list, boot_list);
278         return boot;
279 }
280
281 static efi_status_t
282 read_boot_order(efi_variable_t *boot_order)
283 {
284         efi_status_t status;
285         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
286         char boot_order_name[80], text_uuid[40];
287         efi_guid_unparse(&guid, text_uuid);
288
289         memset(boot_order, 0, sizeof(*boot_order));
290         sprintf(boot_order_name, "BootOrder-%s", text_uuid);
291
292         status = read_variable(boot_order_name, boot_order);
293         if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
294                 return status;
295
296         if (status == EFI_NOT_FOUND) {
297                 /* Create it */
298                 efichar_from_char(boot_order->VariableName, "BootOrder",
299                                   1024);
300                 memcpy(&boot_order->VendorGuid, &guid, sizeof(guid));
301                 boot_order->Attributes = EFI_VARIABLE_NON_VOLATILE 
302                         | EFI_VARIABLE_BOOTSERVICE_ACCESS
303                         | EFI_VARIABLE_RUNTIME_ACCESS;
304                 return status;
305         }
306         return EFI_SUCCESS;
307 }
308
309
310
311
312 static efi_status_t
313 add_to_boot_order(uint16_t num)
314 {
315         efi_status_t status;
316         efi_variable_t boot_order;
317         uint64_t new_data_size;
318         uint16_t *new_data, *old_data;
319
320         status = read_boot_order(&boot_order);
321         if (status != EFI_SUCCESS) return status;
322
323         /* We've now got an array (in boot_order.Data) of the
324            boot order.  First add our entry, then copy the old array.
325         */
326         old_data = (uint16_t *)&(boot_order.Data);
327         new_data_size = boot_order.DataSize + sizeof(uint16_t);
328         new_data = malloc(new_data_size);
329
330         new_data[0] = num;
331         memcpy(new_data+1, old_data, boot_order.DataSize);
332
333         /* Now new_data has what we need */
334         memcpy(&(boot_order.Data), new_data, new_data_size);
335         boot_order.DataSize = new_data_size;
336         return write_variable(&boot_order);
337 }
338
339
340 static efi_status_t
341 remove_from_boot_order(uint16_t num)
342 {
343         efi_status_t status;
344         efi_variable_t boot_order;
345         uint64_t new_data_size;
346         uint16_t *new_data, *old_data;
347         int old_i,new_i;
348
349         status = read_boot_order(&boot_order);
350         if (status != EFI_SUCCESS) return status;
351
352         /* If it's empty, yea! */
353         if (!boot_order.DataSize) return EFI_SUCCESS;
354
355         /* We've now got an array (in boot_order.Data) of the
356            boot order.  Simply copy the array, skipping the
357            entry we're deleting.
358         */
359         old_data = (uint16_t *)&(boot_order.Data);
360         /* Start with the same size */
361         new_data_size = boot_order.DataSize;
362         new_data = malloc(new_data_size);
363         for (old_i=0,new_i=0;
364              old_i < boot_order.DataSize / sizeof(uint16_t);
365              old_i++) {
366                 if (old_data[old_i] != num) {
367                                 /* Copy this value */
368                         new_data[new_i] = old_data[old_i];
369                         new_i++;
370                 }
371         }
372
373         /* Now new_data has what we need */
374         new_data_size = new_i * sizeof(uint16_t);
375         memset(&(boot_order.Data), 0, boot_order.DataSize);
376         memcpy(&(boot_order.Data), new_data, new_data_size);
377         boot_order.DataSize = new_data_size;
378
379         return write_variable(&boot_order);
380 }
381
382 static int
383 read_boot_current()
384 {
385         efi_status_t status;
386         efi_variable_t boot_next;
387         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
388         char boot_next_name[80], text_uuid[40];
389         uint16_t *n = (uint16_t *)(boot_next.Data);
390
391         efi_guid_unparse(&guid, text_uuid);
392
393         memset(&boot_next, 0, sizeof(boot_next));
394         sprintf(boot_next_name, "BootCurrent-%s", text_uuid);
395
396         status = read_variable(boot_next_name, &boot_next);
397         if (status) return -1;
398
399         return *n;
400 }
401
402 static int
403 read_boot_next()
404 {
405         efi_status_t status;
406         efi_variable_t boot_next;
407         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
408         char boot_next_name[80], text_uuid[40];
409         uint16_t *n = (uint16_t *)(boot_next.Data);
410
411         efi_guid_unparse(&guid, text_uuid);
412
413         memset(&boot_next, 0, sizeof(boot_next));
414         sprintf(boot_next_name, "BootNext-%s", text_uuid);
415
416         status = read_variable(boot_next_name, &boot_next);
417         if (status) return -1;
418
419         return *n;
420 }
421
422
423 static efi_status_t
424 set_boot_next(uint16_t num)
425 {
426         efi_variable_t var;
427         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
428         uint16_t *n = (uint16_t *)var.Data;
429
430         memset(&var, 0, sizeof(var));
431
432         efichar_from_char(var.VariableName, "BootNext",
433                           1024);
434         memcpy(&var.VendorGuid, &guid, sizeof(guid));
435         *n = num;
436         var.DataSize = sizeof(uint16_t);
437         var.Attributes = EFI_VARIABLE_NON_VOLATILE 
438                 | EFI_VARIABLE_BOOTSERVICE_ACCESS
439                 | EFI_VARIABLE_RUNTIME_ACCESS;
440         return write_variable(&var);
441 }
442
443 static efi_status_t
444 delete_boot_next()
445 {
446         efi_variable_t var;
447         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
448
449         memset(&var, 0, sizeof(var));
450
451         efichar_from_char(var.VariableName, "BootNext",
452                           1024);
453         memcpy(&var.VendorGuid, &guid, sizeof(guid));
454         return write_variable(&var);
455 }
456
457
458 static efi_status_t
459 delete_boot_var(uint16_t num)
460 {
461         efi_status_t status;
462         efi_variable_t var;
463         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
464         char name[80];
465         list_t *pos, *n;
466         var_entry_t *boot;
467         sprintf(name, "Boot%04x", num);
468
469         memset(&var, 0, sizeof(var));
470
471         efichar_from_char(var.VariableName, name, 1024);
472         memcpy(&var.VendorGuid, &guid, sizeof(guid));
473         status = write_variable(&var);
474
475         if (status) return status;
476
477         list_for_each_safe(pos, n, &boot_entry_list) {
478                 boot = list_entry(pos, var_entry_t, list);
479                 if (boot->num == num) {
480                         status = remove_from_boot_order(num);
481                         if (status) return status;
482                         list_del(&(boot->list));
483                         break; /* short-circuit since it was found */
484                 }
485         }
486         return EFI_SUCCESS;
487 }
488
489
490 static void
491 set_var_nums(const char *pattern, list_t *list)
492 {
493         list_t *pos;
494         var_entry_t *var;
495         int num=0, rc;
496
497         list_for_each(pos, list) {
498                 var = list_entry(pos, var_entry_t, list);
499                 rc = sscanf(var->name->d_name, pattern, &num);
500                 if (rc == 1) var->num = num;
501         }
502 }
503
504 #if 0
505 static efi_variable_t *
506 find_pci_scsi_disk_blk(int fd, int bus, int device, int func,
507                        list_t *blk_list)
508 {
509         list_t *pos;
510         int rc;
511         Scsi_Idlun idlun;
512         unsigned char host, channel, id, lun;
513         var_entry_t *blk;
514         efi_variable_t *blk_var;
515         long size = 0;
516
517         memset(&idlun, 0, sizeof(idlun));
518         rc = get_scsi_idlun(fd, &idlun);
519         if (rc) return NULL;
520
521         rc = disk_get_size(fd, &size);
522
523         idlun_to_components(&idlun, &host, &channel, &id, &lun);
524
525         list_for_each(pos, blk_list) {
526                 blk = list_entry(pos, var_entry_t, list);
527                 blk_var = blk->var_data;
528
529                 if (!compare_pci_scsi_disk_blk(blk_var,
530                                                bus, device, func,
531                                                host, channel, id, lun,
532                                                0, size)) {
533                         return blk_var;
534                 }
535         }
536         return NULL;
537 }
538
539
540
541
542 /* The right blkX variable contains:
543    1) the PCI and SCSI information for the disk passed in disk_name
544    2) Does not contain a partition field 
545 */
546
547
548 static efi_variable_t *
549 find_disk_blk(char *disk_name, list_t *blk_list)
550 {
551         efi_variable_t *disk_blk = NULL;
552         int fd, rc;
553         unsigned char bus=0,device=0,func=0;
554         int interface_type=interface_type_unknown;
555         unsigned int controllernum=0, disknum=0;
556         unsigned char part=0;
557
558         fd = open(disk_name, O_RDONLY|O_DIRECT);
559         rc = disk_get_pci(fd, &bus, &device, &func);
560         if (rc) {
561                 fprintf(stderr, "disk_get_pci() failed.\n");
562                 return NULL;
563         }
564         rc = disk_info_from_fd(fd,
565                                &interface_type,
566                                &controllernum,
567                                &disknum,
568                                &part);
569         if (rc) {
570                 fprintf(stderr, "disk_info_from_fd() failed.\n");
571                 return NULL;
572         }
573         switch (interface_type)
574         {
575         case scsi:
576                 return find_pci_scsi_disk_blk(fd,bus,device,func,blk_list);
577                 break;
578         case ata:
579                 return find_pci_ata_disk_blk(fd,bus,device,func,blk_list);
580                 break;
581         case i2o:
582                 return find_pci_i2o_disk_blk(fd,bus,device,func,blk_list);
583                 break;
584         case md:
585                 return find_pci_md_disk_blk(fd,bus,device,func,blk_list);
586                 break;
587         default:
588                 break;
589         }
590         return NULL;
591 }
592 #endif
593
594 static void
595 unparse_boot_order(uint16_t *order, int length)
596 {
597         int i;
598         printf("BootOrder: ");
599         for (i=0; i<length; i++) {
600                 printf("%04x", order[i]);
601                 if (i < (length-1))
602                         printf(",");
603         }
604         printf("\n");
605 }
606
607 static int
608 parse_boot_order(char *buffer, uint16_t *order, int length)
609 {
610         int i;
611         int num, rc;
612
613         for (i=0; i<length && *buffer; i++) {
614                 rc = sscanf(buffer, "%x", &num);
615                 if (rc == 1) order[i] = num & 0xFFFF;
616                 /* Advance to the comma */ 
617                 while (*buffer && *buffer != ',') buffer++;
618                 /* Advance through the comma(s) */
619                 while (*buffer && *buffer == ',') buffer++;
620         }
621         return i;
622 }
623
624 static efi_status_t
625 set_boot_order()
626 {
627         efi_variable_t var;
628         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
629         uint16_t *n = (uint16_t *)var.Data;
630
631         if (!opts.bootorder) return EFI_SUCCESS;
632
633         memset(&var, 0, sizeof(var));
634
635         efichar_from_char(var.VariableName, "BootOrder",
636                           1024);
637         memcpy(&var.VendorGuid, &guid, sizeof(guid));
638         var.Attributes = EFI_VARIABLE_NON_VOLATILE
639                 | EFI_VARIABLE_BOOTSERVICE_ACCESS
640                 | EFI_VARIABLE_RUNTIME_ACCESS;
641
642         var.DataSize = parse_boot_order(opts.bootorder, n, 1024/sizeof(uint16_t)) * sizeof(uint16_t);
643         return write_variable(&var);
644 }
645
646 static void
647 show_boot_vars()
648 {
649         list_t *pos;
650         var_entry_t *boot;
651         char description[80];
652         EFI_LOAD_OPTION *load_option;
653         EFI_DEVICE_PATH *path;
654         char text_path[1024], *p;
655         unsigned long optional_data_len=0;
656
657         list_for_each(pos, &boot_entry_list) {
658                 boot = list_entry(pos, var_entry_t, list);
659                 load_option = (EFI_LOAD_OPTION *)
660                         boot->var_data.Data;
661                 efichar_to_char(description,
662                                 load_option->description, sizeof(description));
663                 memset(text_path, 0, sizeof(text_path));
664                 path = load_option_path(load_option);
665                 printf("Boot%04x", boot->num);
666                 if (load_option->attributes & LOAD_OPTION_ACTIVE)
667                         printf("* ");
668                 else    printf("  ");
669                 printf("%s", description);
670
671                 if (opts.verbose) {
672                         unparse_path(text_path, path,
673                                      load_option->file_path_list_length);
674                         /* Print optional data */
675                         optional_data_len =
676                                 boot->var_data.DataSize -
677                                 load_option->file_path_list_length -
678                                 ((char *)path - (char *)load_option);
679                         if (optional_data_len) {
680                                 p = text_path;
681                                 p += strlen(text_path);
682                                 unparse_raw_text(p, ((uint8_t *)path) +
683                                                  load_option->file_path_list_length,
684                                                  optional_data_len);
685                         }
686
687                         printf("\t%s", text_path);
688                 }
689                 printf("\n");
690         }
691 }
692
693
694
695 static void
696 show_boot_order()
697 {
698         efi_status_t status;
699         efi_variable_t boot_order;
700         uint16_t *data;
701
702         status = read_boot_order(&boot_order);
703
704         if (status != EFI_SUCCESS) {
705                 perror("show_boot_order()");
706                 return;
707         }
708
709         /* We've now got an array (in boot_order.Data) of the
710            boot order.  First add our entry, then copy the old array.
711         */
712         data = (uint16_t *)&(boot_order.Data);
713         if (boot_order.DataSize)
714                 unparse_boot_order(data, boot_order.DataSize / sizeof(uint16_t));
715
716 }
717
718 static efi_status_t
719 set_active_state()
720 {
721         list_t *pos;
722         var_entry_t *boot;
723         EFI_LOAD_OPTION *load_option;
724
725         list_for_each(pos, &boot_entry_list) {
726                 boot = list_entry(pos, var_entry_t, list);
727                 load_option = (EFI_LOAD_OPTION *)
728                         boot->var_data.Data;
729                 if (boot->num == opts.bootnum) {
730                         if (opts.active == 1) {
731                                 if (load_option->attributes
732                                     & LOAD_OPTION_ACTIVE) return EFI_SUCCESS;
733                                 else {
734                                         load_option->attributes
735                                                 |= LOAD_OPTION_ACTIVE;
736                                         return write_variable(&boot->var_data);
737                                 }
738                         }
739                         else if (opts.active == 0) {
740                                 if (!(load_option->attributes
741                                       & LOAD_OPTION_ACTIVE))
742                                         return EFI_SUCCESS;
743                                 else {
744                                         load_option->attributes
745                                                 &= ~LOAD_OPTION_ACTIVE;
746                                         return write_variable(&boot->var_data);
747                                 }
748                         }
749                 }
750         }
751         return EFI_SUCCESS;
752 }
753
754
755
756 static efi_status_t
757 delete_boot_order()
758 {
759         efi_variable_t var;
760         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
761
762         memset(&var, 0, sizeof(var));
763
764         efichar_from_char(var.VariableName, "BootOrder",
765                           1024);
766         memcpy(&var.VendorGuid, &guid, sizeof(guid));
767         return write_variable(&var);
768 }
769
770
771 static void
772 usage()
773 {
774         printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION);
775         printf("usage: efibootmgr [options]\n");
776         printf("\t-a | --active         sets bootnum active\n");
777         printf("\t-A | --inactive       sets bootnum inactive\n");
778         printf("\t-b | --bootnum XXXX   modify BootXXXX (hex)\n");
779         printf("\t-B | --delete-bootnum delete bootnum (hex)\n");
780         printf("\t-c | --create         create new variable bootnum and add to bootorder\n");
781         printf("\t-d | --disk disk       (defaults to /dev/sda) containing loader\n");
782         printf("\t-e | --edd [1|3|-1]   force EDD 1.0 or 3.0 creation variables, or guess\n");
783         printf("\t-E | --device num      EDD 1.0 device number (defaults to 0x80)\n");
784         printf("\t-g | --gpt            force disk with invalid PMBR to be treated as GPT\n");
785         printf("\t-H | --acpi_hid XXXX  set the ACPI HID (used with -i)\n");
786         printf("\t-i | --iface name     create a netboot entry for the named interface\n");
787         printf("\t-l | --loader name     (defaults to \\elilo.efi)\n");
788         printf("\t-L | --label label     Boot manager display label (defaults to \"Linux\")\n");
789         printf("\t-n | --bootnext XXXX   set BootNext to XXXX (hex)\n");
790         printf("\t-N | --delete-bootnext delete BootNext\n");
791         printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,...     explicitly set BootOrder (hex)\n");
792         printf("\t-O | --delete-bootorder delete BootOrder\n");
793         printf("\t-p | --part part        (defaults to 1) containing loader\n");
794         printf("\t-q | --quiet            be quiet\n");
795         printf("\t-t | --test filename    don't write to NVRAM, write to filename.\n");
796         printf("\t-u | --unicode | --UCS-2  pass extra args as UCS-2 (default is ASCII)\n");
797         printf("\t-U | --acpi_uid XXXX    set the ACPI UID (used with -i)\n");
798         printf("\t-v | --verbose          print additional information\n");
799         printf("\t-V | --version          return version and exit\n");
800         printf("\t-w | --write-signature  write unique sig to MBR if needed\n");
801 }
802
803 static void
804 set_default_opts()
805 {
806         memset(&opts, 0, sizeof(opts));
807         opts.bootnum         = -1;   /* auto-detect */
808         opts.bootnext        = -1;   /* Don't set it */
809         opts.active          = -1;   /* Don't set it */
810         opts.edd10_devicenum = 0x80;
811         opts.loader          = "\\elilo.efi";
812         opts.label           = "Linux";
813         opts.disk            = "/dev/sda";
814         opts.iface           = NULL;
815         opts.part            = 1;
816         opts.acpi_hid        = -1;
817         opts.acpi_uid        = -1;
818 }
819
820 static void
821 parse_opts(int argc, char **argv)
822 {
823         int c, num, rc;
824         int option_index = 0;
825
826         while (1)
827         {
828                 static struct option long_options[] =
829                         /* name, has_arg, flag, val */
830                 {
831                         {"active",                 no_argument, 0, 'a'},
832                         {"inactive",               no_argument, 0, 'A'},
833                         {"bootnum",          required_argument, 0, 'b'},
834                         {"delete-bootnum",         no_argument, 0, 'B'},
835                         {"create",                 no_argument, 0, 'c'},
836                         {"disk",             required_argument, 0, 'd'},
837                         {"iface",            required_argument, 0, 'i'},
838                         {"acpi_hid",         required_argument, 0, 'H' },
839                         {"edd-device",       required_argument, 0, 'E'},
840                         {"edd30",            required_argument, 0, 'e'},
841                         {"gpt",                    no_argument, 0, 'g'},
842                         {"loader",           required_argument, 0, 'l'},
843                         {"label",            required_argument, 0, 'L'},
844                         {"bootnext",         required_argument, 0, 'n'},
845                         {"delete-bootnext",        no_argument, 0, 'N'},
846                         {"bootorder",        required_argument, 0, 'o'},
847                         {"delete-bootorder",       no_argument, 0, 'O'},
848                         {"part",             required_argument, 0, 'p'},
849                         {"quiet",                  no_argument, 0, 'q'},
850                         {"test",             required_argument, 0, 't'},
851                         {"unicode",                no_argument, 0, 'u'},
852                         {"UCS-2",                  no_argument, 0, 'u'},
853                         {"acpi_uid",         required_argument, 0, 'U' },
854                         {"verbose",          optional_argument, 0, 'v'},
855                         {"version",                no_argument, 0, 'V'},
856                         {"write-signature",        no_argument, 0, 'w'},
857                         {0, 0, 0, 0}
858                 };
859
860                 c = getopt_long (argc, argv,
861                                  "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:uU:v::Vw",
862                                  long_options, &option_index);
863                 if (c == -1)
864                         break;
865
866                 switch (c)
867                 {
868                 case 'a':
869                         opts.active = 1;
870                         break;
871                 case 'A':
872                         opts.active = 0;
873                         break;
874                 case 'B':
875                         opts.delete_boot = 1;
876                         break;
877                 case 'b':
878                         rc = sscanf(optarg, "%x", &num);
879                         if (rc == 1) opts.bootnum = num;
880                         break;
881                 case 'c':
882                         opts.create = 1;
883                         break;
884                 case 'd':
885                         opts.disk = optarg;
886                         break;
887                 case 'e':
888                         rc = sscanf(optarg, "%d", &num);
889                         if (rc == 1) opts.edd_version = num;
890                         break;
891                 case 'E':
892                         rc = sscanf(optarg, "%x", &num);
893                         if (rc == 1) opts.edd10_devicenum = num;
894                         break;
895                 case 'g':
896                         opts.forcegpt = 1;
897                         break;
898                 case 'H':
899                         rc = sscanf(optarg, "%x", &num);
900                         if (rc == 1) opts.acpi_hid = num;
901                         break;
902                 case 'i':
903                         opts.iface = optarg;
904                         break;
905                 case 'l':
906                         opts.loader = optarg;
907                         break;
908                 case 'L':
909                         opts.label = optarg;
910                         break;
911                 case 'N':
912                         opts.delete_bootnext = 1;
913                         break;
914                 case 'n':
915                         rc = sscanf(optarg, "%x", &num);
916                         if (rc == 1) opts.bootnext = num;
917                         break;
918                 case 'o':
919                         opts.bootorder = optarg;
920                         break;
921                 case 'O':
922                         opts.delete_bootorder = 1;
923                         break;
924                 case 'p':
925                         rc = sscanf(optarg, "%u", &num);
926                         if (rc == 1) opts.part = num;
927                         break;
928                 case 'q':
929                         opts.quiet = 1;
930                         break;
931                 case 't':
932                         opts.testfile = optarg;
933                         break;
934                 case 'u':
935                         opts.unicode = 1;
936                         break;
937
938                 case 'U':
939                         rc = sscanf(optarg, "%x", &num);
940                         if (rc == 1) opts.acpi_uid = num;
941                         break;
942                 case 'v':
943                         opts.verbose = 1;
944                         if (optarg) {
945                                 if (!strcmp(optarg, "v"))  opts.verbose = 2;
946                                 if (!strcmp(optarg, "vv")) opts.verbose = 3;
947                                 rc = sscanf(optarg, "%d", &num);
948                                 if (rc == 1)  opts.verbose = num;
949                         }
950                         break;
951                 case 'V':
952                         opts.showversion = 1;
953                         break;
954
955                 case 'w':
956                         opts.write_signature = 1;
957                         break;
958
959                 default:
960                         usage();
961                         exit(1);
962                 }
963         }
964
965         if (optind < argc) {
966                 opts.argc = argc;
967                 opts.argv = argv;
968                 opts.optind = optind;
969         }
970 }
971
972
973 int
974 main(int argc, char **argv)
975 {
976         struct dirent  **boot_names = NULL;
977         var_entry_t *new_boot = NULL;
978         int num, num_boot_names=0;
979
980         set_default_opts();
981         parse_opts(argc, argv);
982         if (opts.showversion) {
983                 printf("version %s\n", EFIBOOTMGR_VERSION);
984                 return 0;
985         }
986
987         if (opts.iface && opts.acpi_hid == -1 && opts.acpi_uid == -1) {
988                 fprintf(stderr, "\nYou must specify the ACPI HID and UID when using -i.\n\n");
989                 return 1;
990         }
991
992         if (!opts.testfile) {
993                 num_boot_names = read_boot_var_names(&boot_names);
994                 read_vars(boot_names, num_boot_names, &boot_entry_list);
995                 set_var_nums("Boot%04x-%*s", &boot_entry_list);
996
997                 if (opts.delete_boot) {
998                         if (opts.bootnum == -1)
999                                 fprintf(stderr, "\nYou must specify a boot entry to delete (see the -b option).\n\n");
1000                         else
1001                                 delete_boot_var(opts.bootnum);
1002                 }
1003
1004                 if (opts.active >= 0) {
1005                         set_active_state();
1006                 }
1007         }
1008
1009         if (opts.create) {
1010                 warn_duplicate_name(&boot_entry_list);
1011                 new_boot = make_boot_var(&boot_entry_list);
1012                 /* Put this boot var in the right BootOrder */
1013                 if (!opts.testfile && new_boot)
1014                         add_to_boot_order(new_boot->num);
1015         }
1016
1017         if (!opts.testfile) {
1018
1019                 if (opts.delete_bootorder) {
1020                         delete_boot_order();
1021                 }
1022
1023                 if (opts.bootorder) {
1024                         set_boot_order();
1025                 }
1026
1027
1028                 if (opts.delete_bootnext) {
1029                         delete_boot_next();
1030                 }
1031
1032                 if (opts.bootnext >= 0) {
1033                         set_boot_next(opts.bootnext & 0xFFFF);
1034                 }
1035
1036                 if (!opts.quiet) {
1037                         num = read_boot_next();
1038                         if (num != -1 ) {
1039                                 printf("BootNext: %04x\n", num);
1040                         }
1041                         num = read_boot_current();
1042                         if (num != -1) {
1043                                 printf("BootCurrent: %04x\n", num);
1044                         }
1045                         show_boot_order();
1046                         show_boot_vars();
1047                 }
1048         }
1049         free_dirents(boot_names, num_boot_names);
1050         return 0;
1051 }
1052