From patchwork Thu Jul 28 12:15:27 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hatim Kanchwala X-Patchwork-Id: 653698 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.coreboot.org (mail.coreboot.org [80.81.252.135]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3s0W8N2hMbz9t0G for ; Thu, 28 Jul 2016 22:17:40 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=hatimak-me.20150623.gappssmtp.com header.i=@hatimak-me.20150623.gappssmtp.com header.b=jM/WuL+N; dkim-atps=neutral Received: from [127.0.0.1] (helo=ra.coresystems.de) by mail.coreboot.org with esmtp (Exim 4.86_2) (envelope-from ) id 1bSkEb-0005IE-II; Thu, 28 Jul 2016 14:16:17 +0200 Received: from mail-pa0-f68.google.com ([209.85.220.68]) by mail.coreboot.org with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.86_2) (envelope-from ) id 1bSkE6-0005EF-K7 for flashrom@flashrom.org; Thu, 28 Jul 2016 14:16:06 +0200 Received: by mail-pa0-f68.google.com with SMTP id ez1so3466267pab.3 for ; Thu, 28 Jul 2016 05:15:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hatimak-me.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=Q8fgmDfn4vjCrW8beGwA/N4rIHyb8olh0WNy4lDg3Tw=; b=jM/WuL+Noiv4DudRB/27s9MCtQ4F+VDaHDl+XjE/gaLlx9JQ95YATbJLGVIKdFqNPq tXbC0qF5sE65ETOvRPYRC4ENOI6+oAKz3x7F7Wu11kd+4SOTAG4sC0ObfZoXZXJtQFdo DWLO95nwBWUYcmgqhbXDsFrCynSc+k25LCMTDgu7SPnfvLh1/3wfNW4n5KxNjsXdr6Wx MvCyqStaneqEOzMXJjr+x9zGo8+0wncCPDLmycNCVUBX7jG6u7a2qfLK7RWFRcQ4bqHW XnQmrsBn+IXet0fqPeJyzy5YuVbPqCnVngBy7RhwxtlCCVONTPcX7Fb3G/Dczyy/YTp3 dO/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=Q8fgmDfn4vjCrW8beGwA/N4rIHyb8olh0WNy4lDg3Tw=; b=RFiD6FPMkBNB8FBGHN2ZTtoAGHNp3gW0UhgSZJaui6/cTeKEVs/ChFkOG9MPnOpzoj MbHGA/rVcbnuFjVjBe7jmDldlYEpf9Rnk+4ujkvf+6tw8d9F4aMKrEf5Bw2/ShEdgClu 1Wnihi8NgspPqTCE9RRZAQdmh5946EiYnGZ8648hlHxF0TuG8UOFcKqPB7rEjhHUXtDd fYtDutz2CXLEfMBDr/A3nJVxZUfhqGfP827lRQiQtXgmDsa5tTu9BuxRmKmLtDzcMlw0 FEwTuulfh5cN9It5Z4IqKV3qPOGuJ8Ie3+ZfjX9B6T8bHoN5EtulMsE1Hz2LwVG34iI5 Kaxw== X-Gm-Message-State: AEkoouvgu8kHqBIkxqfXmFynGZnPqC8oACqzz61/PvUBD1MfpBFnCpzkbXqzOKdqzY8l/Q== X-Received: by 10.66.55.35 with SMTP id o3mr58162793pap.90.1469708142842; Thu, 28 Jul 2016 05:15:42 -0700 (PDT) Received: from ubuntu-lenovo-z580.domain.name ([150.129.237.248]) by smtp.gmail.com with ESMTPSA id fe8sm11309081pad.2.2016.07.28.05.15.41 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 28 Jul 2016 05:15:42 -0700 (PDT) From: Hatim Kanchwala To: flashrom@flashrom.org Date: Thu, 28 Jul 2016 17:45:27 +0530 Message-Id: <1469708129-1422-3-git-send-email-hatim@hatimak.me> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1469708129-1422-1-git-send-email-hatim@hatimak.me> References: <1469708129-1422-1-git-send-email-hatim@hatimak.me> X-Spam-Score: 1.3 (+) Subject: [flashrom] [PATCH 2/4] OTP/Security Register infrastructure X-BeenThere: flashrom@flashrom.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: flashrom discussion and development mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: flashrom-bounces@flashrom.org Sender: "flashrom" X-Duff: Orig. Duff, Duff Lite, Duff Dry, Duff Dark, Raspberry Duff, Lady Duff, Red Duff, Tartar Control Duff - Read, write or erase OTP memory - Two distinct models (not exhaustive) - - Security sector (Eon) - Separate memory array accessible as a normal sector while in OTP mode, WRSR locks it permanently - Security Registers (GigaDevice and Winbond) - Separate memory location with dedicated opcodes for read, program and erase; OTP status controlled by Lock Bits (LB1, LB2, ...) in status register(s) - struct region represents a quantum of OTP memory - start address, size and modifier bit in status register (generic design applies to all aforementioned models) - struct flashchip contains pointer to struct otp, which has members to represent OTP regions and function pointers to fetch and print status, read, write, erase and lock OTP regions Signed-off-by: Hatim Kanchwala --- Makefile | 2 +- chipdrivers.h | 23 ++++ flash.h | 23 ++++ otp.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ otp.h | 28 +++++ spi.h | 20 +++ spi25.c | 73 +++++++++++ 7 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 otp.c create mode 100644 otp.h diff --git a/Makefile b/Makefile index c274e79..e97f641 100644 --- a/Makefile +++ b/Makefile @@ -503,27 +503,27 @@ override CONFIG_SATAMV = no endif ifeq ($(CONFIG_IT8212), yes) UNSUPPORTED_FEATURES += CONFIG_IT8212=yes else override CONFIG_IT8212 = no endif endif ############################################################################### # Flash chip drivers and bus support infrastructure. CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \ sst28sf040.o 82802ab.o sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o \ - spi25.o spi25_statusreg.o writeprotect.o statusreg_layouts.o \ + spi25.o spi25_statusreg.o otp.o writeprotect.o statusreg_layouts.o \ writeprotect_layouts.o opaque.o sfdp.o en29lv640b.o at45db.o ############################################################################### # Library code. LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o ############################################################################### # Frontend related stuff. CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o # Set the flashrom version string from the highest revision number of the checked out flashrom files. diff --git a/chipdrivers.h b/chipdrivers.h index d3b27cc..ab18cc5 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -16,26 +16,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * * Header file for flash chip drivers. Included from flash.h. * As a general rule, every function listed here should take a pointer to * struct flashctx as first parameter. */ #ifndef __CHIPDRIVERS_H__ #define __CHIPDRIVERS_H__ 1 #include "flash.h" /* for chipaddr and flashctx */ +#include "otp.h" /* For enum otp_region */ #include "spi25_statusreg.h" /* For enum status_register_num */ #include "writeprotect.h" /* spi.c */ int spi_aai_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, int unsigned len); /* spi25.c */ int probe_spi_rdid(struct flashctx *flash); int probe_spi_rdid4(struct flashctx *flash); int probe_spi_rems(struct flashctx *flash); int probe_spi_res1(struct flashctx *flash); @@ -52,26 +53,30 @@ int spi_block_erase_62(struct flashctx *flash, unsigned int addr, unsigned int b int spi_block_erase_81(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_c4(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen); erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode); int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int spi_byte_program(struct flashctx *flash, unsigned int addr, uint8_t databyte); int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); int spi_nbyte_read(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize); int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize); +int spi_enter_otp_mode(struct flashctx *flash); +int spi_sec_reg_read(struct flashctx *flash, uint8_t *buf, uint32_t start_addr, uint32_t len); +int spi_sec_reg_prog(struct flashctx *flash, uint8_t const *buf, uint32_t start_addr, uint32_t len); +int spi_sec_reg_erase(struct flashctx *flash, uint32_t addr); /* spi25_statusreg.c */ uint8_t spi_read_status_register(struct flashctx *flash); uint8_t spi_read_status_register_generic(struct flashctx *flash, enum status_register_num SRn); int spi_write_status_register(struct flashctx *flash, int status); int spi_write_status_register_generic(struct flashctx *flash, enum status_register_num SRn, uint8_t status); enum status_register_num top_status_register(struct flashctx *flash); char pos_bit(struct flashctx *flash, enum status_register_bit bit); enum wp_mode get_wp_mode_generic(struct flashctx *flash); int set_wp_mode_generic(struct flashctx *flash, enum wp_mode wp_mode); int spi_prettyprint_status_register_generic(struct flashctx *flash, enum status_register_num SRn); int spi_prettyprint_status_register_wp_generic(struct flashctx *flash); void spi_prettyprint_status_register_bit(uint8_t status, int bit); @@ -104,26 +109,44 @@ int spi_disable_blockprotect_at25f(struct flashctx *flash); int spi_disable_blockprotect_at25f512a(struct flashctx *flash); int spi_disable_blockprotect_at25f512b(struct flashctx *flash); int spi_disable_blockprotect_at25fs010(struct flashctx *flash); int spi_disable_blockprotect_at25fs040(struct flashctx *flash); int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash); int spi_prettyprint_status_register_n25q(struct flashctx *flash); int spi_disable_blockprotect_n25q(struct flashctx *flash); int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash); int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash); int spi_prettyprint_status_register_sst25(struct flashctx *flash); int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash); int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash); +/* otp.c */ +int eon_status_generic(struct flashctx *flash, enum otp_region otp_region); +int eon_print_status_generic(struct flashctx *flash); +int eon_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int eon_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int eon_erase_generic(struct flashctx *flash, enum otp_region otp_region); +int eon_lock_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_status_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_print_status_generic(struct flashctx *flash); +int gd_w_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int gd_w_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len); +int gd_erase_generic(struct flashctx *flash, enum otp_region otp_region); +int gd_w_lock_generic(struct flashctx *flash, enum otp_region otp_region); + /* writeprotect.c */ struct range *sec_block_range_pattern(struct flashctx *flash); char get_cmp(struct flashctx *flash); int set_cmp(struct flashctx *flash, uint8_t cmp); uint32_t bp_bitmask_generic(struct flashctx *flash); struct range *bp_to_range(struct flashctx *flash, unsigned char bp_config); int range_to_bp_bitfield(struct flashctx *flash, uint32_t start, uint32_t len); int print_range_generic(struct flashctx *flash); int print_table_generic(struct flashctx *flash); int set_range_generic(struct flashctx *flash, uint32_t start, uint32_t len); int disable_generic(struct flashctx *flash); struct range *range_table_global(struct flashctx *flash); struct range *a25l032_range_table(struct flashctx *flash); diff --git a/flash.h b/flash.h index 631c1a8..c37426c 100644 --- a/flash.h +++ b/flash.h @@ -27,26 +27,27 @@ #include "platform.h" #include #include #include #include #include #if IS_WINDOWS #include #undef min #undef max #endif +#include "otp.h" #include "spi25_statusreg.h" #include "writeprotect.h" #define ERROR_PTR ((void*)-1) /* Error codes */ #define ERROR_OOM -100 #define TIMEOUT_ERROR -101 /* TODO: check using code for correct usage of types */ typedef uintptr_t chipaddr; #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2)) @@ -240,26 +241,48 @@ struct flashchip { struct range *ranges; /* Either ranges is assigned to or range_table is, NOT both. */ /* Return pointer to WP range table. */ struct range *(*range_table) (struct flashctx *flash); /* Return BP(BP0, BP1, ... , SEC, TB) bit mask. */ uint32_t (*bp_bitmask) (struct flashctx *flash); /* Given a range, set the corresponding BP and CMP bit (if present) in the status * register. If range is invalid, return -1 and abort writing to status register. */ int (*set_range) (struct flashctx *flash, uint32_t start, uint32_t len); /* Disable any block protection in effect. */ int (*disable) (struct flashctx *flash); int (*print_table) (struct flashctx *flash); } *wp; + + struct otp { + struct region { + /* This address corresponds to the first byte in the OTP memory region. */ + uint32_t addr; + uint32_t size; /* in bytes */ + + /* Usually, setting this modifier bit will permanently lock the + * corresponding OTP region against writes. + * Not all chips have a modifier bit (AMIC, Macronix). */ + enum status_register_bit status_bit; + } region[MAX_OTP_REGIONS + 1]; /* We need one more than MAX_STATUS_REGISTERS */ + + int (*status) (struct flashctx *flash, enum otp_region otp_region); + int (*print_status) (struct flashctx *flash); + int (*read) (struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_addr, uint32_t len); + int (*write) (struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_addr, uint32_t len); + int (*erase) (struct flashctx *flash, enum otp_region otp_region); + int (*lock) (struct flashctx *flash, enum otp_region otp_region); + } *otp; }; struct flashctx { struct flashchip *chip; /* FIXME: The memory mappings should be saved in a more structured way. */ /* The physical_* fields store the respective addresses in the physical address space of the CPU. */ uintptr_t physical_memory; /* The virtual_* fields store where the respective physical address is mapped into flashrom's address * space. A value equivalent to (chipaddr)ERROR_PTR indicates an invalid mapping (or none at all). */ chipaddr virtual_memory; /* Some flash devices have an additional register space; semantics are like above. */ uintptr_t physical_registers; chipaddr virtual_registers; diff --git a/otp.c b/otp.c new file mode 100644 index 0000000..4af4b84 --- /dev/null +++ b/otp.c @@ -0,0 +1,381 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2016 Hatim Kanchwala + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "chipdrivers.h" +#include "flash.h" +#include "spi25_statusreg.h" + +/* Return the top-most (highest) OTP region of flash. */ +static enum otp_region top_otp_region(struct flashctx *flash) { + enum otp_region otp_region = OTP_REG_1; + struct region *region = flash->chip->otp->region; + + while (region[otp_region++].size != 0) + ; + return otp_region - 2; +} + +/* Some standard error checking used by program and erase functions. */ +static int otp_error_check(struct flashctx *flash, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + if (otp_region > top_otp_region(flash)) { + msg_cdbg("Trying to access non-existent OTP region %d\n%s has only %d OTP regions\n", + otp_region + 1, flash->chip->name, top_otp_region(flash) + 1); + return 1; + } + if (start_byte + len > flash->chip->otp->region[otp_region].size) { + msg_cdbg("OTP region for %s is %d bytes\n", flash->chip->name, + flash->chip->otp->region[otp_region].size); + return 1; + } + return 0; +} + +/* === Eon chip specific functions === */ +static uint8_t bp_bitfield, to_restore = 0; + +static void save_bp(struct flashctx *flash) { + uint8_t status = flash->chip->status_register->read(flash, SR1); + uint32_t bp_bitmask = flash->chip->wp->bp_bitmask(flash); + bp_bitfield = (status & bp_bitmask) >> pos_bit(flash, BP0); +} + +static int restore_bp(struct flashctx *flash) { + uint8_t status = flash->chip->status_register->read(flash, SR1); + uint32_t bp_bitmask = flash->chip->wp->bp_bitmask(flash); + status = ((status & ~bp_bitmask) | (bp_bitfield << pos_bit(flash, BP0))) & 0xff; + return flash->chip->status_register->write(flash, SR1, status); +} + +/* Enter OTP mode. If any Block Protect bits are set, then save + * their state and temporarily unset them all. */ +static int enter_otp_mode(struct flashctx *flash) { + uint8_t bp = flash->chip->status_register->read(flash, SR1) & flash->chip->wp->bp_bitmask(flash); + if (bp) { + msg_cdbg("Need to unset all BP bits before entering OTP mode ...\n"); + msg_cdbg("BP bits will be restored to 0x%02x\n", bp >> pos_bit(flash, BP0)); + to_restore = 1; + save_bp(flash); + } + return spi_enter_otp_mode(flash); +} + +/* Exit OTP mode. If any Block Protect bits were set prior to issuing + * an Enter OTP, then restore those bits after exiting. */ +static int exit_otp_mode(struct flashctx *flash) { + int result = spi_write_disable(flash); + if (result) { + msg_cdbg("Couldn't exit OTP mode\n"); + return result; + } + + if (to_restore) { + msg_cdbg("Restoring BP bits to their state prior to entering OTP mode ...\n"); + result = restore_bp(flash); + if (result) + msg_cdbg("Couldn't restore BP bits\n"); + } + to_restore = 0; + return result; +} + +int eon_status_generic(struct flashctx *flash, enum otp_region otp_region) { + enter_otp_mode(flash); + uint8_t status = (flash->chip->status_register->read(flash, SR1) & + (1 << pos_bit(flash, SRP0))) ? 1 : 0; + exit_otp_mode(flash); + return status; +} + +int eon_print_status_generic(struct flashctx *flash) { + enum otp_region top_region = top_otp_region(flash), region_n; + msg_cdbg("%s contains %d OTP memory region%s (also called OTP sector%s) -\n", + flash->chip->name, top_region + 1, + (top_region == 0) ? "" : "s", (top_region == 0) ? "" : "s"); + + for (region_n = OTP_REG_1; region_n <= top_region; region_n++) { + msg_cdbg("OTP memory region %d: %d bytes, controlled by %s bit in status register %d " + "(while in OTP mode)\n", region_n + 1, flash->chip->otp->region[region_n].size, + statreg_bit_desc[flash->chip->otp->region[region_n].status_bit][0], + (pos_bit(flash, flash->chip->otp->region[region_n].status_bit) / 8) + 1); + if (flash->chip->otp->status(flash, region_n)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased " + "or written to\n", region_n + 1); + } + } + return 0; +} + +/* Read len bytes of the security register (corresponding to otp_region) into buf, + * starting from start_byte. */ +int eon_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + enter_otp_mode(flash); + result = flash->chip->read(flash, buf, start_byte, len); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Write len bytes to the security register (corresponding to otp_region) form buf, + * starting from start_byte. */ +int eon_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + enter_otp_mode(flash); + result = flash->chip->write(flash, buf, start_byte, len); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Erase the security register corresponding to otp_region. */ +int eon_erase_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + enter_otp_mode(flash); + result = spi_block_erase_20(flash, flash->chip->otp->region[otp_region].addr, + flash->chip->otp->region[otp_region].size); + exit_otp_mode(flash); + + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Lock the OTP memory corresponding to otp_region. The corresponding bit + * in the status register is set (which is one-time programmable). For Eon + * chips, the SRP/SRP0/SRWD bit is served as OTP it while in OTP mode. + * Note that if the bit was already set, the function does not consider + * it a point of failure. */ +int eon_lock_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + enum status_register_bit status_bit = flash->chip->otp->region[otp_region].status_bit; + if (pos_bit(flash, status_bit) == -1) { + /* Check if such a bit even exists in the status register in the first place. */ + // TODO(hatim): This block does not seem to have many use cases as the error + // can be avoided while reviewing patches itself + msg_cdbg("OTP modifier bit %s for %s defined incorrectly\n", + statreg_bit_desc[status_bit][0], flash->chip->name); + msg_cerr("%s failed\n", __func__); + return 1; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP modifier bit already set, " + "cannot alter value as it is one-time-programmable only\n"); + // FIXME(hatim): Should we return zero or non-zero here? + return 0; + } + + enter_otp_mode(flash); + /* WRSR will set OTP modifier bit irrespective of status byte supplied. */ + flash->chip->status_register->write(flash, SR1, 1 << pos_bit(flash, SRP0)); + exit_otp_mode(flash); + + if (!flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("Unable to set OTP modifier bit\n"); + msg_cerr("%s failed\n", __func__); + return 1; + } else + return 0; +} + + +/* === GigaDevice and (most) Winbond chip specific functions === */ +/* Get the OTP modifier bit (these are usually the LB1, LB2, ... bits) from the status + * registers. */ +static int gd_w_get_otp_bit(struct flashctx *flash, enum status_register_bit modifier_bit) { + enum status_register_num SRn = pos_bit(flash, modifier_bit) / 8; + uint8_t modifier_bit_mask = 1 << (pos_bit(flash, modifier_bit) - (SRn * 8)); + + uint8_t status = flash->chip->status_register->read(flash, SRn); + return status & modifier_bit_mask; +} + +/* Set the OTP modifier bit (these are usually the LB1, LB2, ... bits) in the status registers. + * We take no value of the bit as an argument because they are one-time-programmable only and + * they can only be set. */ +static int gd_w_set_otp_bit(struct flashctx *flash, enum status_register_bit modifier_bit) { + enum status_register_num SRn = pos_bit(flash, modifier_bit) / 8; + uint8_t modifier_bit_mask = 1 << (pos_bit(flash, modifier_bit) - (SRn * 8)); + + uint8_t status = flash->chip->status_register->read(flash, SRn); + status = (status & ~modifier_bit_mask) & 0xff; + status |= modifier_bit_mask; + return flash->chip->status_register->write(flash, SRn, status); +} + +int gd_w_status_generic(struct flashctx *flash, enum otp_region otp_region) { + return gd_w_get_otp_bit(flash, flash->chip->otp->region[otp_region].status_bit) ? 1 : 0; +} + +int gd_w_print_status_generic(struct flashctx *flash) { + enum otp_region top_region = top_otp_region(flash), region_n; + msg_cdbg("%s contains %d OTP memory region%s (also called Security Register%s) -\n", + flash->chip->name, top_region + 1, + (top_region == 0) ? "" : "s", (top_region == 0) ? "" : "s"); + + for (region_n = OTP_REG_1; region_n <= top_region; region_n++) { + msg_cdbg("OTP memory region %d: %d bytes, controlled by %s bit in status register %d\n", + region_n + 1, + flash->chip->otp->region[region_n].size, + statreg_bit_desc[flash->chip->otp->region[region_n].status_bit][0], + (pos_bit(flash, flash->chip->otp->region[region_n].status_bit) / 8) + 1); + if (flash->chip->otp->status(flash, region_n)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased " + "or written to\n", region_n + 1); + } + } + return 0; +} + +/* Read len bytes of the security register (corresponding to otp_region) into buf, + * starting from start_byte. */ +int gd_w_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + /* Prefix the first couple of pre-defined bits of the security register address. */ + result = spi_sec_reg_read(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Write len bytes to the security register (corresponding to otp_region) form buf, + * starting from start_byte. */ +int gd_w_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region, + uint32_t start_byte, uint32_t len) { + int result = otp_error_check(flash, otp_region, start_byte, len); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + /* Prefix the first couple of pre-defined bits of the security register address. */ + result = spi_sec_reg_prog(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Erase the security register corresponding to otp_region. */ +int gd_erase_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP memory region %d is permanently locked and cannot be erased\n", + otp_region + 1); + msg_cerr("%s failed\n", __func__); + return 1; + } + + result = spi_sec_reg_erase(flash, flash->chip->otp->region[otp_region].addr); + if (result) + msg_cerr("%s failed\n", __func__); + return result; +} + +/* Lock the OTP memory corresponding to otp_region. The corresponding bit + * in the status register is set (which is one-time programmable). + * Note that if the bit was already set, the function does not consider + * it a point of failure. */ +int gd_w_lock_generic(struct flashctx *flash, enum otp_region otp_region) { + int result = otp_error_check(flash, otp_region, 0x000000, 0); + if (result) { + msg_cerr("%s failed\n", __func__); + return result; + } + + enum status_register_bit status_bit = flash->chip->otp->region[otp_region].status_bit; + if (pos_bit(flash, status_bit) == -1) { + /* Check if such a bit even exists in the status register in the first place. */ + // TODO(hatim): This block does not seem to have many use cases as the error + // can be avoided while reviewing patches itself + msg_cdbg("OTP modifier bit %s for %s defined incorrectly\n", + statreg_bit_desc[status_bit][0], flash->chip->name); + msg_cerr("%s failed\n", __func__); + return 1; + } + if (flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("OTP modifier bit already set, " + "cannot alter value as it is one-time-programmable only\n"); + // FIXME(hatim): Should we return zero or non-zero here? + return 0; + } else { + result = gd_w_set_otp_bit(flash, status_bit); + if (result) + msg_cerr("%s failed\n", __func__); + if (!flash->chip->otp->status(flash, otp_region)) { + msg_cdbg("Unable to set OTP modifier bit\n"); + msg_cerr("%s failed\n", __func__); + return 1; + } + return result; + } +} diff --git a/otp.h b/otp.h new file mode 100644 index 0000000..790c8c5 --- /dev/null +++ b/otp.h @@ -0,0 +1,28 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2016 Hatim Kanchwala + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __OTP_H__ +#define __OTP_H__ 1 + +#define MAX_OTP_REGIONS 4 + +enum otp_region { OTP_REG_1 = 0, OTP_REG_2, OTP_REG_3 }; + +#endif /* !__OTP_H__ */ diff --git a/spi.h b/spi.h index f061041..90bff5f 100644 --- a/spi.h +++ b/spi.h @@ -148,22 +148,42 @@ /* JEDEC_READ_INSIZE : any length */ /* Write memory byte */ #define JEDEC_BYTE_PROGRAM 0x02 #define JEDEC_BYTE_PROGRAM_OUTSIZE 0x05 #define JEDEC_BYTE_PROGRAM_INSIZE 0x00 /* Write AAI word (SST25VF080B) */ #define JEDEC_AAI_WORD_PROGRAM 0xad #define JEDEC_AAI_WORD_PROGRAM_OUTSIZE 0x06 #define JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE 0x03 #define JEDEC_AAI_WORD_PROGRAM_INSIZE 0x00 +/* Enter OTP mode (supported by most Eon chips) */ +#define JEDEC_ENTER_OTP 0x3A +#define JEDEC_ENTER_OTP_OUTSIZE 0x01 +#define JEDEC_ENTER_OTP_INSIZE 0x00 + +/* Read Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_READ_SEC_REG 0x48 +#define JEDEC_READ_SEC_REG_OUTSIZE 0x05 +/* JEDEC_READ_SEC_REG_INSIZE any length */ + +/* Program Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_PROG_BYTE_SEC_REG 0x42 +#define JEDEC_PROG_BYTE_SEC_REG_OUTSIZE 0x05 +#define JEDEC_PROG_BYTE_SEC_REG_INSIZE 0x00 + +/* Erase Security Register(s) (supported by most GigaDevice chips) */ +#define JEDEC_ERASE_SEC_REG 0x44 +#define JEDEC_ERASE_SEC_REG_OUTSIZE 0x04 +#define JEDEC_ERASE_SEC_REG_INSIZE 0x00 + /* Error codes */ #define SPI_GENERIC_ERROR -1 #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 #define SPI_FLASHROM_BUG -5 #define SPI_PROGRAMMER_ERROR -6 #endif /* !__SPI_H__ */ diff --git a/spi25.c b/spi25.c index 51db4c8..a38623c 100644 --- a/spi25.c +++ b/spi25.c @@ -1259,13 +1259,86 @@ int default_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned i if (spi_chip_write_1(flash, buf + pos - start, pos, pos % 2)) return SPI_GENERIC_ERROR; pos += pos % 2; } return 0; bailout: result = spi_write_disable(flash); if (result != 0) msg_cerr("%s failed to disable AAI mode.\n", __func__); return SPI_GENERIC_ERROR; } + + +/* This function is specific to mostly Eon chips. It maps the + * additional OTP sector to the top or bottom sector (depending + * on the chip). The mapped sector behaves like just another + * normal sector. */ +int spi_enter_otp_mode(struct flashctx *flash) +{ + static const unsigned char cmd[JEDEC_ENTER_OTP_OUTSIZE] = { JEDEC_ENTER_OTP }; + return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); +} + +int spi_sec_reg_read(struct flashctx *flash, uint8_t *buf, uint32_t start_addr, uint32_t len) +{ + /* We assume that start_addr and len are correct and proceed without any error checking. */ + uint8_t cmd[JEDEC_READ_SEC_REG_OUTSIZE] = { + (uint8_t)JEDEC_READ_SEC_REG, + (uint8_t)(start_addr >> 16) & 0xff, + (uint8_t)(start_addr >> 8) & 0xff, + (uint8_t)start_addr & 0xff, + (uint8_t)0x00, + }; + + int result = spi_send_command(flash, sizeof(cmd), len, cmd, buf); + if (result) + msg_cerr("%s failed.\n", __func__); + return result; +} + +int spi_sec_reg_prog(struct flashctx *flash, uint8_t const *buf, uint32_t start_addr, uint32_t len) +{ + /* We assume that start_addr and len are correct, the security register is unlocked + * and proceed without any error checking. */ + int ret = spi_write_enable(flash); + if (ret) { + msg_cerr("%s failed\n", __func__); + return ret; + } + + uint8_t cmd[JEDEC_PROG_BYTE_SEC_REG_OUTSIZE - 1 + len]; + cmd[0] = (uint8_t)JEDEC_PROG_BYTE_SEC_REG; + cmd[1] = (uint8_t)(start_addr >> 16) & 0xff; + cmd[2] = (uint8_t)(start_addr >> 8) & 0xff; + cmd[3] = (uint8_t)start_addr & 0xff; + memcpy((void *)&cmd[4], (void *)buf, (size_t)len); + // FIXME(hatim): We should probably poll WIP bit + ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (ret) + msg_cerr("%s\n", __func__); + return ret; +} + +int spi_sec_reg_erase(struct flashctx *flash, uint32_t addr) +{ + /* We assume that addr is correct and proceed without any error checking. */ + int ret = spi_write_enable(flash); + if (ret) { + msg_cerr("%s failed\n", __func__); + return ret; + } + + uint8_t cmd[JEDEC_ERASE_SEC_REG_OUTSIZE] = { + (uint8_t)JEDEC_ERASE_SEC_REG, + (uint8_t)(addr >> 16) & 0xff, + (uint8_t)(addr >> 8) & 0xff, + (uint8_t)addr & 0xff, + }; + // FIXME(hatim): We should probably poll WIP bit + ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (ret) + msg_cerr("%s\n", __func__); + return ret; +}