xref: /aosp_15_r20/external/coreboot/util/smmstoretool/fv.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "fv.h"
4 
5 #include <assert.h>
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include "udk2017.h"
12 
13 // The same as in `smmstore.h` header, which isn't in `commonlib`.
14 #define SMM_BLOCK_SIZE (64 * 1024)
15 
16 static const EFI_GUID EfiVariableGuid = EFI_VARIABLE_GUID;
17 
18 static const EFI_GUID EfiAuthenticatedVariableGuid =
19 	EFI_AUTHENTICATED_VARIABLE_GUID;
20 
21 static const EFI_GUID EfiSystemNvDataFvGuid = {
22 	0xfff12b8d, 0x7696, 0x4c8b,
23 	{ 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50 }
24 };
25 
calc_checksum(const uint16_t * hdr,size_t size)26 static uint16_t calc_checksum(const uint16_t *hdr, size_t size)
27 {
28 	assert(size % 2 == 0 && "Header can't have odd length.");
29 
30 	uint16_t checksum = 0;
31 	for (size_t i = 0; i < size / 2; ++i)
32 		checksum += hdr[i];
33 	return checksum;
34 }
35 
fv_init(struct mem_range_t fv)36 bool fv_init(struct mem_range_t fv)
37 {
38 	if (fv.length % SMM_BLOCK_SIZE != 0) {
39 		fprintf(stderr,
40 			"Firmware Volume size is not a multiple of 64KiB\n");
41 		return false;
42 	}
43 
44 	memset(fv.start, 0xff, fv.length);
45 
46 	const EFI_FIRMWARE_VOLUME_HEADER vol_hdr = {
47 		.FileSystemGuid = EfiSystemNvDataFvGuid,
48 		.FvLength = fv.length,
49 		.Signature = EFI_FVH_SIGNATURE,
50 		.Attributes = EFI_FVB2_READ_ENABLED_CAP
51 					| EFI_FVB2_READ_STATUS
52 					| EFI_FVB2_WRITE_ENABLED_CAP
53 					| EFI_FVB2_WRITE_STATUS
54 					| EFI_FVB2_STICKY_WRITE
55 					| EFI_FVB2_MEMORY_MAPPED
56 					| EFI_FVB2_ERASE_POLARITY,
57 		.HeaderLength = sizeof(vol_hdr)
58 			      + sizeof(EFI_FV_BLOCK_MAP_ENTRY),
59 		.Revision = EFI_FVH_REVISION,
60 		.BlockMap[0] = {
61 			.NumBlocks = fv.length / SMM_BLOCK_SIZE,
62 			.Length = SMM_BLOCK_SIZE,
63 		},
64 	};
65 
66 	EFI_FIRMWARE_VOLUME_HEADER *vol_hdr_dst = (void *)fv.start;
67 	*vol_hdr_dst = vol_hdr;
68 	vol_hdr_dst->BlockMap[1].NumBlocks = 0;
69 	vol_hdr_dst->BlockMap[1].Length = 0;
70 
71 	vol_hdr_dst->Checksum =
72 		~calc_checksum((const void *)vol_hdr_dst, vol_hdr.HeaderLength);
73 	++vol_hdr_dst->Checksum;
74 
75 	const VARIABLE_STORE_HEADER var_store_hdr = {
76 		// Authentication-related fields will be filled with 0xff.
77 		.Signature = EfiAuthenticatedVariableGuid,
78 		// Actual size of the storage is block size, the rest is
79 		// Fault Tolerant Write (FTW) space and the FTW spare space.
80 		.Size = SMM_BLOCK_SIZE - vol_hdr.HeaderLength,
81 		.Format = VARIABLE_STORE_FORMATTED,
82 		.State = VARIABLE_STORE_HEALTHY,
83 	};
84 
85 	VARIABLE_STORE_HEADER *var_store_hdr_dst =
86 		(void *)(fv.start + vol_hdr.HeaderLength);
87 	*var_store_hdr_dst = var_store_hdr;
88 
89 	return true;
90 }
91 
guid_eq(const EFI_GUID * lhs,const EFI_GUID * rhs)92 static bool guid_eq(const EFI_GUID *lhs, const EFI_GUID *rhs)
93 {
94 	return memcmp(lhs, rhs, sizeof(*lhs)) == 0;
95 }
96 
check_fw_vol_hdr(const EFI_FIRMWARE_VOLUME_HEADER * hdr,size_t max_size)97 static bool check_fw_vol_hdr(const EFI_FIRMWARE_VOLUME_HEADER *hdr,
98 			     size_t max_size)
99 {
100 	if (hdr->Revision != EFI_FVH_REVISION ||
101 		hdr->Signature != EFI_FVH_SIGNATURE ||
102 		hdr->FvLength > max_size ||
103 		hdr->HeaderLength > max_size ||
104 		hdr->HeaderLength % 2 != 0) {
105 		fprintf(stderr, "No firmware volume header present\n");
106 		return false;
107 	}
108 
109 	if (!guid_eq(&hdr->FileSystemGuid, &EfiSystemNvDataFvGuid)) {
110 		fprintf(stderr, "Firmware volume GUID non-compatible\n");
111 		return false;
112 	}
113 
114 	uint16_t checksum = calc_checksum((const void *)hdr, hdr->HeaderLength);
115 	if (checksum != 0) {
116 		fprintf(stderr,
117 			"Firmware Volume checksum is non-zero: 0x%04X\n",
118 			checksum);
119 		return false;
120 	}
121 
122 	return true;
123 }
124 
check_var_store_hdr(const VARIABLE_STORE_HEADER * hdr,size_t max_size,bool * auth_vars)125 static bool check_var_store_hdr(const VARIABLE_STORE_HEADER *hdr,
126 				size_t max_size,
127 				bool *auth_vars)
128 {
129 	*auth_vars = guid_eq(&hdr->Signature, &EfiAuthenticatedVariableGuid);
130 	if (!*auth_vars && !guid_eq(&hdr->Signature, &EfiVariableGuid)) {
131 		fprintf(stderr, "Variable store has unexpected GUID\n");
132 		return false;
133 	}
134 
135 	if (hdr->Size > max_size) {
136 		fprintf(stderr, "Variable store size is too large: %zu > %zu\n",
137 			(size_t)hdr->Size, max_size);
138 		return false;
139 	}
140 
141 	if (hdr->Format != VARIABLE_STORE_FORMATTED) {
142 		fprintf(stderr, "Variable store is not formatted\n");
143 		return false;
144 	}
145 
146 	if (hdr->State != VARIABLE_STORE_HEALTHY) {
147 		fprintf(stderr, "Variable store is not in a healthy state\n");
148 		return false;
149 	}
150 
151 	return true;
152 }
153 
fv_parse(struct mem_range_t fv,struct mem_range_t * var_store,bool * auth_vars)154 bool fv_parse(struct mem_range_t fv, struct mem_range_t *var_store,
155 	      bool *auth_vars)
156 {
157 	const EFI_FIRMWARE_VOLUME_HEADER *vol_hdr = (void *)fv.start;
158 	if (!check_fw_vol_hdr(vol_hdr, fv.length)) {
159 		fprintf(stderr, "No valid firmware volume was found\n");
160 		return false;
161 	}
162 
163 	uint8_t *fw_vol_data = fv.start + vol_hdr->HeaderLength;
164 	size_t volume_size = fv.length - vol_hdr->HeaderLength;
165 	const VARIABLE_STORE_HEADER *var_store_hdr = (void *)fw_vol_data;
166 	if (!check_var_store_hdr(var_store_hdr, volume_size, auth_vars)) {
167 		fprintf(stderr, "No valid variable store was found");
168 		return false;
169 	}
170 
171 	var_store->start = fw_vol_data + sizeof(*var_store_hdr);
172 	var_store->length = volume_size - sizeof(*var_store_hdr);
173 	return true;
174 }
175