]> git.gag.com Git - debian/elilo/blob - tools/eliloalt.c
Imported Debian patch 3.7-1
[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 <sys/stat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <getopt.h>
42 #include <errno.h>
43 #include <stdint.h>
44 #include <string.h>
45 #include <dirent.h>
46
47
48 #define ELILOALT_VERSION        "0.02"
49
50 #define ELILO_ALT_NAME  "EliloAlt"
51 #define OLDEFIVAR_DIR   "/proc/efi/vars"
52 #define NEWEFIVAR_DIR   "/sys/firmware/efi/vars"
53 #define ELILO_ALTVAR    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 *elilo_alt_name = ELILO_ALT_NAME;
86 static char *efi_vars_dir = NULL;
87 static char *elilo_altvar = NULL;
88 static char *elilo_newvar = NULL;
89 static char *elilo_delvar = NULL;
90 unsigned char efivars_version = -1; 
91
92 static struct option cmd_options[]={
93         { "version", 0, 0, 1},
94         { "help", 0, 0, 2},
95         { "delete", 0, 0, 3},
96         { "print", 0, 0, 4},
97         { "set", 1, 0, 5},
98         { 0, 0, 0, 0}
99 };
100
101 static void fatal_error(char *fmt,...) __attribute__((noreturn));
102
103 static void
104 fatal_error(char *fmt, ...) 
105 {
106         va_list ap;
107
108         va_start(ap, fmt);
109         vfprintf(stderr, fmt, ap);
110         va_end(ap);
111
112         exit(1);
113 }
114
115
116 static void
117 usage(char **argv)
118 {
119         printf("Usage: %s [OPTIONS] cmdline\n", argv[0]);
120
121         printf( "-h, --help\t\tdisplay this help and exit\n"
122                 "--version\t\toutput version information and exit\n"
123                 "-s, --set cmdline\tset elilo alternate variable to cmdline\n"
124                 "-p, --print\t\tprint elilo alternate variable\n"
125                 "-d, --delete\t\tprint elilo alternate variable\n"
126         );
127 }
128
129 static char *
130 check_proc_efi(int find_entry)
131 {
132         DIR *efi_vars;
133         struct dirent *entry;
134         static char name[1024];
135
136         if (getuid() != 0) {
137                 fatal_error("This program must be run as root\n");
138         }
139         efi_vars = opendir(efi_vars_dir);
140         if (efi_vars == NULL) {
141                 fatal_error("Cannot access %s\n", efi_vars_dir);
142         }
143         if (!find_entry) {
144                 closedir(efi_vars);
145                 return NULL;
146         }
147         /* Find one entry we can open */
148         while ((entry = readdir(efi_vars)) != NULL) {
149                 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
150                         break;
151         }
152         if (entry == NULL) {
153                 fatal_error("Cannot find entry in %s\n", efi_vars_dir);
154         }
155         sprintf(name, "%s/%s", efi_vars_dir, entry->d_name);
156         closedir(efi_vars);
157         return name;
158 }
159
160 static void
161 delete_var(void)
162 {
163         efi_variable_t var;
164         int fd, r, i;
165
166         check_proc_efi(0);
167         
168         if (efivars_version == 1) {
169                 fd = open(elilo_altvar, O_WRONLY);
170                 if (fd == -1) {
171                         fatal_error("variable not defined\n");
172                 }
173         } else {
174                 fd = open(elilo_delvar, O_WRONLY);
175                 if (fd == -1) {
176                         fatal_error("can't open %s\n", elilo_delvar);
177                 }
178         }
179
180         memset(&var, 0, sizeof(var));
181
182         for (i=0; i < sizeof(elilo_alt_name); i++) {
183                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
184         }
185
186         /* 
187          * we use NULL GUID so no need to initialize it now memset() did it
188          * writing with a datasize=0 will effectively delete the variable.
189          */
190         
191         r = write(fd, &var, sizeof(var));
192         if (r != sizeof(var)) {
193                 fatal_error("Variable %s defined but invalid content\n", elilo_altvar);
194         }
195         close(fd);
196 }
197
198
199 static void
200 print_var(void)
201 {
202         efi_variable_t var;
203         int fd, r, i;
204
205
206         check_proc_efi(0);
207
208         fd = open(elilo_altvar, O_RDONLY);
209         if (fd == -1) {
210                 fatal_error("variable not defined\n");
211         }
212
213         memset(&var, 0, sizeof(var));
214
215         r = read(fd, &var, sizeof(var));
216         if (r != sizeof(var)) {
217                 fatal_error("Variable %s defined but invalid content\n", elilo_altvar);
218         }
219         printf("EliloAlt=\"");
220         for(i=0; i < var.datasize; i+=1){
221                 printf("%c", var.data[i]);
222         }
223         printf("\"\n");
224         close(fd);
225 }
226
227 static void
228 set_var(char *cmdline)
229 {
230         efi_variable_t var;
231         int fd, r, i, j, l;
232         char *name;
233
234         name = check_proc_efi(1);
235
236         if (cmdline == NULL) {
237                 fatal_error("invalid cmdline argument\n");
238         }
239
240         l = strlen(cmdline);
241
242         if (l >= 1024) {
243                 fatal_error("Variable content is too long, must be <= 512 characters\n");
244         }
245
246         if (efivars_version == 1) {
247                 fd = open(name, O_WRONLY);
248                 if (fd == -1) {
249                         fatal_error("can't open %s: %s\n", name,
250                                     strerror(errno));
251                 }
252         } else {
253                 fd = open(elilo_newvar, O_WRONLY);
254                 if (fd == -1) {
255                         fatal_error("can't open %s: %s\n", elilo_newvar,
256                                     strerror(errno));
257                 }
258         }
259         memset(&var, 0, sizeof(var));
260
261         for (i=0; i < sizeof(elilo_alt_name); i++) {
262                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
263         }
264
265         for (i=0, j=0; i < l; i++, j+=2) {
266                 var.data[j] = (efi_char16_t)cmdline[i];
267         }
268         /* +2 = include char16 for null termination */
269         var.datasize   = j+2;
270
271         var.attributes =  EFI_VARIABLE_NON_VOLATILE 
272                         | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
273
274         /* 
275          * we use NULL GUID so no need to initialize it now memset() did it
276          * writing with a datasize=0 will effectively delete the variable.
277          */
278         
279         r = write(fd, &var, sizeof(var));
280         if (r != sizeof(var)) {
281                 fatal_error("Variable %s defined but invalid content %d\n", elilo_altvar, r);
282         }
283         close(fd);
284
285 }
286
287 int
288 main(int argc, char **argv)
289 {
290         int c;
291         struct stat statbuf;
292
293         if (stat(OLDEFIVAR_DIR, &statbuf) == 0) {
294                 efi_vars_dir = strdup(OLDEFIVAR_DIR);
295                 efivars_version = 1;
296         } else if (stat(NEWEFIVAR_DIR, &statbuf) == 0) {
297                 efi_vars_dir = strdup(NEWEFIVAR_DIR);
298                 elilo_newvar = malloc(strlen(efi_vars_dir) +
299                                       strlen("new_var") + 2);
300                 sprintf(elilo_newvar, "%s/%s", efi_vars_dir, "new_var");
301                 elilo_delvar = malloc(strlen(efi_vars_dir) +
302                                       strlen("del_var") + 2);
303                 sprintf(elilo_delvar, "%s/%s", efi_vars_dir, "del_var");
304                 efivars_version = 2;
305         } else {
306                 fatal_error("No EFI vars dir found\n\ttried:\n\t%s\n\t%s",
307                             OLDEFIVAR_DIR, NEWEFIVAR_DIR);
308         }
309         elilo_altvar = malloc(strlen(efi_vars_dir)+strlen(ELILO_ALTVAR)+2);
310         sprintf(elilo_altvar, "%s/%s", efi_vars_dir, ELILO_ALTVAR);
311
312         while ((c=getopt_long(argc, argv,"hdps:", cmd_options, 0)) != -1) {
313                 switch(c) {
314                         case   0: continue; /* fast path for options */
315                         case   1:
316                                 printf("Version %s Date: %s\n", ELILOALT_VERSION, __DATE__);
317                                 exit(0);
318                         case   2:
319                         case 'h':
320                                 usage(argv);
321                                 exit(0);
322                         case   3:
323                         case 'd':
324                                 delete_var();
325                                 exit(0);
326                         case   4:
327                         case 'p':
328                                 print_var();
329                                 exit(0);
330                         case   5:
331                         case 's':
332                                 set_var(optarg);
333                                 exit(0);
334                         default:
335                                 fatal_error("Unknown option\n");
336                 }
337         }
338         print_var();
339         return 0;
340 }