summaryrefslogtreecommitdiffstats
path: root/recipes-bsp/systemd-boot/systemd-boot/0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch
blob: e88012c30c747396c6ce670b9aba0a4c312e8e96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
From a3c4fc8c2847fe289a617bcba1d905f580f0e18d Mon Sep 17 00:00:00 2001
From: Jianxun Zhang <jianxun.zhang@linux.intel.com>
Date: Wed, 1 Jun 2016 16:32:22 -0700
Subject: [PATCH 2/3] sd-boot: Load board-specific boot entries from RMC
 database

RMC provides a centralized database file on ESP. The DB contains
fingerprints and any file blobs associated to physical boards.
Callers can fetch board-specific data with fingerprint info
collected from board at runtime if there is any record matched
board's fingerprint.

To let bootloader know which file blob in RMC should be queried,
a special config file BOOTENTRY.CONFIG is defined as:

boot.conf
install.conf

Bootloader calls RMC APIs and other functions to perform these
tasks before it shows boot menu to user:

(1) Load RMC database file from ESP
(2) Collect fingerprint data from board
(3) Query BOOTENTRY.CONFIG from RMC DB with fingerprint
(4) Parse BOOTENTRY.CONFIG to know names of boot entry files
(5) Query boot entry files one by one from RMC DB, and add
    them into sd-boot config data.

The final effect is that bootloader will show board-specific
boot entries in boot menu to user. User then can choose one
of them to boot system with the selected configuration.

If any of these steps fails, bootloader simply skips loading
RMC configs or any entry file not successfully fetched from
RMC DB. Once any entry is loaded successfully from RMC DB,
bootloader skips loading any boot entries from ESP.

Upstream-Status: Pending

Signed-off-by: Jianxun Zhang <jianxun.zhang@linux.intel.com>
---
 src/boot/efi/boot.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 2 deletions(-)

diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 30c1ead..d1b029b 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -15,6 +15,7 @@
 
 #include <efi.h>
 #include <efilib.h>
+#include <rmc_api.h>
 
 #include "console.h"
 #include "disk.h"
@@ -33,6 +34,9 @@ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot
 
 static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
 
