blob: 16e24ccd24772b198458e993ca19936d26594701 [file] [log] [blame] [edit]
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2022 Google Inc.
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "flash.h"
#include "programmer.h"
#include "libflashrom.h"
#include "writeprotect.h"
static const struct flashchip *find_chip(const char *name)
{
for (const struct flashchip *chip = flashchips; chip && chip->name; chip++)
if (!strcmp(chip->name, name))
return chip;
return NULL;
}
static const char *get_wp_error_str(int err)
{
switch (err) {
case FLASHROM_WP_ERR_CHIP_UNSUPPORTED:
return "WP operations are not implemented for this chip";
case FLASHROM_WP_ERR_READ_FAILED:
return "failed to read the current WP configuration";
case FLASHROM_WP_ERR_WRITE_FAILED:
return "failed to write the new WP configuration";
case FLASHROM_WP_ERR_VERIFY_FAILED:
return "unexpected WP configuration read back from chip";
case FLASHROM_WP_ERR_MODE_UNSUPPORTED:
return "the requested protection mode is not supported";
case FLASHROM_WP_ERR_RANGE_UNSUPPORTED:
return "the requested protection range is not supported";
case FLASHROM_WP_ERR_RANGE_LIST_UNAVAILABLE:
return "could not determine what protection ranges are available";
case FLASHROM_WP_ERR_UNSUPPORTED_STATE:
return "can't operate on current WP configuration of the chip";
}
return "unknown WP error";
}
void chip_4ba_feature_decode(const uint32_t feature_bits)
{
if (feature_bits | FEATURE_4BA_ENTER)
printf(" > Can enter/exit 4BA mode with instructions 0xb7/0xe9 w/o WREN\n");
if (feature_bits | FEATURE_4BA_ENTER_WREN)
printf(" > Can enter/exit 4BA mode with instructions 0xb7/0xe9 after WREN\n");
if (feature_bits | FEATURE_4BA_ENTER_EAR7)
printf(" > Can enter/exit 4BA mode by setting bit7 of the ext addr reg\n");
if (feature_bits | FEATURE_4BA_EAR_C5C8)
printf(" > Regular 3-byte operations can be used by writing the most"\
"significant address byte into an extended address register"\
"(using 0xc5/0xc8 instructions).\n");
if (feature_bits | FEATURE_4BA_EAR_1716)
printf(" > Like FEATURE_4BA_EAR_C5C8 but with 0x17/0x16 instructions.\n");
if (feature_bits | FEATURE_4BA_READ)
printf(" > Native 4BA read instruction (0x13) is supported.\n");
if (feature_bits | FEATURE_4BA_FAST_READ)
printf(" > Native 4BA fast read instruction (0x0c) is supported.\n");
if (feature_bits | FEATURE_4BA_WRITE)
printf(" > Native 4BA byte program (0x12) is supported.\n");
putchar('\n');
}
void print_register_state(uint8_t *reg_values, uint8_t *wp_bit_masks)
{
/*
* The value of last_reg determines how many register values are printed.
*
* TODO: We look through wp_bit_masks to find the last non-zero
* register, but it might be better to just print SR1/SR2/SR3.
* I.e. set last_reg = STATUS3;
*/
enum flash_reg last_reg = STATUS1;
for (enum flash_reg reg = STATUS1; reg < MAX_REGISTERS; reg++) {
if (wp_bit_masks[reg] != 0)
last_reg = reg;
}
printf("\n * SR = {");
for (enum flash_reg reg = STATUS1; reg <= last_reg; reg++) {
if (reg != STATUS1)
printf(", ");
printf("0x%02x", reg_values[reg]);
}
printf("}.\n");
printf(" * SR mask = {");
for (enum flash_reg reg = STATUS1; reg <= last_reg; reg++) {
if (reg != STATUS1)
printf(", ");
printf("0x%02x", wp_bit_masks[reg]);
}
printf("}.\n");
printf(" * SR Value/Mask = ");
for (enum flash_reg reg = STATUS1; reg <= last_reg; reg++) {
if (reg != STATUS1)
printf(" ");
printf("0x%02X 0x%02X", reg_values[reg], wp_bit_masks[reg]);
}
printf("\n");
}
enum flashrom_wp_result print_wp_regmasks(const char *name, uint32_t wp_start, uint32_t wp_len)
{
struct registered_master r_mst = {0};
struct flashctx flash = { .mst = &r_mst };
flash.chip = (struct flashchip *) find_chip(name);
if (flash.chip)
printf(" > found match '%s' in chip db.\n\n", flash.chip->name);
else {
fprintf(stderr, " no match found for '%s' in chip db.\n", name);
exit(1);
}
chip_4ba_feature_decode(flash.chip->feature_bits);
struct flashrom_wp_cfg *cfg = NULL;
enum flashrom_wp_result ret = flashrom_wp_cfg_new(&cfg);
if (ret != FLASHROM_WP_OK) {
fprintf(stderr, " wp init err %d.\n", ret);
return ret;
}
flashrom_wp_set_range(cfg, wp_start, wp_len);
flashrom_wp_set_mode(cfg, FLASHROM_WP_MODE_HARDWARE);
uint8_t reg_values[MAX_REGISTERS] = {0};
uint8_t wp_bit_masks[MAX_REGISTERS] = {0};
uint8_t unused[MAX_REGISTERS];
ret = wp_cfg_to_reg_values(reg_values, wp_bit_masks, unused, &flash, cfg);
flashrom_wp_cfg_release(cfg);
if (ret != FLASHROM_WP_OK) {
fprintf(stderr, " register value/mask calculation err %d.\n", ret);
return ret;
}
print_register_state(reg_values, wp_bit_masks);
return ret;
}
void print_help(int argc, char* argv[])
{
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
"Required arguments:\n"
" -n, --name=name Name of chip to \n"
" calculate SR values for\n"
" -s, --start=addr Start address of protection range\n"
" -l, --length=addr Length of protection range\n\n"
"Optional arguments:\n"
" -h, --help Print help and exit\n\n",
argv[0]);
}
int main(int argc, char* argv[])
{
char *name = NULL;
uint32_t wp_start = 0, wp_len = 0; /* default */
bool wp_start_set = false, wp_len_set = false;
static const char optstr[] = "hn:s:l:";
static const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"name", 1, NULL, 'n'},
{"start", 1, NULL, 's'},
{"length", 1, NULL, 'l'},
{NULL, 0, NULL, 0},
};
int opt, opt_idx = 0;
while ((opt = getopt_long(argc, argv, optstr, long_options, &opt_idx)) != EOF) {
switch (opt) {
case 'n':
name = optarg;
break;
case 's':
wp_start = strtoul(optarg, NULL, 0);
wp_start_set = true;
break;
case 'l':
wp_len = strtoul(optarg, NULL, 0);
wp_len_set = true;
break;
case 'h':
default:
print_help(argc, argv);
return 0;
}
}
if (!name) {
fprintf(stderr, "Error: --name <chip name> must be provided\n");
return 1;
}
if (!wp_start_set) {
fprintf(stderr, "Error: --start <address> must be provided\n");
return 1;
}
if (!wp_len_set) {
fprintf(stderr, "Error: --length <len> must be provided\n");
return 1;
}
printf(" > requested chip name: '%s' with start: 0x%x and len: 0x%x.\n", name, wp_start, wp_len);
enum flashrom_wp_result ret = print_wp_regmasks(name, wp_start, wp_len);
if (ret != FLASHROM_WP_OK) {
fprintf(stderr, "Error: '%s'\n", get_wp_error_str(ret));
return 1;
}
return 0;
}