blob: c6ee913d3cdd9e58888d10908456054313d0788d [file] [log] [blame]
Sergii Dmytruk04bd9652023-11-17 19:31:20 +02001/* 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`.
Sergii Dmytrukb2d86f12024-03-10 18:51:45 +020014#define SMM_BLOCK_SIZE (64 * 1024)
Sergii Dmytruk04bd9652023-11-17 19:31:20 +020015
16static const EFI_GUID EfiVariableGuid = EFI_VARIABLE_GUID;
17
18static const EFI_GUID EfiAuthenticatedVariableGuid =
19 EFI_AUTHENTICATED_VARIABLE_GUID;
20
21static const EFI_GUID EfiSystemNvDataFvGuid = {
22 0xfff12b8d, 0x7696, 0x4c8b,
23 { 0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50 }
24};
25
26static 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
Sergii Dmytrukb2d86f12024-03-10 18:51:45 +020036bool fv_init(struct mem_range_t fv)
Sergii Dmytruk04bd9652023-11-17 19:31:20 +020037{
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
92static bool guid_eq(const EFI_GUID *lhs, const EFI_GUID *rhs)
93{
94 return memcmp(lhs, rhs, sizeof(*lhs)) == 0;
95}
96
97static 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
125static 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
Sergii Dmytrukb2d86f12024-03-10 18:51:45 +0200154bool fv_parse(struct mem_range_t fv, struct mem_range_t *var_store,
155 bool *auth_vars)
Sergii Dmytruk04bd9652023-11-17 19:31:20 +0200156{
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}