blob: 9708ea9bac840f441690558cd09781a4a8edab06 [file] [log] [blame]
Nico Hubera1939832025-10-07 21:58:02 +00001/*
2 * This file is part of the flashprog project.
3 *
4 * Copyright (C) 2025 Nico Huber <nico.h@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <stdbool.h>
18#include <stdint.h>
19#include <stdlib.h>
20#include <string.h>
21#include <inttypes.h>
22
23#include "flash.h"
24#include "hwaccess_physmap.h"
25#include "programmer.h"
26
27struct spi100 {
28 const uint8_t *memory;
29 size_t size_override;
30};
31
32static uint16_t spi100_read16(const char *spibar, unsigned int reg)
33{
34 return mmio_readw(spibar + reg);
35}
36
37static uint32_t spi100_read32(const char *spibar, unsigned int reg)
38{
39 return mmio_readl(spibar + reg);
40}
41
42static uint64_t spi100_read64(const char *spibar, unsigned int reg)
43{
44 return (uint64_t)mmio_readl(spibar + reg + 4) << 32 | mmio_readl(spibar + reg);
45}
46
47static int spi100_mmap_read(struct flashctx *flash, uint8_t *dst, unsigned int start, unsigned int len)
48{
49 const struct spi100 *const spi100 = flash->mst.opaque->data;
50 mmio_readn_aligned(spi100->memory + start, dst, len, 8);
51 return 0;
52}
53
54static int compare64(const char *const s1, const char *const s2, unsigned int offset)
55{
56 offset &= ~63;
57 return memcmp(s1 + offset, s2 + offset, 64);
58}
59
60/* Compare two memory ranges at pseudo-random offsets. */
61static int compare_sparse(const void *const s1, const void *const s2, const size_t n)
62{
63 const unsigned int offsets[] = {
64 12, 123, 1234, 12345, 123456, 123456, 1234567, 12345678, 123456789,
65 0x12, 0x123, 0x1234, 0x12345, 0x123456, 0x1234567, 0x12345678,
66 0, 01, 012, 0123, 01234, 012345, 0123456, 01234567,
67 };
68 const unsigned int step = 1234;
69
70 if (n < step + 64)
71 return 0;
72
73 unsigned int i;
74 for (i = 0; i < ARRAY_SIZE(offsets); ++i) {
75 const unsigned int offset = offsets[i] % ((n - 64) / step) * step;
76
77 const int diff1 = compare64(s1, s2, offset);
78 if (diff1)
79 return diff1;
80
81 const int diff2 = compare64(s1, s2, n - 64 - offset);
82 if (diff2)
83 return diff2;
84 }
85
86 return 0;
87}
88
89static int rom3read_probe(struct flashctx *const flash)
90{
91 const struct spi100 *const spi100 = flash->mst.opaque->data;
92 const void *const rom3 = spi100->memory;
93
94 size_t flash_size = spi100->size_override;
95 if (flash_size)
96 goto size_known;
97
98 /*
99 * Only thing to probe is the size. That's going to be peculiar,
100 * though: As the whole 64MiB rom3 range is decoded, we can only
101 * look for repeating memory contents.
102 */
103 msg_pinfo("Trying to probe flash size based on its contents and read patterns. If this\n"
104 "doesn't work, you can override probing with `-p internal:rom_size_mb=<size>`.\n");
105
106 /*
107 * We start comparing the two halves of the 64 MiB space. And if
108 * they match, split the lower half, and so on until we find a
109 * mismatch (or not, in the unlikely case of empty memory?).
110 */
111 for (flash_size = 64*MiB; flash_size > 0; flash_size /= 2) {
112 if (compare_sparse(rom3, rom3 + flash_size / 2, flash_size / 2))
113 break;
114 }
115
116size_known:
117 flash->chip->total_size = flash_size / KiB;
118 flash->chip->feature_bits |= FEATURE_NO_ERASE;
119 flash->chip->tested =
120 (struct tested){ .probe = OK, .read = OK, .erase = NA, .write = NA, .wp = NA };
121
122 return !!flash->chip->total_size;
123}
124
125static int rom3read_read(struct flashctx *const flash, uint8_t *buf, unsigned int start, unsigned int len)
126{
127 /* Use top-aligned decoding, for some reason it's
128 faster after using the bottom end for probing. */
129 start += 64*MiB - flashprog_flash_getsize(flash);
130 return flashprog_read_chunked(flash, buf, start, len, MAX_DATA_READ_UNLIMITED, spi100_mmap_read);
131}
132
133static int rom3read_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
134{
135 msg_perr("Write is not supported with ROM Armor enabled.\n");
136 return 1;
137}
138
139static int rom3read_erase(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen)
140{
141 msg_perr("Erase is not supported with ROM Armor enabled.\n");
142 return 1;
143}
144
145static int rom3read_shutdown(void *spi100)
146{
147 free(spi100);
148 return 0;
149}
150
151static const struct opaque_master rom3read_master = {
152 .max_data_read = MAX_DATA_UNSPECIFIED,
153 .max_data_write = MAX_DATA_UNSPECIFIED,
154 .probe = rom3read_probe,
155 .read = rom3read_read,
156 .write = rom3read_write,
157 .erase = rom3read_erase,
158 .shutdown = rom3read_shutdown,
159};
160
161static bool spi100_check_4ba(const void *const spibar)
162{
163 const uint16_t rom2_addr_override = spi100_read16(spibar, 0x30);
164 const uint32_t addr32_ctrl3 = spi100_read32(spibar, 0x5c);
165
166 /* Most bits are undocumented ("reserved"), so we play safe. */
167 if (rom2_addr_override != 0x14c0) {
168 msg_perr("ROM2 address override *not* in default configuration.\n");
169 return false;
170 }
171
172 /* Another override (xor'ed) for the most-significant address bits. */
173 if (addr32_ctrl3 & 0xff) {
174 msg_perr("SPI ROM page bits set: 0x%02x\n", addr32_ctrl3 & 0xff);
175 return false;
176 }
177
178 return true;
179}
180
181int amd_rom3read_probe(const void *const spibar, const void *const rom2,
182 const void *const rom3, const size_t rom3_len)
183{
184 if (rom3_len != 64*MiB) {
185 msg_perr("Error: Only 64MiB rom range 3 supported.\n");
186 return ERROR_FATAL;
187 }
188
189 if (!spi100_check_4ba(spibar))
190 return ERROR_FATAL;
191
192 size_t size = 0;
193 char *const size_override = extract_programmer_param("rom_size_mb");
194 if (size_override) {
195 char *endptr;
196 size = strtoul(size_override, &endptr, 10);
197 if (*endptr || size < 1 || size > 64 || (size & (size - 1))) {
198 msg_perr("Error: Invalid ROM size override: \"%s\".\n"
199 "Valid values are powers of 2 from 1 through 64 (MiB).\n",
200 size_override);
201 free(size_override);
202 return -1;
203 }
204 size *= MiB;
205 }
206 free(size_override);
207
208 const uint64_t rom3_base = spi100_read64(spibar, 0x60);
209 if (rom3_base != 0xfd00000000) {
210 msg_perr("Unexpected value for Rom3 base: 0x%"PRIx64"\n", rom3_base);
211 return ERROR_FATAL;
212 }
213
214 if (compare_sparse(rom2, rom3 + 48*MiB, 16*MiB)) {
215 msg_perr("Rom2 and Rom3 don't seem to map the same memory.\n");
216 return ERROR_FATAL;
217 }
218
219 struct spi100 *const spi100 = malloc(sizeof(*spi100));
220 if (!spi100) {
221 msg_perr("Out of memory!\n");
222 return ERROR_FATAL;
223 }
224 spi100->memory = rom3;
225 spi100->size_override = size;
226
227 return register_opaque_master(&rom3read_master, spi100);
228}