1 /* Copyright 2023 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Utility functions for Intel Flash Descriptor (ifd) and the 'Converged
6 * Security and Manageability Engine' (CSME).
7 */
8
9 #include <string.h>
10
11 #include "cbfstool.h"
12 #include "platform_csme.h"
13 #include "updater.h"
14
15 /* Structure from coreboot util/ifdtool/ifdtool.h */
16 // flash descriptor
17 struct fdbar {
18 uint32_t flvalsig;
19 uint32_t flmap0;
20 uint32_t flmap1;
21 uint32_t flmap2;
22 uint32_t flmap3; // Exist for 500 series onwards
23 } __attribute__((packed));
24
25 // flash master
26 struct fmba {
27 uint32_t flmstr1;
28 uint32_t flmstr2;
29 uint32_t flmstr3;
30 uint32_t flmstr4;
31 uint32_t flmstr5;
32 uint32_t flmstr6;
33 } __attribute__((packed));
34
find_fmba(const struct firmware_image * image)35 static struct fmba * const find_fmba(const struct firmware_image *image) {
36 struct firmware_section section;
37 const uint32_t signature = 0x0FF0A55A;
38 const struct fdbar *fd;
39
40 if (!image->size)
41 return NULL;
42 if (find_firmware_section(§ion, image, FMAP_SI_DESC))
43 return NULL;
44
45 if (section.size < sizeof(*fd) + sizeof(struct fmba))
46 return NULL;
47 fd = memmem(section.data, section.size - sizeof(*fd),
48 (const void *)&signature, sizeof(signature));
49 if (!fd)
50 return NULL;
51
52 const uint64_t offset = (fd->flmap1 & 0xff) << 4;
53 if (offset + sizeof(struct fmba) > section.size)
54 return NULL;
55
56 return (struct fmba * const)(section.data + offset);
57 }
58
is_flmstr1_locked(const struct fmba * const fmba)59 static bool is_flmstr1_locked(const struct fmba * const fmba)
60 {
61 /*
62 * (from idftool.c) There are multiple versions of IFD but there are no
63 * version tags in the descriptor. Starting from Apollolake all
64 * Chromebooks should be using IFD v2 so we'll check only the v2 values.
65 * V2: unlocked FLMSTR is 0xfffffff?? (31:20=write, 19:8=read)
66 */
67 const bool is_locked = (fmba->flmstr1 & 0xfff00000) != 0xfff00000;
68 VB2_DEBUG("FLMSTR1 = %#08x (%s)\n", fmba->flmstr1, is_locked ? "LOCKED" : "unlocked");
69
70 return is_locked;
71 }
72
is_flash_descriptor_locked(const struct firmware_image * image)73 bool is_flash_descriptor_locked(const struct firmware_image *image)
74 {
75 /*
76 * TODO(roccochen) When the flashrom supports exporting FRAP,
77 * we can replace the parsing of FLMSTRs to rely on FRAP for deciding if
78 * AP RO is locked or not.
79 */
80 const struct fmba *fmba = find_fmba(image);
81 if (!fmba) {
82 WARN("Failed to find flash master. Assuming unlocked.\n");
83 return false;
84 }
85 return is_flmstr1_locked(fmba);
86 }
87
88 /*
89 * Unlock the flash descriptor by rewriting the FLMSTR1.
90 *
91 * Returns 0 on success, any other values for failure.
92 */
unlock_flmstrs(struct firmware_image * image,uint32_t flmstr1,uint32_t flmstr2,uint32_t flmstr3)93 static int unlock_flmstrs(struct firmware_image *image,
94 uint32_t flmstr1, uint32_t flmstr2, uint32_t flmstr3)
95 {
96 struct fmba * const fmba = find_fmba(image);
97
98 if (!fmba) {
99 ERROR("Failed to unlock the Flash Master values.\n");
100 return -1;
101 }
102
103 if (fmba->flmstr1 == flmstr1 &&
104 fmba->flmstr2 == flmstr2 &&
105 fmba->flmstr3 == flmstr3) {
106 VB2_DEBUG("No need to change the Flash Master values.\n");
107 return 0;
108 }
109 VB2_DEBUG("Change flmstr1=%#08x->%#08x\n", fmba->flmstr1, flmstr1);
110 VB2_DEBUG("Change flmstr2=%#08x->%#08x\n", fmba->flmstr2, flmstr2);
111 VB2_DEBUG("Change flmstr3=%#08x->%#08x\n", fmba->flmstr3, flmstr3);
112
113 fmba->flmstr1 = flmstr1;
114 fmba->flmstr2 = flmstr2;
115 fmba->flmstr3 = flmstr3;
116 INFO("Changed Flash Master values to unlocked.\n");
117 return 0;
118 }
119
120 /*
121 * Unlock the flash descriptor for Skylake and Kabylake platforms.
122 *
123 * The FLMSTR settings are dedicated for the Skylake (glados) and Kabylake (eve)
124 * platforms, and are slightly different to those in the common
125 * unlock_flash_master() function. The common settings might work, but we keep
126 * these as is for now to avoid breaking things on old devices. These settings
127 * are also hardcoded in postinst scripts (e.g. https://crrev.com/i/252522), so
128 * those would probably need to be changed too.
129 */
unlock_csme_eve(struct firmware_image * image)130 int unlock_csme_eve(struct firmware_image *image)
131 {
132 return unlock_flmstrs(image, 0xffffff00, 0xffffff00, 0xffffff00);
133 }
134
135 /*
136 * Determine the platform to pass to ifdtool (e.g. 'adl') by extracting
137 * CONFIG_IFD_CHIPSET from the config file in CBFS. However, old nissa firmware
138 * may not have all config fields in the CBFS file, so fall back to a hack of
139 * checking for 'nissa' in the descriptor file path.
140 *
141 * On success, returns the platform, which must be freed by the caller.
142 * On failure, returns NULL.
143 */
determine_ifd_platform(const char * image_path)144 static char *determine_ifd_platform(const char *image_path)
145 {
146 char *platform;
147 char *ifd_path;
148
149 cbfstool_get_config_string(image_path, NULL, "CONFIG_IFD_CHIPSET", &platform);
150 if (platform)
151 return platform;
152
153 /* Fall back to checking for nissa in the descriptor file path */
154 cbfstool_get_config_string(image_path, NULL, "CONFIG_IFD_BIN_PATH", &ifd_path);
155 if (ifd_path && strstr(ifd_path, "/nissa/")) {
156 VB2_DEBUG("Use platform 'adl' since descriptor path contains 'nissa'\n");
157 ASPRINTF(&platform, "adl");
158 }
159
160 if (ifd_path)
161 free(ifd_path);
162
163 return platform;
164 }
165
166 /*
167 * Run ifdtool with the given options.
168 *
169 * Returns 0 on success, otherwise failure.
170 */
run_ifdtool(const char * image_path,char * platform,const char * extra_options)171 static int run_ifdtool(const char *image_path, char *platform, const char *extra_options)
172 {
173 char *command;
174 int ret = 0;
175
176 ASPRINTF(&command, "ifdtool -p \"%s\" -O \"%s\" \"%s\" %s 2>&1",
177 platform, image_path, image_path, extra_options);
178 if (system(command)) {
179 ERROR("Failed to run: %s\n", command);
180 ret = -1;
181 }
182
183 free(command);
184 return ret;
185 }
186
187 /*
188 * Unlock the CSME for recent Intel platforms (CML onwards).
189 *
190 * This allows the SI_DESC and SI_ME regions to be updated.
191 *
192 * Returns 0 on success, otherwise failure.
193 */
unlock_csme(struct updater_config * cfg)194 int unlock_csme(struct updater_config *cfg)
195 {
196 const char *temp_path;
197 char *platform;
198 int ret = -1;
199
200 temp_path = get_firmware_image_temp_file(&cfg->image, &cfg->tempfiles);
201 if (!temp_path) {
202 ERROR("Failed to get image temp file\n");
203 return ret;
204 }
205
206 platform = determine_ifd_platform(temp_path);
207 if (!platform) {
208 ERROR("Failed to determine IFD platform\n");
209 return ret;
210 }
211
212 VB2_DEBUG("Using platform '%s'\n", platform);
213
214 /* Unlock FMLSTRs */
215 if (run_ifdtool(temp_path, platform, "-u")) {
216 ERROR("Failed to unlock FLMSTRs\n");
217 goto cleanup;
218 }
219
220 /*
221 * Disable GPR0 (Global Protected Range). When enabled, it provides
222 * write-protection to part of the SI_ME region, specifically CSE_RO and
223 * part of CSE_DATA, so it must be disabled to allow updating SI_ME.
224 */
225 if (run_ifdtool(temp_path, platform, "-g")) {
226 ERROR("Failed to disable GPR0\n");
227 goto cleanup;
228 }
229
230 if (reload_firmware_image(temp_path, &cfg->image)) {
231 ERROR("Failed to reload firmware image\n");
232 goto cleanup;
233 }
234
235 /* Double check the descriptor was actually unlocked */
236 if (is_flash_descriptor_locked(&cfg->image)) {
237 ERROR("Descriptor is still locked after running ifdtool\n");
238 goto cleanup;
239 }
240
241 INFO("Unlocked Intel ME on platform '%s'\n", platform);
242 ret = 0;
243
244 cleanup:
245 free(platform);
246
247 return ret;
248 }
249