chiark / gitweb /
sd-boot: add EFI boot manager and stub loader
[elogind.git] / src / sd-boot / util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
15  * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
16  */
17
18 #include <efi.h>
19 #include <efilib.h>
20
21 #include "util.h"
22
23 /*
24  * Allocated random UUID, intended to be shared across tools that implement
25  * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
26  * associated EFI variables.
27  */
28 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
29
30 #ifdef __x86_64__
31 UINT64 ticks_read(VOID) {
32         UINT64 a, d;
33         __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
34         return (d << 32) | a;
35 }
36 #else
37 UINT64 ticks_read(VOID) {
38         UINT64 val;
39         __asm__ volatile ("rdtsc" : "=A" (val));
40         return val;
41 }
42 #endif
43
44 /* count TSC ticks during a millisecond delay */
45 UINT64 ticks_freq(VOID) {
46         UINT64 ticks_start, ticks_end;
47
48         ticks_start = ticks_read();
49         uefi_call_wrapper(BS->Stall, 1, 1000);
50         ticks_end = ticks_read();
51
52         return (ticks_end - ticks_start) * 1000;
53 }
54
55 UINT64 time_usec(VOID) {
56         UINT64 ticks;
57         static UINT64 freq;
58
59         ticks = ticks_read();
60         if (ticks == 0)
61                 return 0;
62
63         if (freq == 0) {
64                 freq = ticks_freq();
65                 if (freq == 0)
66                         return 0;
67         }
68
69         return 1000 * 1000 * ticks / freq;
70 }
71
72 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
73         UINT32 flags;
74
75         flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
76         if (persistent)
77                 flags |= EFI_VARIABLE_NON_VOLATILE;
78
79         return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
80 }
81
82 EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
83         return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
84 }
85
86 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
87         CHAR16 str[32];
88
89         SPrint(str, 32, L"%d", i);
90         return efivar_set(name, str, persistent);
91 }
92
93 EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
94         CHAR8 *buf;
95         CHAR16 *val;
96         UINTN size;
97         EFI_STATUS err;
98
99         err = efivar_get_raw(&loader_guid, name, &buf, &size);
100         if (EFI_ERROR(err))
101                 return err;
102
103         val = StrDuplicate((CHAR16 *)buf);
104         if (!val) {
105                 FreePool(buf);
106                 return EFI_OUT_OF_RESOURCES;
107         }
108
109         *value = val;
110         return EFI_SUCCESS;
111 }
112
113 EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
114         CHAR16 *val;
115         EFI_STATUS err;
116
117         err = efivar_get(name, &val);
118         if (!EFI_ERROR(err)) {
119                 *i = Atoi(val);
120                 FreePool(val);
121         }
122         return err;
123 }
124
125 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
126         CHAR8 *buf;
127         UINTN l;
128         EFI_STATUS err;
129
130         l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
131         buf = AllocatePool(l);
132         if (!buf)
133                 return EFI_OUT_OF_RESOURCES;
134
135         err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
136         if (!EFI_ERROR(err)) {
137                 *buffer = buf;
138                 if (size)
139                         *size = l;
140         } else
141                 FreePool(buf);
142         return err;
143
144 }
145
146 VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
147         CHAR16 str[32];
148
149         if (usec == 0)
150                 usec = time_usec();
151         if (usec == 0)
152                 return;
153
154         SPrint(str, 32, L"%ld", usec);
155         efivar_set(name, str, FALSE);
156 }
157
158 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
159         CHAR16 unichar;
160         UINTN len;
161         UINTN i;
162
163         if (stra[0] < 0x80)
164                 len = 1;
165         else if ((stra[0] & 0xe0) == 0xc0)
166                 len = 2;
167         else if ((stra[0] & 0xf0) == 0xe0)
168                 len = 3;
169         else if ((stra[0] & 0xf8) == 0xf0)
170                 len = 4;
171         else if ((stra[0] & 0xfc) == 0xf8)
172                 len = 5;
173         else if ((stra[0] & 0xfe) == 0xfc)
174                 len = 6;
175         else
176                 return -1;
177
178         switch (len) {
179         case 1:
180                 unichar = stra[0];
181                 break;
182         case 2:
183                 unichar = stra[0] & 0x1f;
184                 break;
185         case 3:
186                 unichar = stra[0] & 0x0f;
187                 break;
188         case 4:
189                 unichar = stra[0] & 0x07;
190                 break;
191         case 5:
192                 unichar = stra[0] & 0x03;
193                 break;
194         case 6:
195                 unichar = stra[0] & 0x01;
196                 break;
197         }
198
199         for (i = 1; i < len; i++) {
200                 if ((stra[i] & 0xc0) != 0x80)
201                         return -1;
202                 unichar <<= 6;
203                 unichar |= stra[i] & 0x3f;
204         }
205
206         *c = unichar;
207         return len;
208 }
209
210 CHAR16 *stra_to_str(CHAR8 *stra) {
211         UINTN strlen;
212         UINTN len;
213         UINTN i;
214         CHAR16 *str;
215
216         len = strlena(stra);
217         str = AllocatePool((len + 1) * sizeof(CHAR16));
218
219         strlen = 0;
220         i = 0;
221         while (i < len) {
222                 INTN utf8len;
223
224                 utf8len = utf8_to_16(stra + i, str + strlen);
225                 if (utf8len <= 0) {
226                         /* invalid utf8 sequence, skip the garbage */
227                         i++;
228                         continue;
229                 }
230
231                 strlen++;
232                 i += utf8len;
233         }
234         str[strlen] = '\0';
235         return str;
236 }
237
238 CHAR16 *stra_to_path(CHAR8 *stra) {
239         CHAR16 *str;
240         UINTN strlen;
241         UINTN len;
242         UINTN i;
243
244         len = strlena(stra);
245         str = AllocatePool((len + 2) * sizeof(CHAR16));
246
247         str[0] = '\\';
248         strlen = 1;
249         i = 0;
250         while (i < len) {
251                 INTN utf8len;
252
253                 utf8len = utf8_to_16(stra + i, str + strlen);
254                 if (utf8len <= 0) {
255                         /* invalid utf8 sequence, skip the garbage */
256                         i++;
257                         continue;
258                 }
259
260                 if (str[strlen] == '/')
261                         str[strlen] = '\\';
262                 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
263                         /* skip double slashes */
264                         i += utf8len;
265                         continue;
266                 }
267
268                 strlen++;
269                 i += utf8len;
270         }
271         str[strlen] = '\0';
272         return str;
273 }
274
275 CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
276         do {
277                 if (*s == c)
278                         return s;
279         } while (*s++);
280         return NULL;
281 }
282
283 INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) {
284         EFI_FILE_HANDLE handle;
285         CHAR8 *buf;
286         UINTN buflen;
287         EFI_STATUS err;
288         UINTN len;
289
290         err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
291         if (EFI_ERROR(err))
292                 return err;
293
294         if (size == 0) {
295                 EFI_FILE_INFO *info;
296
297                 info = LibFileInfo(handle);
298                 buflen = info->FileSize+1;
299                 FreePool(info);
300         } else
301                 buflen = size;
302
303         if (off > 0) {
304                 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
305                 if (EFI_ERROR(err))
306                         return err;
307         }
308
309         buf = AllocatePool(buflen);
310         err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
311         if (!EFI_ERROR(err)) {
312                 buf[buflen] = '\0';
313                 *content = buf;
314                 len = buflen;
315         } else {
316                 len = err;
317                 FreePool(buf);
318         }
319
320         uefi_call_wrapper(handle->Close, 1, handle);
321         return len;
322 }