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