blob: ad03fb70b5821f2fe9c8649ed57ac868e7889f44 [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
Nico Huber91f51522026-03-07 22:57:56 +010054static int rom3read_prepare(struct flashctx *const flash)
Nico Hubera1939832025-10-07 21:58:02 +000055{
56 const struct spi100 *const spi100 = flash->mst.opaque->data;
57 const void *const rom3 = spi100->memory;
58
59 size_t flash_size = spi100->size_override;
Nico Hubera19cd392026-07-04 15:25:27 +020060 if (!flash_size) {
61 /*
62 * Only thing to probe is the size. That's going to be peculiar,
63 * though: As the whole 64MiB rom3 range is decoded, we can only
64 * look for repeating memory contents.
65 */
66 msg_pinfo("Trying to probe flash size based on its contents and read patterns. If this\n"
67 "doesn't work, you can override probing with `-p internal:rom_size_mb=<size>`.\n");
Nico Hubera1939832025-10-07 21:58:02 +000068
Nico Hubera19cd392026-07-04 15:25:27 +020069 flash_size = estimate_addressable_size(rom3, 64*MiB);
Nico Hubera1939832025-10-07 21:58:02 +000070 }
71
Nico Hubera1939832025-10-07 21:58:02 +000072 flash->chip->total_size = flash_size / KiB;
73 flash->chip->feature_bits |= FEATURE_NO_ERASE;
74 flash->chip->tested =
75 (struct tested){ .probe = OK, .read = OK, .erase = NA, .write = NA, .wp = NA };
76
77 return !!flash->chip->total_size;
78}
79
80static int rom3read_read(struct flashctx *const flash, uint8_t *buf, unsigned int start, unsigned int len)
81{
82 /* Use top-aligned decoding, for some reason it's
83 faster after using the bottom end for probing. */
84 start += 64*MiB - flashprog_flash_getsize(flash);
85 return flashprog_read_chunked(flash, buf, start, len, MAX_DATA_READ_UNLIMITED, spi100_mmap_read);
86}
87
88static int rom3read_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
89{
90 msg_perr("Write is not supported with ROM Armor enabled.\n");
91 return 1;
92}
93
94static int rom3read_erase(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen)
95{
96 msg_perr("Erase is not supported with ROM Armor enabled.\n");
97 return 1;
98}
99
100static int rom3read_shutdown(void *spi100)
101{
102 free(spi100);
103 return 0;
104}
105
106static const struct opaque_master rom3read_master = {
107 .max_data_read = MAX_DATA_UNSPECIFIED,
108 .max_data_write = MAX_DATA_UNSPECIFIED,
Nico Huber91f51522026-03-07 22:57:56 +0100109 .prepare = rom3read_prepare,
Nico Hubera1939832025-10-07 21:58:02 +0000110 .read = rom3read_read,
111 .write = rom3read_write,
112 .erase = rom3read_erase,
113 .shutdown = rom3read_shutdown,
114};
115
116static bool spi100_check_4ba(const void *const spibar)
117{
118 const uint16_t rom2_addr_override = spi100_read16(spibar, 0x30);
119 const uint32_t addr32_ctrl3 = spi100_read32(spibar, 0x5c);
120
121 /* Most bits are undocumented ("reserved"), so we play safe. */
122 if (rom2_addr_override != 0x14c0) {
123 msg_perr("ROM2 address override *not* in default configuration.\n");
124 return false;
125 }
126
127 /* Another override (xor'ed) for the most-significant address bits. */
128 if (addr32_ctrl3 & 0xff) {
129 msg_perr("SPI ROM page bits set: 0x%02x\n", addr32_ctrl3 & 0xff);
130 return false;
131 }
132
133 return true;
134}
135
136int amd_rom3read_probe(const void *const spibar, const void *const rom2,
137 const void *const rom3, const size_t rom3_len)
138{
139 if (rom3_len != 64*MiB) {
140 msg_perr("Error: Only 64MiB rom range 3 supported.\n");
141 return ERROR_FATAL;
142 }
143
144 if (!spi100_check_4ba(spibar))
145 return ERROR_FATAL;
146
147 size_t size = 0;
148 char *const size_override = extract_programmer_param("rom_size_mb");
149 if (size_override) {
150 char *endptr;
151 size = strtoul(size_override, &endptr, 10);
152 if (*endptr || size < 1 || size > 64 || (size & (size - 1))) {
153 msg_perr("Error: Invalid ROM size override: \"%s\".\n"
154 "Valid values are powers of 2 from 1 through 64 (MiB).\n",
155 size_override);
156 free(size_override);
157 return -1;
158 }
159 size *= MiB;
160 }
161 free(size_override);
162
163 const uint64_t rom3_base = spi100_read64(spibar, 0x60);
164 if (rom3_base != 0xfd00000000) {
165 msg_perr("Unexpected value for Rom3 base: 0x%"PRIx64"\n", rom3_base);
166 return ERROR_FATAL;
167 }
168
169 if (compare_sparse(rom2, rom3 + 48*MiB, 16*MiB)) {
170 msg_perr("Rom2 and Rom3 don't seem to map the same memory.\n");
171 return ERROR_FATAL;
172 }
173
174 struct spi100 *const spi100 = malloc(sizeof(*spi100));
175 if (!spi100) {
176 msg_perr("Out of memory!\n");
177 return ERROR_FATAL;
178 }
179 spi100->memory = rom3;
180 spi100->size_override = size;
181
182 return register_opaque_master(&rom3read_master, spi100);
183}