Furquan Shaikh | d7fb6a9 | 2021-09-16 22:04:11 -0700 | [diff] [blame] | 1 | /* BPDT version 1.6 support */ |
| 2 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 3 | |
| 4 | #include <sys/types.h> |
| 5 | |
| 6 | #include "cse_serger.h" |
| 7 | |
| 8 | struct bpdt_header { |
| 9 | uint32_t signature; /* BPDT_SIGNATURE */ |
| 10 | uint16_t descriptor_count; |
| 11 | uint16_t version; /* Layout 1.6 = 1 */ |
| 12 | uint16_t reserved; |
| 13 | uint8_t whole_checksum; |
| 14 | uint8_t rom_checksum; |
| 15 | uint32_t ifwi_version; |
| 16 | struct { |
| 17 | uint16_t major; |
| 18 | uint16_t minor; |
| 19 | uint16_t build; |
| 20 | uint16_t hotfix; |
| 21 | } fit_tool_version; |
| 22 | } __packed; |
| 23 | |
| 24 | struct cse_layout { |
| 25 | uint8_t rom_bypass[16]; |
| 26 | uint32_t data_offset; |
| 27 | uint32_t data_size; |
| 28 | uint32_t bp1_offset; |
| 29 | uint32_t bp1_size; |
| 30 | uint32_t bp2_offset; |
| 31 | uint32_t bp2_size; |
| 32 | uint32_t bp3_offset; |
| 33 | uint32_t bp3_size; |
| 34 | uint32_t reserved[16]; |
| 35 | uint8_t checksum; |
| 36 | } __packed; |
| 37 | |
| 38 | static bool match_version(const struct buffer *buff) |
| 39 | { |
| 40 | const uint8_t *data = buffer_get(buff); |
| 41 | const uint32_t sig = read_le32(data); |
| 42 | const uint16_t version = read_at_le16(data, offsetof(struct bpdt_header, version)); |
| 43 | |
| 44 | if (sig != BPDT_SIGNATURE) { |
| 45 | ERROR("Invalid BPDT signature(0x%x)!\n", sig); |
| 46 | return false; |
| 47 | } |
| 48 | |
| 49 | return version == BPDT_VERSION_1_6; |
| 50 | } |
| 51 | |
| 52 | static bpdt_hdr_ptr create_bpdt_hdr(void) |
| 53 | { |
| 54 | struct bpdt_header *h = malloc(sizeof(*h)); |
| 55 | |
| 56 | if (!h) |
| 57 | return NULL; |
| 58 | |
| 59 | h->signature = BPDT_SIGNATURE; |
| 60 | h->descriptor_count = 0; |
| 61 | h->version = BPDT_VERSION_1_6; |
| 62 | h->reserved = 0; |
| 63 | /* TODO(b/202549343): Need to calculate checksum */ |
| 64 | h->whole_checksum = 0; |
| 65 | h->rom_checksum = 0; |
| 66 | h->ifwi_version = 0; |
| 67 | h->fit_tool_version.major = 0; |
| 68 | h->fit_tool_version.minor = 0; |
| 69 | h->fit_tool_version.build = 0; |
| 70 | h->fit_tool_version.hotfix = 0; |
| 71 | |
Solomon Alan-Dei | a2503fa | 2022-10-29 08:30:23 -0600 | [diff] [blame] | 72 | return h; |
Furquan Shaikh | d7fb6a9 | 2021-09-16 22:04:11 -0700 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | static void print_bpdt_hdr(const bpdt_hdr_ptr ptr) |
| 76 | { |
| 77 | struct bpdt_header *h = ptr; |
| 78 | |
| 79 | printf(" * BPDT header\n"); |
| 80 | printf("%-25s 0x%-23.8x\n", "Signature", h->signature); |
| 81 | printf("%-25s %-25d\n", "Descriptor count", h->descriptor_count); |
| 82 | printf("%-25s %d (Layout 1.6)\n", "BPDT Version", h->version); |
| 83 | printf("%-25s 0x%-23x\n", "Reserved", h->reserved); |
| 84 | printf("%-25s 0x%-23x\n", "Whole Checksum", h->whole_checksum); |
| 85 | printf("%-25s 0x%-23x\n", "ROM Checksum", h->rom_checksum); |
| 86 | printf("%-25s 0x%-23x\n", "IFWI Version", h->ifwi_version); |
| 87 | printf("%-25s %d.%d.%d.%d(%.2x.%.2x.%.2x.%.2x)\n", "FIT Tool Version", |
| 88 | h->fit_tool_version.major, h->fit_tool_version.minor, |
| 89 | h->fit_tool_version.build, h->fit_tool_version.hotfix, |
| 90 | h->fit_tool_version.major, h->fit_tool_version.minor, |
| 91 | h->fit_tool_version.build, h->fit_tool_version.hotfix); |
| 92 | } |
| 93 | |
| 94 | static bpdt_hdr_ptr read_bpdt_hdr(struct buffer *buff) |
| 95 | { |
| 96 | struct bpdt_header *h = malloc(sizeof(*h)); |
| 97 | |
| 98 | if (!h) |
| 99 | return NULL; |
| 100 | |
| 101 | READ_MEMBER(buff, h->signature); |
| 102 | READ_MEMBER(buff, h->descriptor_count); |
| 103 | READ_MEMBER(buff, h->version); |
| 104 | READ_MEMBER(buff, h->reserved); |
| 105 | READ_MEMBER(buff, h->whole_checksum); |
| 106 | READ_MEMBER(buff, h->rom_checksum); |
| 107 | READ_MEMBER(buff, h->ifwi_version); |
| 108 | READ_MEMBER(buff, h->fit_tool_version); |
| 109 | |
| 110 | return h; |
| 111 | } |
| 112 | |
| 113 | static int write_bpdt_hdr(struct buffer *buff, const bpdt_hdr_ptr ptr) |
| 114 | { |
| 115 | struct bpdt_header *h = ptr; |
| 116 | |
| 117 | if (buffer_size(buff) < sizeof(struct bpdt_header)) { |
| 118 | ERROR("Not enough size in buffer for BPDT header!\n"); |
| 119 | return -1; |
| 120 | } |
| 121 | |
| 122 | WRITE_MEMBER(buff, h->signature); |
| 123 | WRITE_MEMBER(buff, h->descriptor_count); |
| 124 | WRITE_MEMBER(buff, h->version); |
| 125 | WRITE_MEMBER(buff, h->reserved); |
| 126 | WRITE_MEMBER(buff, h->whole_checksum); |
| 127 | WRITE_MEMBER(buff, h->rom_checksum); |
| 128 | WRITE_MEMBER(buff, h->ifwi_version); |
| 129 | WRITE_MEMBER(buff, h->fit_tool_version); |
| 130 | |
| 131 | return 0; |
| 132 | } |
| 133 | |
| 134 | static size_t get_bpdt_entry_count(const bpdt_hdr_ptr ptr) |
| 135 | { |
| 136 | return ((const struct bpdt_header *)ptr)->descriptor_count; |
| 137 | } |
| 138 | |
| 139 | static void inc_bpdt_entry_count(bpdt_hdr_ptr ptr) |
| 140 | { |
| 141 | struct bpdt_header *h = ptr; |
| 142 | h->descriptor_count++; |
| 143 | } |
| 144 | |
Furquan Shaikh | 8b4ca15 | 2021-10-09 23:06:09 -0700 | [diff] [blame] | 145 | static cse_layout_ptr create_cse_layout(const struct region *r) |
Furquan Shaikh | d7fb6a9 | 2021-09-16 22:04:11 -0700 | [diff] [blame] | 146 | { |
| 147 | struct cse_layout *l = malloc(sizeof(*l)); |
| 148 | |
| 149 | if (!l) |
| 150 | return NULL; |
| 151 | |
Nico Huber | d7612e9 | 2024-01-11 18:50:50 +0100 | [diff] [blame^] | 152 | l->data_offset = region_offset(&r[DP]); |
| 153 | l->data_size = region_sz(&r[DP]); |
| 154 | l->bp1_offset = region_offset(&r[BP1]); |
| 155 | l->bp1_size = region_sz(&r[BP1]); |
| 156 | l->bp2_offset = region_offset(&r[BP2]); |
| 157 | l->bp2_size = region_sz(&r[BP2]); |
| 158 | l->bp3_offset = region_offset(&r[BP3]); |
| 159 | l->bp3_size = region_sz(&r[BP3]); |
Furquan Shaikh | d7fb6a9 | 2021-09-16 22:04:11 -0700 | [diff] [blame] | 160 | l->checksum = 0; /* unused */ |
| 161 | |
Solomon Alan-Dei | a2503fa | 2022-10-29 08:30:23 -0600 | [diff] [blame] | 162 | return l; |
Furquan Shaikh | d7fb6a9 | 2021-09-16 22:04:11 -0700 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | static void print_cse_layout(const cse_layout_ptr ptr) |
| 166 | { |
| 167 | struct cse_layout *l = ptr; |
| 168 | |
| 169 | printf(" * CSE Layout\n\n"); |
| 170 | printf("ROM Bypass: "); |
| 171 | for (size_t i = 0; i < sizeof(l->rom_bypass); i++) |
| 172 | printf("0x%x ", l->rom_bypass[i]); |
| 173 | printf("\n"); |
| 174 | printf("Data partition offset: 0x%x\n", l->data_offset); |
| 175 | printf("Data partition size: 0x%x\n", l->data_size); |
| 176 | printf("BP1 offset: 0x%x\n", l->bp1_offset); |
| 177 | printf("BP1 size: 0x%x\n", l->bp1_size); |
| 178 | printf("BP2 offset: 0x%x\n", l->bp2_offset); |
| 179 | printf("BP2 size: 0x%x\n", l->bp2_size); |
| 180 | printf("BP3 offset: 0x%x\n", l->bp3_offset); |
| 181 | printf("BP3 size: 0x%x\n", l->bp3_size); |
| 182 | printf("Checksum: 0x%x\n", l->checksum); |
| 183 | } |
| 184 | |
| 185 | static cse_layout_ptr read_cse_layout(struct buffer *buff) |
| 186 | { |
| 187 | struct cse_layout *l = malloc(sizeof(*l)); |
| 188 | |
| 189 | if (!l) |
| 190 | return NULL; |
| 191 | |
| 192 | READ_MEMBER(buff, l->rom_bypass); |
| 193 | READ_MEMBER(buff, l->data_offset); |
| 194 | READ_MEMBER(buff, l->data_size); |
| 195 | READ_MEMBER(buff, l->bp1_offset); |
| 196 | READ_MEMBER(buff, l->bp1_size); |
| 197 | READ_MEMBER(buff, l->bp2_offset); |
| 198 | READ_MEMBER(buff, l->bp2_size); |
| 199 | READ_MEMBER(buff, l->bp3_offset); |
| 200 | READ_MEMBER(buff, l->bp3_size); |
| 201 | READ_MEMBER(buff, l->reserved); |
| 202 | READ_MEMBER(buff, l->checksum); |
| 203 | |
| 204 | return l; |
| 205 | } |
| 206 | |
| 207 | static int write_cse_layout(struct buffer *buff, const cse_layout_ptr ptr) |
| 208 | { |
| 209 | struct cse_layout *l = ptr; |
| 210 | |
| 211 | if (buffer_size(buff) < sizeof(struct cse_layout)) { |
| 212 | ERROR("Not enough size in buffer for CSE layout!\n"); |
| 213 | return -1; |
| 214 | } |
| 215 | |
| 216 | WRITE_MEMBER(buff, l->rom_bypass); |
| 217 | WRITE_MEMBER(buff, l->data_offset); |
| 218 | WRITE_MEMBER(buff, l->data_size); |
| 219 | WRITE_MEMBER(buff, l->bp1_offset); |
| 220 | WRITE_MEMBER(buff, l->bp1_size); |
| 221 | WRITE_MEMBER(buff, l->bp2_offset); |
| 222 | WRITE_MEMBER(buff, l->bp2_size); |
| 223 | WRITE_MEMBER(buff, l->bp3_offset); |
| 224 | WRITE_MEMBER(buff, l->bp3_size); |
| 225 | WRITE_MEMBER(buff, l->reserved); |
| 226 | WRITE_MEMBER(buff, l->checksum); |
| 227 | |
| 228 | return 0; |
| 229 | } |
| 230 | |
| 231 | static void update_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e) |
| 232 | { |
| 233 | (void)ptr; |
| 234 | (void)e; |
| 235 | |
| 236 | /* TODO(b/202549343) */ |
| 237 | ERROR("Update checksum is not supported for 1.6!\n"); |
| 238 | } |
| 239 | |
| 240 | static bool validate_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e) |
| 241 | { |
| 242 | (void)e; |
| 243 | (void)ptr; |
| 244 | |
| 245 | /* TODO(b/202549343) */ |
| 246 | ERROR("Validate checksum is not supported for 1.6!\n"); |
| 247 | |
| 248 | return true; |
| 249 | } |
| 250 | |
| 251 | const struct bpdt_ops bpdt_1_6_ops = { |
| 252 | .match_version = match_version, |
| 253 | |
| 254 | .create_hdr = create_bpdt_hdr, |
| 255 | .print_hdr = print_bpdt_hdr, |
| 256 | .read_hdr = read_bpdt_hdr, |
| 257 | .write_hdr = write_bpdt_hdr, |
| 258 | |
| 259 | .get_entry_count = get_bpdt_entry_count, |
| 260 | .inc_entry_count = inc_bpdt_entry_count, |
| 261 | |
| 262 | .create_layout = create_cse_layout, |
| 263 | .print_layout = print_cse_layout, |
| 264 | .read_layout = read_cse_layout, |
| 265 | .write_layout = write_cse_layout, |
| 266 | |
| 267 | .update_checksum = update_checksum, |
| 268 | .validate_checksum = validate_checksum, |
| 269 | |
| 270 | .subpart_hdr_version = SUBPART_HDR_VERSION_1, |
| 271 | .subpart_entry_version = SUBPART_ENTRY_VERSION_1, |
| 272 | }; |