Imported Upstream version 3.14
[debian/elilo] / tools / eliloalt.c
1 /*
2  * eliloalt.c
3  *
4  * Copyright (C) 2002-2003 Hewlett-Packard Co
5  * Contributed by Stephane Eranian <eranian@hpl.hp.com>
6  *
7  * This file is part of the ELILO, the EFI Linux boot loader.
8  *
9  *  ELILO is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2, or (at your option)
12  *  any later version.
13  *
14  *  ELILO is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with ELILO; see the file COPYING.  If not, write to the Free
21  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22  *  02111-1307, USA.
23  *
24  */
25
26 /*
27  * This program is used to set the EliloAlt EFI variable to influence
28  * how elilo will behave at the next reboot. This variable is used
29  * to boot a certain kernel/configuration only once (debug, for instance).
30  *
31  * This is is supposed to be installed in /usr/sbin/eliloalt and must only
32  * be run by root.
33  */
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <getopt.h>
41 #include <errno.h>
42 #include <stdint.h>
43 #include <string.h>
44 #include <dirent.h>
45
46
47 #define ELILOALT_VERSION        "0.02"
48
49 #define ELILO_ALT_NAME  "EliloAlt"
50 #define EFIVAR_DIR      "/sys/firmware/efi/vars"
51 #define OFIVAR_DIR      "/proc/efi/vars"
52 #define ELILO_ALTVAR    EFIVAR_DIR"/"ELILO_ALT_NAME"-00000000-0000-0000-0000-000000000000"
53 #define OLILO_ALTVAR    OFIVAR_DIR"/"ELILO_ALT_NAME"-00000000-0000-0000-0000-000000000000"
54
55 #define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
56 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
57 #define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
58
59 typedef unsigned long efi_status_t;
60 typedef uint8_t  efi_bool_t;
61 typedef uint16_t efi_char16_t;          /* UNICODE character */
62
63 /*
64  * EFI GUID type definition
65  */
66 typedef struct {
67         uint32_t data1;
68         uint16_t data2;
69         uint16_t data3;
70         uint8_t  data4[8];
71 } efi_guid_t;
72
73 /*
74  * EFI variable structure 
75  */
76 typedef struct _efi_variable_t {
77         efi_char16_t    variablename[1024/sizeof(efi_char16_t)];
78         efi_guid_t      vendorguid;
79         uint64_t        datasize;
80         uint8_t         data[1024];
81         efi_status_t    status;
82         uint32_t        attributes;
83 } __attribute__((packed)) efi_variable_t;
84
85 static char *efivar_dir = EFIVAR_DIR;
86 static char *elilo_alt_name = ELILO_ALT_NAME;
87 static char *elilo_altvar = ELILO_ALTVAR;
88
89 static struct option cmd_options[]={
90         { "version", 0, 0, 1},
91         { "help", 0, 0, 2},
92         { "delete", 0, 0, 3},
93         { "print", 0, 0, 4},
94         { "set", 1, 0, 5},
95         { 0, 0, 0, 0}
96 };
97
98 static void fatal_error(char *fmt,...) __attribute__((noreturn));
99
100 static void
101 fatal_error(char *fmt, ...) 
102 {
103         va_list ap;
104
105         va_start(ap, fmt);
106         vfprintf(stderr, fmt, ap);
107         va_end(ap);
108
109         exit(1);
110 }
111
112
113 static void
114 usage(char **argv)
115 {
116         printf("Usage: %s [OPTIONS] cmdline\n", argv[0]);
117
118         printf( "-h, --help\t\tdisplay this help and exit\n"
119                 "--version\t\toutput version information and exit\n"
120                 "-s, --set cmdline\tset elilo alternate variable to cmdline\n"
121                 "-p, --print\t\tprint elilo alternate variable\n"
122                 "-d, --delete\t\tprint elilo alternate variable\n"
123         );
124 }
125
126 static char *
127 check_proc_efi(int find_entry)
128 {
129         DIR *efi_vars;
130         struct dirent *entry;
131         static char name[1024];
132
133         if (getuid() != 0) {
134                 fatal_error("This program must be run as root\n");
135         }
136         efi_vars = opendir(efivar_dir);
137         if (efi_vars == NULL) {
138                 efivar_dir = OFIVAR_DIR;
139                 elilo_altvar = OLILO_ALTVAR;
140                 efi_vars = opendir(efivar_dir);
141         }
142         if (efi_vars == NULL) {
143                 fatal_error("Can access neither %s nor %s\n",
144                             EFIVAR_DIR, efivar_dir);
145         }
146         if (!find_entry) {
147                 closedir(efi_vars);
148                 return NULL;
149         }
150         /* Find one entry we can open */
151         while ((entry = readdir(efi_vars)) != NULL) {
152                 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
153                         break;
154         }
155         if (entry == NULL) {
156                 fatal_error("Cannot find entry in %s\n", efivar_dir);
157         }
158         snprintf(name, 1023, "%s/%s", efivar_dir, entry->d_name);
159         name[1023] = 0;
160         closedir(efi_vars);
161         return name;
162 }
163
164 static void
165 delete_var(void)
166 {
167         efi_variable_t var;
168         int fd, r, i;
169
170         check_proc_efi(0);
171
172         fd = open(elilo_altvar, O_WRONLY);
173         if (fd == -1) {
174                 fatal_error("variable not defined\n");
175         }
176
177         memset(&var, 0, sizeof(var));
178
179         for (i=0; i < sizeof(elilo_alt_name); i++) {
180                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
181         }
182
183         /* 
184          * we use NULL GUID so no need to initialize it now memset() did it
185          * writing with a datasize=0 will effectively delete the variable.
186          */
187         
188         r = write(fd, &var, sizeof(var));
189         if (r != sizeof(var)) {
190                 fatal_error("Variable %s defined but invalid content\n", elilo_altvar);
191         }
192         close(fd);
193 }
194
195
196 static void
197 print_var(void)
198 {
199         efi_variable_t var;
200         int fd, r, i;
201
202
203         check_proc_efi(0);
204
205         fd = open(elilo_altvar, O_RDONLY);
206         if (fd == -1) {
207                 fatal_error("variable not defined\n");
208         }
209
210         memset(&var, 0, sizeof(var));
211
212         r = read(fd, &var, sizeof(var));
213         if (r != sizeof(var)) {
214                 fatal_error("Variable %s defined but invalid content\n", elilo_altvar);
215         }
216         printf("EliloAlt=\"");
217         for(i=0; i < var.datasize; i+=1){
218                 printf("%c", var.data[i]);
219         }
220         printf("\"\n");
221         close(fd);
222 }
223
224 static void
225 set_var(char *cmdline)
226 {
227         efi_variable_t var;
228         int fd, r, i, j, l;
229         char *name;
230
231         name = check_proc_efi(1);
232
233         if (cmdline == NULL) {
234                 fatal_error("invalid cmdline argument\n");
235         }
236
237         l = strlen(cmdline);
238
239         if (l >= 1024) {
240                 fatal_error("Variable content is too long, must be <= 512 characters\n");
241         }
242
243         fd = open(name, O_WRONLY);
244         if (fd == -1) {
245                 fatal_error("can't open %s: %s\n", elilo_altvar, strerror(errno));
246         }
247
248         memset(&var, 0, sizeof(var));
249
250         for (i=0; i < sizeof(elilo_alt_name); i++) {
251                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
252         }
253
254         for (i=0, j=0; i < l; i++, j+=2) {
255                 var.data[j] = (efi_char16_t)cmdline[i];
256         }
257         /* +2 = include char16 for null termination */
258         var.datasize   = j+2;
259
260         var.attributes =  EFI_VARIABLE_NON_VOLATILE 
261                         | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
262
263         /* 
264          * we use NULL GUID so no need to initialize it now memset() did it
265          * writing with a datasize=0 will effectively delete the variable.
266          */
267         
268         r = write(fd, &var, sizeof(var));
269         if (r != sizeof(var)) {
270                 fatal_error("Variable %s defined but invalid content %d\n", elilo_altvar, r);
271         }
272         close(fd);
273
274 }
275
276 int
277 main(int argc, char **argv)
278 {
279         int c;
280
281         while ((c=getopt_long(argc, argv,"hdps:", cmd_options, 0)) != -1) {
282                 switch(c) {
283                         case   0: continue; /* fast path for options */
284                         case   1:
285                                 printf("Version %s Date: %s\n", ELILOALT_VERSION, __DATE__);
286                                 exit(0);
287                         case   2:
288                         case 'h':
289                                 usage(argv);
290                                 exit(0);
291                         case   3:
292                         case 'd':
293                                 delete_var();
294                                 exit(0);
295                         case   4:
296                         case 'p':
297                                 print_var();
298                                 exit(0);
299                         case   5:
300                         case 's':
301                                 set_var(optarg);
302                                 exit(0);
303                         default:
304                                 fatal_error("Unknown option\n");
305                 }
306         }
307         print_var();
308         return 0;
309 }