blob: 2d93128eb5d18a98b80a8fd49a11c82922ba75df [file] [log] [blame]
/*
* 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 rom3read_prepare(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) {
/*
* 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");
flash_size = estimate_addressable_size(rom3, 64*MiB);
}
flash->chip.total_size = flash_size / KiB;
flash->chip.feature_bits |= FEATURE_NO_ERASE;
flash->chip.tested = (struct flashprog_test_status)
{ .probe = OK, .read = OK, .erase = NA, .write = NA, .block_protection = 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,
.prepare = rom3read_prepare,
.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);
}