+static CHAR8* rmc_db;
+static rmc_fingerprint_t *rmc_fp;
+
 enum loader_type {
         LOADER_UNDEFINED,
         LOADER_EFI,
@@ -1702,6 +1706,136 @@ static VOID config_free(Config *config) {
         FreePool(config->entry_oneshot);
 }
 
+/* Derived from line_get_key_value(), we could consolidate two functions later */
+static CHAR8 *get_line(CHAR8 *content, UINT64 *pos) {
+        CHAR8 *line;
+        UINT64 linelen;
+
+skip:
+        line = content + *pos;
+        if (*line == '\0')
+                return NULL;
+
+        linelen = 0;
+        while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
+               linelen++;
+
+        /* move pos to next line */
+        *pos += linelen;
+        if (content[*pos])
+                (*pos)++;
+
+        /* empty line */
+        if (linelen == 0)
+                goto skip;
+
+        /* terminate line */
+        line[linelen] = '\0';
+
+        /* remove leading whitespace */
+        while (strchra((CHAR8 *)" \t", *line)) {
+                line++;
+                linelen--;
+        }
+
+        /* remove trailing whitespace */
+        while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
+                linelen--;
+        line[linelen] = '\0';
+
+        if (*line == '#')
+                goto skip;
+
+        return line;
+}
+
+/* load rmc database file from ESP and try to get fingerprint. These
+ * are essential information indicating we could query rmc data for
+ * this board at least
+ * return 0 if both database file and fingerprint can be obtained, otherwise
+ * non-zero value is returned.
+ *
+ * Note: db and fp hold valid values only when this function returns 0.
+ * Caller is responsible to free allocated memory pointed by *db and *fp when
+ * this function returns 0.
+ */
+
+static UINTN rmc_initialize(EFI_FILE *root_dir, EFI_SYSTEM_TABLE *sys_table, CHAR8 **db, rmc_fingerprint_t **fp) {
+        UINTN len;
+        UINTN ret = 1;
+
+        if (!db || !fp)
+                return ret;
+
+        *db = NULL;
+        *fp = NULL;
+
+        /* load rmc database */
+        len = file_read(root_dir, L"\\rmc.db", 0, 0, db);
+
+        if (len <= 0)
+                goto done;
+
+        *fp = AllocateZeroPool(sizeof(rmc_fingerprint_t));
+        /* call rmc to get fingerprint. We will use single-action rmc APIs to query multiple files.
+         * This should bring a better performance than calling double-action rmc API every time.
+         */
+        if (rmc_get_fingerprint(sys_table, *fp))
+                goto done;
+
+        ret = 0;
+done:
+        if (ret) {
+                FreePool(*db);
+                FreePool(*fp);
+        }
+
+        return ret;
+}
+
+/* load RMC entries
+ * return TRUE when at least one entry is loaded, otherwise, return FALSE
+ */
+static BOOLEAN config_load_rmc_entries(Config *config, EFI_HANDLE *device, CHAR16 *loaded_image_path, CHAR8 *db, rmc_fingerprint_t *fp) {
+        CHAR8 *boot_entry = NULL;
+        CHAR8 *boot_config = NULL;
+        rmc_file_t rp;
+        CHAR8 *line;
+        UINT64 pos = 0;
+        BOOLEAN ret = FALSE;
+
+        if (!db || !fp)
+                return ret;
+
+        /* query boot entry config file */
+        if (rmc_query_file_by_fp(fp, db, "BOOTENTRY.CONFIG", &rp))
+                return ret;
+
+        /* file blob read from rmc db is not necessarily null-terminated, and we
+         * should keep mem where rmc db lives from change during parsing
+         */
+        boot_config = AllocatePool(rp.blob_len * sizeof(CHAR8) + 1);
+        CopyMem(boot_config, rp.blob, rp.blob_len);
+        boot_config[rp.blob_len] = '\0';
+        /* parse boot entry config */
+        while ((line = get_line(boot_config, &pos))) {
+                if (rmc_query_file_by_fp(fp, db, (char *)line, &rp))
+                        continue;
+                if (rp.blob_len > 0) {
+                        boot_entry = AllocatePool(rp.blob_len * sizeof(CHAR8) + 1);
+                        CopyMem(boot_entry, rp.blob, rp.blob_len);
+                        boot_entry[rp.blob_len] = '\0';
+                        config_entry_add_from_file(config, device,
+                                        stra_to_str(line), boot_entry,
+                                        loaded_image_path);
+                        /* tell caller success when a RMC entry is loaded */
+                        ret = TRUE;
+                }
+        }
+
+        return ret;
+}
+
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         CHAR16 *s;
         CHAR8 *b;
@@ -1714,6 +1848,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         UINT64 init_usec;
         BOOLEAN menu = FALSE;
         CHAR16 uuid[37];
+        BOOLEAN rmc_entry = FALSE;
 
         InitializeLib(image, sys_table);
         init_usec = time_usec();
@@ -1745,6 +1880,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 return EFI_LOAD_ERROR;
         }
 
+        /* Initialize rmc before loading any config */
+        rmc_initialize(root_dir, sys_table, &rmc_db, &rmc_fp);
 
         /* the filesystem path to this image, to prevent adding ourselves to the menu */
         loaded_image_path = DevicePathToStr(loaded_image->FilePath);
@@ -1753,11 +1890,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         ZeroMem(&config, sizeof(Config));
         config_load_defaults(&config, root_dir);
 
+        if (rmc_db && rmc_fp)
+                rmc_entry = config_load_rmc_entries(&config, loaded_image->DeviceHandle, loaded_image_path, rmc_db, rmc_fp);
+
         /* scan /EFI/Linux/ directory */
         config_entry_add_linux(&config, loaded_image, root_dir);
 
-        /* scan /loader/entries/\*.conf files */
-        config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+        /* scan /loader/entries/\*.conf files only when no RMC entry is loaded */
+        if (rmc_entry == FALSE)
+                config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
 
         /* sort entries after version number */
         config_sort_entries(&config);
@@ -1851,6 +1992,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 out:
         FreePool(loaded_image_path);
         config_free(&config);
+        FreePool(rmc_db);
+        FreePool(rmc_fp);
         uefi_call_wrapper(root_dir->Close, 1, root_dir);
         uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
         return err;
-- 
2.7.4