i2SOM: add write bootstream to NAND flash

The u-boot of i.MX6ULL chip needs header data.The update_nand.c will
add header data when writing into NAND flash.
These code reference from Digi ccimx6ul.
This commit is contained in:
SteveChen
2018-03-25 15:25:59 +08:00
parent f7652281cd
commit 5e56f8119d
14 changed files with 4777 additions and 0 deletions

View File

@ -0,0 +1,14 @@
#
# (C) Copyright 2017
# i2SOM Team.
#
# SPDX-License-Identifier: GPL-2.0+
#
$(if $(CONFIG_CMD_BOOTSTREAM), $(shell mkdir -p $(obj)cmd_bootstream))
obj-$(CONFIG_CMD_BOOTSTREAM) += cmd_bootstream/bch.o \
cmd_bootstream/ncb.o \
cmd_bootstream/cmd_bootstream.o \
cmd_bootstream/mxsboot.o
obj-$(CONFIG_CMD_UPDATE_NAND) += cmd_update_nand.o helper.o

View File

@ -0,0 +1,353 @@
/*
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef BOOTCONTROLBLOCKS_H_
#define BOOTCONTROLBLOCKS_H_
#define NCB_FINGERPRINT1 0x504d5453 //!< 'STMP'
#define NCB_FINGERPRINT2 0x2042434e //!< 'NCB<space>' - NAND Control Block
#define NCB_FINGERPRINT3 0x4e494252 //!< 'RBIN' - ROM Boot Image Block - N
#define FCB_FINGERPRINT 0x20424346 //!< 'FCB '
#define FCB_VERSION_1 0x01000000
#define LDLB_FINGERPRINT1 0x504d5453 //!< 'STMP'
#define LDLB_FINGERPRINT2 0x424c444c //!< 'LDLB' - Logical Device Layout Block
#define LDLB_FINGERPRINT3 0x4c494252 //!< 'RBIL' - ROM Boot Image Block - L
#define DBBT_FINGERPRINT1 0x504d5453 //!< 'STMP'
#define DBBT_FINGERPRINT2 0x54424244 //!< 'DBBT' - Discovered Bad Block Table.
#define DBBT_FINGERPRINT3 0x44494252 //!< 'RBID' - ROM Boot Image Block - D
#define DBBT_VERSION_1 0x01000000
#define LDLB_VERSION_MAJOR 0x0001
#define LDLB_VERSION_MINOR 0x0000
#define LDLB_VERSION_SUB 0x0000
#define TYPICAL_NAND_READ_SIZE 2048
#define BCB_MAGIC_OFFSET 12
#define MAXSEQLEN 183
//==============================================================================
//! \brief NAND Timing structure for setting up the GPMI timing.
//!
//! This structure holds the timing for the NAND. This data is used by
//! rom_nand_hal_GpmiSetNandTiming to setup the GPMI hardware registers.
typedef struct _NAND_Timing {
uint8_t m_u8DataSetup;
uint8_t m_u8DataHold;
uint8_t m_u8AddressSetup;
uint8_t m_u8DSAMPLE_TIME;
} NCB_NAND_Timing_t;
//==============================================================================
//! \brief Structure defining where NCB and LDLB parameters are located.
//!
//! This structure defines the basic fingerprint template for both the Nand
//! Control Block (NCB) and the Logical Drive Layout Block (LDLB). This
//! template is used to determine if the sector read is a Boot Control Block.
//! This structure defines the NAND Control Block (NCB). This block
//! contains information describing the timing for the NAND, the number of
//! NANDs in the system, the block size of the NAND, the page size of the NAND,
//! and other criteria for the NAND. This is information that is
//! required just to successfully communicate with the NAND.
//! This structure also defines the Logical Drive Layout Block (LDLB). This
//! block contains information describing the version as well as the layout of
//! the code and data on the NAND Media. For the ROM, we're only concerned
//! with the boot firmware start. Additional information may be stored in
//! the Reserved3 area. This area will be of interest to the SDK.
//! This structure also defines the Discovered Bad Block Table (DBBT) header.
//! This block contains the information used for parsing the bad block tables
//! which are stored in subsequent 2K sectors. The DBBT header is 8K, followed
//! by the first NANDs entries, then the 2nd NANDs entries on a subsequent 2K
//! page (determined by how many 2K pages the first nand requires), and so on.
typedef struct _NCB_BootBlockStruct_t {
uint32_t m_u32FingerPrint1; //!< First fingerprint in first byte.
union {
struct {
NCB_NAND_Timing_t m_NANDTiming; //!< Optimum timing parameters for Tas, Tds, Tdh in nsec.
uint32_t m_u32DataPageSize; //!< 2048 for 2K pages, 4096 for 4K pages.
uint32_t m_u32TotalPageSize; //!< 2112 for 2K pages, 4314 for 4K pages.
uint32_t m_u32SectorsPerBlock; //!< Number of 2K sections per block.
uint32_t m_u32SectorInPageMask; //!< Mask for handling pages > 2K.
uint32_t m_u32SectorToPageShift; //!< Address shift for handling pages > 2K.
uint32_t m_u32NumberOfNANDs; //!< Total Number of NANDs - not used by ROM.
} NCB_Block1;
struct {
struct {
uint16_t m_u16Major;
uint16_t m_u16Minor;
uint16_t m_u16Sub;
uint16_t m_u16Reserved;
} LDLB_Version; //!< LDLB version - not used by ROM.
uint32_t m_u32NANDBitmap; //!< bit 0 == NAND 0, bit 1 == NAND 1, bit 2 = NAND 2, bit 3 = NAND3
} LDLB_Block1;
struct {
uint32_t m_u32NumberBB_NAND0; //!< # Bad Blocks stored in this table for NAND0.
uint32_t m_u32NumberBB_NAND1; //!< # Bad Blocks stored in this table for NAND1.
uint32_t m_u32NumberBB_NAND2; //!< # Bad Blocks stored in this table for NAND2.
uint32_t m_u32NumberBB_NAND3; //!< # Bad Blocks stored in this table for NAND3.
uint32_t m_u32Number2KPagesBB_NAND0; //!< Bad Blocks for NAND0 consume this # of 2K pages.
uint32_t m_u32Number2KPagesBB_NAND1; //!< Bad Blocks for NAND1 consume this # of 2K pages.
uint32_t m_u32Number2KPagesBB_NAND2; //!< Bad Blocks for NAND2 consume this # of 2K pages.
uint32_t m_u32Number2KPagesBB_NAND3; //!< Bad Blocks for NAND3 consume this # of 2K pages.
} DBBT_Block1;
// This one just forces the spacing.
uint32_t m_Reserved1[10];
};
uint32_t m_u32FingerPrint2; //!< 2nd fingerprint at byte 10.
union {
struct {
uint32_t m_u32NumRowBytes; //!< Number of row bytes in read/write transactions.
uint32_t m_u32NumColumnBytes; //!< Number of row bytes in read/write transactions.
uint32_t m_u32TotalInternalDie; //!< Number of separate chips in this NAND.
uint32_t m_u32InternalPlanesPerDie; //!< Number of internal planes - treat like separate chips.
uint32_t m_u32CellType; //!< MLC or SLC.
uint32_t m_u32ECCType; //!< 4 symbol or 8 symbol ECC?
/**********************************/
uint32_t m_u32EccBlock0Size; //!< Number of bytes for Block0 - BCH
uint32_t m_u32EccBlockNSize; //!< Block size in bytes for all blocks other than Block0 - BCH
uint32_t m_u32EccBlock0EccLevel; //!< Ecc level for Block 0 - BCH
uint32_t m_u32NumEccBlocksPerPage; //!< Number of blocks per page - BCH
uint32_t m_u32MetadataBytes; //!< Metadata size - BCH
uint32_t m_u32EraseThreshold; //!< To set into BCH_MODE register.
/**************** above is NCBv2 */
uint32_t m_u32Read1stCode; //!< First value sent to initiate a NAND Read sequence.
uint32_t m_u32Read2ndCode; //!< Second value sent to initiate a NAND Read sequence.
uint32_t m_u32BootPatch;
uint32_t m_u32PatchSectors;
uint32_t m_u32Firmware_startingNAND2;
} NCB_Block2;
struct {
uint32_t m_u32Firmware_startingNAND; //!< Firmware image starts on this NAND.
uint32_t m_u32Firmware_startingSector; //!< Firmware image starts on this sector.
uint32_t m_u32Firmware_sectorStride; //!< Amount to jump between sectors - unused in ROM.
uint32_t m_uSectorsInFirmware; //!< Number of sectors in firmware image.
uint32_t m_u32Firmware_startingNAND2; //!< Secondary FW Image starting NAND.
uint32_t m_u32Firmware_startingSector2; //!< Secondary FW Image starting Sector.
uint32_t m_u32Firmware_sectorStride2; //!< Secondary FW Image stride - unused in ROM.
uint32_t m_uSectorsInFirmware2; //!< Number of sector in secondary FW image.
struct {
uint16_t m_u16Major;
uint16_t m_u16Minor;
uint16_t m_u16Sub;
uint16_t m_u16Reserved;
} FirmwareVersion;
uint32_t m_u32DiscoveredBBTableSector; //!< Location of Discovered Bad Block Table (DBBT).
uint32_t m_u32DiscoveredBBTableSector2; //!< Location of backup DBBT
} LDLB_Block2;
// This one just forces the spacing.
uint32_t m_Reserved2[20];
};
uint32_t m_u32FingerPrint3; //!< 3rd fingerprint at byte 30.
union {
struct {
unsigned char ncb_unknown[12];
};
};
} NCB_BootBlockStruct_t;
typedef enum _nand_ecc_type {
RS_Ecc_4bit = 0,
RS_Ecc_8bit,
BCH_Ecc_0bit,
BCH_Ecc_2bit,
BCH_Ecc_4bit,
BCH_Ecc_6bit,
BCH_Ecc_8bit,
BCH_Ecc_10bit,
BCH_Ecc_12bit,
BCH_Ecc_14bit,
BCH_Ecc_16bit,
BCH_Ecc_18bit,
BCH_Ecc_20bit
} nand_ecc_type_t;
typedef enum {
ROM_BCH_Ecc_0bit = 0,
ROM_BCH_Ecc_2bit,
ROM_BCH_Ecc_4bit,
ROM_BCH_Ecc_6bit,
ROM_BCH_Ecc_8bit,
ROM_BCH_Ecc_10bit,
ROM_BCH_Ecc_12bit,
ROM_BCH_Ecc_14bit,
ROM_BCH_Ecc_16bit,
ROM_BCH_Ecc_18bit,
ROM_BCH_Ecc_20bit
} rom_ecc_type_t;
//==============================================================================
//! \brief Structure of the Bad Block Entry Table in NAND.
//!
//! This structure defines the Discovered Bad Block Table (DBBT) entries. This
//! block contains a word holding the NAND number then a word describing the number
//! of Bad Blocks on the NAND and an array containing these bad blocks. The ROM
//! will use these entries in the Bad Block table to correctly index to the next
//! sector (skip over bad blocks) while reading from the NAND.
//! Blocks are not guaranteed to be sorted in this table.
typedef struct _BadBlockTableNand_t {
uint32_t uNAND; //!< Which NAND this table is for.
uint32_t uNumberBB; //!< Number of Bad Blocks in this NAND.
// Divide by 4 because of 32 bit words. Subtract 2 because of the 2 above
// 32 bit words.
uint32_t u32BadBlock[(TYPICAL_NAND_READ_SIZE / 4) - 2]; //!< Table of the Bad Blocks.
} BadBlockTableNand_t;
//==============================================================================
//! \brief NAND Timing structure for setting up the GPMI timing.
//!
//! This structure holds the timing for the NAND. This data is used by
//! rom_nand_hal_GpmiSetNandTiming to setup the GPMI hardware registers.
typedef struct {
uint8_t m_u8DataSetup;
uint8_t m_u8DataHold;
uint8_t m_u8AddressSetup;
uint8_t m_u8DSAMPLE_TIME;
/* These are for application use only and not for ROM. */
uint8_t m_u8NandTimingState;
uint8_t m_u8REA;
uint8_t m_u8RLOH;
uint8_t m_u8RHOH;
} FCB_ROM_NAND_Timing_t;
typedef struct {
uint32_t m_u32TMTiming2_ReadLatency;
uint32_t m_u32TMTiming2_PreambleDelay;
uint32_t m_u32TMTiming2_CEDelay;
uint32_t m_u32TMTiming2_PostambleDelay;
uint32_t m_u32TMTiming2_CmdAddPause;
uint32_t m_u32TMTiming2_DataPause;
uint32_t m_u32TMSpeed;
uint32_t m_u32TMTiming1_BusyTimeout;
} FCB_ROM_NAND_TM_Timing_t;
typedef struct {
uint32_t m_u32ONFISpeed;
uint32_t m_u32ONFITiming_ReadLatency;
uint32_t m_u32ONFITiming_CEDelay;
uint32_t m_u32ONFITiming_PreambleDelay;
uint32_t m_u32ONFITiming_PostambleDelay;
uint32_t m_u32ONFITiming_CmdAddPause;
uint32_t m_u32ONFITiming_DataPause;
uint32_t m_u32ONFITiming_BusyTimeout;
} FCB_ROM_NAND_ONFI_Timing_t;
struct fcb_block {
FCB_ROM_NAND_Timing_t m_NANDTiming; //!< Optimum timing parameters for Tas, Tds, Tdh in nsec.
uint32_t m_u32PageDataSize; //!< 2048 for 2K pages, 4096 for 4K pages.
uint32_t m_u32TotalPageSize; //!< 2112 for 2K pages, 4314 for 4K pages.
uint32_t m_u32SectorsPerBlock; //!< Number of 2K sections per block.
uint32_t m_u32NumberOfNANDs; //!< Total Number of NANDs - not used by ROM.
uint32_t m_u32TotalInternalDie; //!< Number of separate chips in this NAND.
uint32_t m_u32CellType; //!< MLC or SLC.
uint32_t m_u32EccBlockNEccType; //!< Type of ECC, can be one of BCH-0-20
uint32_t m_u32EccBlock0Size; //!< Number of bytes for Block0 - BCH
uint32_t m_u32EccBlockNSize; //!< Block size in bytes for all blocks other than Block0 - BCH
uint32_t m_u32EccBlock0EccType; //!< Ecc level for Block 0 - BCH
uint32_t m_u32MetadataBytes; //!< Metadata size - BCH
uint32_t m_u32NumEccBlocksPerPage; //!< Number of blocks per page for ROM use - BCH
uint32_t m_u32EccBlockNEccLevelSDK; //!< Type of ECC, can be one of BCH-0-20
uint32_t m_u32EccBlock0SizeSDK; //!< Number of bytes for Block0 - BCH
uint32_t m_u32EccBlockNSizeSDK; //!< Block size in bytes for all blocks other than Block0 - BCH
uint32_t m_u32EccBlock0EccLevelSDK; //!< Ecc level for Block 0 - BCH
uint32_t m_u32NumEccBlocksPerPageSDK; //!< Number of blocks per page for SDK use - BCH
uint32_t m_u32MetadataBytesSDK; //!< Metadata size - BCH
uint32_t m_u32EraseThreshold; //!< To set into BCH_MODE register.
uint32_t m_u32BootPatch; //!< 0 for normal boot and 1 to load patch starting next to FCB.
uint32_t m_u32PatchSectors; //!< Size of patch in sectors.
uint32_t m_u32Firmware1_startingPage; //!< Firmware image starts on this sector.
uint32_t m_u32Firmware2_startingPage; //!< Secondary FW Image starting Sector.
uint32_t m_u32PagesInFirmware1; //!< Number of sectors in firmware image.
uint32_t m_u32PagesInFirmware2; //!< Number of sector in secondary FW image.
uint32_t m_u32DBBTSearchAreaStartAddress;//!< Page address where dbbt search area begins
uint32_t m_u32BadBlockMarkerByte; //!< Byte in page data that have manufacturer marked bad block marker, this will
//!< bw swapped with metadata[0] to complete page data.
uint32_t m_u32BadBlockMarkerStartBit; //!< For BCH ECC sizes other than 8 and 16 the bad block marker does not start
//!< at 0th bit of m_u32BadBlockMarkerByte. This field is used to get to the
//!< start bit of bad block marker byte with in m_u32BadBlockMarkerByte.
uint32_t m_u32BBMarkerPhysicalOffset; //!< FCB value that gives byte offset for bad block marker on physical NAND page.
uint32_t m_u32BCHType;
FCB_ROM_NAND_TM_Timing_t m_NANDTMTiming;
uint32_t m_u32DISBBM; /* the flag to enable (1)/disable(0) bi swap */
uint32_t m_u32BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
uint32_t m_u32OnfiSyncEnable; //!< Enable the Onfi nand sync mode support
FCB_ROM_NAND_ONFI_Timing_t m_NANDONFITiming;
uint32_t m_u32DISBBSearch; //!< Disable the badblock search when reading the firmware, only using DBBT.
uint32_t m_u32RandomizerEnable; //!< Enable randomizer support
uint32_t reserved[15];
uint32_t m_u32ReadRetryEnable; //!< Enable ready retry support
uint32_t m_u32ReadRetrySeqLength; //!< Read retry sequence length
uint32_t m_u32ReadRetrySeq[MAXSEQLEN]; //!< Read retry sequence length
};
//==============================================================================
//! \brief Structure defining where FCB and DBBT parameters are located.
//!
//! This structure defines the basic fingerprint template for both the Firmware
//! Control Block (FCB) and the Discovered Bad Block Table (DBBT). This
//! template is used to determine if the sector read is a Boot Control Block.
//! This structure defines the Firmware Control Block (FCB). This block
//! contains information describing the timing for the NAND, the number of
//! NANDs in the system, the block size of the NAND, the page size of the NAND,
//! and other criteria for the NAND. This is information that is
//! required just to successfully communicate with the NAND.
//! This block contains information describing the version as well as the layout of
//! the code and data on the NAND Media. For the ROM, we're only concerned
//! with the boot firmware start. Additional information may be stored in
//! the Reserved area. This area will be of interest to the SDK.
//! This structure also defines the Discovered Bad Block Table (DBBT) header.
//! This block contains the information used for parsing the bad block tables
//! which are stored in subsequent 2K sectors. The DBBT header is 8K, followed
//! by the first NANDs entries on a subsequent 2K page (determined by how many
//! 2K pages the first nand requires)
typedef struct {
uint32_t m_u32Checksum; //!< First fingerprint in first byte.
uint32_t m_u32FingerPrint; //!< 2nd fingerprint at byte 4.
uint32_t m_u32Version; //!< 3rd fingerprint at byte 8.
union {
struct fcb_block FCB_Block;
union {
struct {
uint32_t m_u32NumberBB; //!< # Bad Blocks stored in this table for NAND0.
uint32_t m_u32Number2KPagesBB; //!< Bad Blocks for NAND0 consume this # of 2K pages.
} v2;
struct {
uint32_t m_u32res;
uint32_t m_u32DBBTNumOfPages;
} v3;
} DBBT_Block;
};
} BCB_ROM_BootBlockStruct_t;
#endif /*BOOTCONTROLBLOCKS_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2010-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Generic binary BCH encoding/decoding library
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* 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.
*
* Copyright © 2011 Parrot S.A.
*
* Author: Ivan Djelic <ivan.djelic@parrot.com>
*
* Description:
*
* This library provides runtime configurable encoding/decoding of binary
* Bose-Chaudhuri-Hocquenghem (BCH) codes.
*/
#ifndef _BCH_H
#define _BCH_H
//#include <types.h>
/**
* struct bch_control - BCH control structure
* @m: Galois field order
* @n: maximum codeword size in bits (= 2^m-1)
* @t: error correction capability in bits
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t)
* @ecc_bytes: ecc max size (m*t bits) in bytes
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table
* @a_log_tab: Galois field GF(2^m) log lookup table
* @mod8_tab: remainder generator polynomial lookup tables
* @ecc_buf: ecc parity words buffer
* @ecc_buf2: ecc parity words buffer
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots
* @syn: syndrome buffer
* @cache: log-based polynomial representation buffer
* @elp: error locator polynomial
* @poly_2t: temporary polynomials of degree 2t
*/
struct bch_control {
unsigned int m;
unsigned int n;
unsigned int t;
unsigned int ecc_bits;
unsigned int ecc_bytes;
/* private: */
uint16_t *a_pow_tab;
uint16_t *a_log_tab;
uint32_t *mod8_tab;
uint32_t *ecc_buf;
uint32_t *ecc_buf2;
unsigned int *xi_tab;
unsigned int *syn;
int *cache;
struct gf_poly *elp;
struct gf_poly *poly_2t[4];
};
struct bch_control *init_bch(int m, int t, unsigned int prim_poly);
void free_bch(struct bch_control *bch);
void encode_bch(struct bch_control *bch, const uint8_t *data,
unsigned int len, uint8_t *ecc);
int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len,
const uint8_t *recv_ecc, const uint8_t *calc_ecc,
const unsigned int *syn, unsigned int *errloc);
int encode_bch_ecc(void *source_block, size_t source_size,
void *target_block, size_t target_size, int version);
#endif /* _BCH_H */

View File

@ -0,0 +1,696 @@
/*
* Copyright (C) 2017 i2SOM Team.
* Copyright (C) 2011 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*
* i2SOM Team used for i.MX6 platform.
*/
/*
* Bootstream support for Freescale platforms
*/
#include <common.h>
#include <asm/io.h>
#if defined(CONFIG_CMD_BOOTSTREAM) && defined(CONFIG_CMD_NAND)
#include <div64.h>
#include <command.h>
#include <nand.h>
#include <jffs2/load_kernel.h>
#include <linux/mtd/nand.h>
#include <net.h> /* DHCP */
#include "cmd_bootstream.h"
#include "BootControlBlocks.h"
extern uint32_t mx28_nand_mark_byte_offset(void);
extern uint32_t mx28_nand_mark_bit_offset(void);
const struct mtd_config default_mtd_config = {
.chip_count = 1,
.chip_0_offset = 0,
.chip_0_size = 0,
.chip_1_offset = 0,
.chip_1_size = 0,
.search_exponent = 2,
.data_setup_time = 80,
.data_hold_time = 60,
.address_setup_time = 25,
.data_sample_time = 6,
.row_address_size = 3,
.column_address_size = 2,
.read_command_code1 = 0x00,
.read_command_code2 = 0x30,
.boot_stream_major_version = 1,
.boot_stream_minor_version = 0,
.boot_stream_sub_version = 0,
.ncb_version = 3,
.boot_stream_1_address = 0,
.boot_stream_2_address = 0,
.flags = 0,
};
void dump_buffer(unsigned char *buf, int size)
{
int i;
for (i = 0; i < size; i++) {
if ((i % 16) == 0)
printf("\n");
if (i == 0 || (i % 8) == 0)
printf(" ");
printf("%02x ", buf[i]);
}
}
int write_firmware(struct mtd_info *mtd,
struct mtd_config *cfg,
struct mtd_bootblock *bootblock,
unsigned long bs_start_address,
unsigned int boot_stream_size_in_bytes,
unsigned int pre_padding)
{
int startpage, start, size;
int i, r, chunk;
loff_t ofs, end;
int chip = 0;
unsigned long read_addr;
size_t nbytes = 0;
char *readbuf = NULL;
readbuf = malloc(mtd->writesize);
if (NULL == readbuf)
return -1;
if (pre_padding) {
/*
* rewind bs_start_address pre_padding bytes and fill it with
* zeros.
*/
if (bs_start_address - pre_padding < PHYS_SDRAM) {
printf("pre-padding required! "
"Use a $loadaddr of at least 0x%08x\n",
PHYS_SDRAM + pre_padding);
goto _error;
}
bs_start_address -= pre_padding;
memset((u8 *)bs_start_address, 0, pre_padding);
}
//----------------------------------------------------------------------
// Loop over the two boot streams.
//----------------------------------------------------------------------
for (i = 0; i < 2; i++) {
/* Set start address where bootstream is in RAM */
read_addr = bs_start_address;
//--------------------------------------------------------------
// Figure out where to put the current boot stream.
//--------------------------------------------------------------
if (i == 0) {
startpage = bootblock->fcb.FCB_Block.m_u32Firmware1_startingPage;
size = bootblock->fcb.FCB_Block.m_u32PagesInFirmware1;
end = bootblock->fcb.FCB_Block.m_u32Firmware2_startingPage;
} else {
startpage = bootblock->fcb.FCB_Block.m_u32Firmware2_startingPage;
size = bootblock->fcb.FCB_Block.m_u32PagesInFirmware2;
end = lldiv(mtd->size, mtd->writesize);
}
//--------------------------------------------------------------
// Compute the byte addresses corresponding to the page
// addresses.
//--------------------------------------------------------------
start = startpage * mtd->writesize;
size = size * mtd->writesize;
end = end * mtd->writesize;
if (cfg->flags & F_VERBOSE)
printf("mtd: Writting firmware image #%d @%d: 0x%08x - 0x%08x\n", i,
chip, start, start + size);
//--------------------------------------------------------------
// Loop over pages as we write them.
//--------------------------------------------------------------
ofs = start;
while (ofs < end && size > 0) {
//------------------------------------------------------
// Check if the current block is bad.
//------------------------------------------------------
while (nand_block_isbad(mtd, ofs) == 1) {
if (cfg->flags & F_VERBOSE)
fprintf(stdout, "mtd: Skipping bad block at 0x%llx\n", ofs);
ofs += mtd->erasesize;
}
chunk = size;
//------------------------------------------------------
// Check if we've entered a new block and, if so, erase
// it before beginning to write it.
//------------------------------------------------------
if (llmod(ofs, mtd->erasesize) == 0) {
if (cfg->flags & F_VERBOSE) {
fprintf(stdout, "erasing block at 0x%llx\n", ofs);
}
if (!(cfg->flags & F_DRYRUN)) {
r = nand_erase(mtd, ofs, mtd->erasesize);
if (r < 0) {
fprintf(stderr, "mtd: Failed to erase block @0x%llx\n", ofs);
ofs += mtd->erasesize;
continue;
}
}
}
if (chunk > mtd->writesize)
chunk = mtd->writesize;
//------------------------------------------------------
// Write the current chunk to the medium.
//------------------------------------------------------
if (cfg->flags & F_VERBOSE) {
fprintf(stdout, "Writing bootstream file from 0x%lx to offset 0x%llx\n", read_addr, ofs);
}
if (!(cfg->flags & F_DRYRUN)) {
r = nand_write_skip_bad(mtd, ofs, (size_t *)&chunk, &nbytes, mtd->size, (unsigned char *)read_addr, WITH_WR_VERIFY);
if (r || nbytes != chunk) {
fprintf(stderr, "mtd: Failed to write BS @0x%llx (%d)\n", ofs, r);
}
}
//------------------------------------------------------
// Verify the written data
//------------------------------------------------------
r = nand_read_skip_bad(mtd, ofs, (size_t*)&chunk, &nbytes, mtd->size, (unsigned char *)readbuf);
if (r || nbytes != chunk) {
fprintf(stderr, "mtd: Failed to read BS @0x%llx (%d)\n", ofs, r);
goto _error;
}
if (memcmp((void *)read_addr, readbuf, chunk)) {
fprintf(stderr, "mtd: Verification error @0x%llx\n", ofs);
goto _error;
}
ofs += mtd->writesize;
read_addr += mtd->writesize;
size -= chunk;
}
if (cfg->flags & F_VERBOSE)
printf("mtd: Verified firmware image #%d @%d: 0x%08x - 0x%08x\n", i,
chip, start, start + size);
/*
* Write one safe guard page:
* The Image_len of uboot is bigger then the real size of
* uboot by 1K. The ROM will get all 0xff error in this case.
* So we write one more page for safe guard.
*/
//--------------------------------------------------------------
// Check if we ran out of room.
//--------------------------------------------------------------
if (ofs >= end) {
fprintf(stderr, "mtd: Failed to write BS#%d\n", i);
goto _error;
}
}
free(readbuf);
return 0;
_error:
free(readbuf);
return -1;
}
int v1_rom_mtd_init(struct mtd_info *mtd,
struct mtd_config *cfg,
struct mtd_bootblock *bootblock,
unsigned int boot_stream_size_in_bytes,
uint64_t part_size)
{
unsigned int stride_size_in_bytes;
unsigned int search_area_size_in_bytes;
#ifdef CONFIG_USE_NAND_DBBT
unsigned int search_area_size_in_pages;
#endif
unsigned int max_boot_stream_size_in_bytes;
unsigned int boot_stream_size_in_pages;
unsigned int boot_stream1_pos;
unsigned int boot_stream2_pos;
BCB_ROM_BootBlockStruct_t *fcb;
BCB_ROM_BootBlockStruct_t *dbbt;
//----------------------------------------------------------------------
// Compute the geometry of a search area.
//----------------------------------------------------------------------
stride_size_in_bytes = PAGES_PER_STRIDE * mtd->writesize;
search_area_size_in_bytes = (1 << cfg->search_exponent) * stride_size_in_bytes;
#ifdef CONFIG_USE_NAND_DBBT
search_area_size_in_pages = (1 << cfg->search_exponent) * PAGES_PER_STRIDE;
#endif
//----------------------------------------------------------------------
// Check if the target MTD is too small to even contain the necessary
// search areas.
//
// Recall that the boot area for the i.MX28 appears at the beginning of
// the first chip and contains two search areas: one each for the FCB
// and DBBT.
//----------------------------------------------------------------------
if ((search_area_size_in_bytes * 2) > mtd->size) {
fprintf(stderr, "mtd: mtd size too small\n");
return -1;
}
//----------------------------------------------------------------------
// Figure out how large a boot stream the target MTD could possibly
// hold.
//
// The boot area will contain both search areas and two copies of the
// boot stream.
//----------------------------------------------------------------------
max_boot_stream_size_in_bytes =
lldiv(part_size - search_area_size_in_bytes * 2,
//--------------------------------------------//
2);
//----------------------------------------------------------------------
// Figure out how large the boot stream is.
//----------------------------------------------------------------------
boot_stream_size_in_pages =
(boot_stream_size_in_bytes + (mtd->writesize - 1)) /
//---------------------------------------------------//
mtd->writesize;
if (cfg->flags & F_VERBOSE) {
printf("mtd: max_boot_stream_size_in_bytes = %d\n", max_boot_stream_size_in_bytes);
printf("mtd: boot_stream_size_in_bytes = %d\n", boot_stream_size_in_bytes);
}
//----------------------------------------------------------------------
// Check if the boot stream will fit.
//----------------------------------------------------------------------
if (boot_stream_size_in_bytes >= max_boot_stream_size_in_bytes) {
fprintf(stderr, "mtd: bootstream too large\n");
return -1;
}
//----------------------------------------------------------------------
// Compute the positions of the boot stream copies.
//----------------------------------------------------------------------
boot_stream1_pos = 2 * search_area_size_in_bytes;
boot_stream2_pos = boot_stream1_pos + max_boot_stream_size_in_bytes;
if (cfg->flags & F_VERBOSE) {
printf("mtd: #1 0x%08x - 0x%08x (0x%08x)\n",
boot_stream1_pos, boot_stream1_pos + max_boot_stream_size_in_bytes,
boot_stream1_pos + boot_stream_size_in_bytes);
printf("mtd: #2 0x%08x - 0x%08x (0x%08x)\n",
boot_stream2_pos, boot_stream2_pos + max_boot_stream_size_in_bytes,
boot_stream2_pos + boot_stream_size_in_bytes);
}
//----------------------------------------------------------------------
// Fill in the FCB.
//----------------------------------------------------------------------
fcb = &(bootblock->fcb);
memset(fcb, 0, sizeof(*fcb));
fcb->m_u32FingerPrint = FCB_FINGERPRINT;
fcb->m_u32Version = FCB_VERSION_1;
fcb->FCB_Block.m_NANDTiming.m_u8DataSetup = cfg->data_setup_time;
fcb->FCB_Block.m_NANDTiming.m_u8DataHold = cfg->data_hold_time;
fcb->FCB_Block.m_NANDTiming.m_u8AddressSetup = cfg->address_setup_time;
fcb->FCB_Block.m_NANDTiming.m_u8DSAMPLE_TIME = cfg->data_sample_time;
fcb->FCB_Block.m_u32PageDataSize = mtd->writesize;
fcb->FCB_Block.m_u32TotalPageSize = mtd->writesize + mtd->oobsize;
fcb->FCB_Block.m_u32SectorsPerBlock = mtd->erasesize / mtd->writesize;
if (mtd->writesize == 2048) {
fcb->FCB_Block.m_u32NumEccBlocksPerPage = mtd->writesize / 512 - 1;
fcb->FCB_Block.m_u32MetadataBytes = 10;
fcb->FCB_Block.m_u32EccBlock0Size = 512;
fcb->FCB_Block.m_u32EccBlockNSize = 512;
#if CONFIG_MX6UL
fcb->FCB_Block.m_u32EccBlock0EccType = ROM_BCH_Ecc_4bit;
fcb->FCB_Block.m_u32EccBlockNEccType = ROM_BCH_Ecc_4bit;
#endif
} else if (mtd->writesize == 4096) {
fcb->FCB_Block.m_u32NumEccBlocksPerPage = (mtd->writesize / 512) - 1;
fcb->FCB_Block.m_u32MetadataBytes = 10;
fcb->FCB_Block.m_u32EccBlock0Size = 512;
fcb->FCB_Block.m_u32EccBlockNSize = 512;
if (mtd->oobsize == 218) {
fcb->FCB_Block.m_u32EccBlock0EccType = ROM_BCH_Ecc_16bit;
fcb->FCB_Block.m_u32EccBlockNEccType = ROM_BCH_Ecc_16bit;
} else if ((mtd->oobsize == 128)){
fcb->FCB_Block.m_u32EccBlock0EccType = ROM_BCH_Ecc_8bit;
fcb->FCB_Block.m_u32EccBlockNEccType = ROM_BCH_Ecc_8bit;
}
} else {
fprintf(stderr, "Illegal page size %d\n", mtd->writesize);
}
fcb->FCB_Block.m_u32BootPatch = 0; // Normal boot.
fcb->FCB_Block.m_u32Firmware1_startingPage = boot_stream1_pos / mtd->writesize;
fcb->FCB_Block.m_u32Firmware2_startingPage = boot_stream2_pos / mtd->writesize;
fcb->FCB_Block.m_u32PagesInFirmware1 = boot_stream_size_in_pages;
fcb->FCB_Block.m_u32PagesInFirmware2 = boot_stream_size_in_pages;
#ifdef CONFIG_USE_NAND_DBBT
fcb->FCB_Block.m_u32DBBTSearchAreaStartAddress = search_area_size_in_pages;
#else
fcb->FCB_Block.m_u32DBBTSearchAreaStartAddress = 0;
#endif
fcb->FCB_Block.m_u32BadBlockMarkerByte = mx28_nand_mark_byte_offset();
fcb->FCB_Block.m_u32BadBlockMarkerStartBit = mx28_nand_mark_bit_offset();
fcb->FCB_Block.m_u32BBMarkerPhysicalOffset = mtd->writesize;
//----------------------------------------------------------------------
// Fill in the DBBT.
//----------------------------------------------------------------------
dbbt = &(bootblock->dbbt28);
memset(dbbt, 0, sizeof(*dbbt));
dbbt->m_u32FingerPrint = DBBT_FINGERPRINT2;
dbbt->m_u32Version = 1;
dbbt->DBBT_Block.v2.m_u32NumberBB = 0;
dbbt->DBBT_Block.v2.m_u32Number2KPagesBB = 0;
return 0;
}
//------------------------------------------------------------------------------
// This function writes the search areas for a given BCB. It will write *two*
// search areas for a given BCB. If there are multiple chips, it will write one
// search area on each chip. If there is one chip, it will write two search
// areas on the first chip.
//
// md A pointer to the current struct mtd_data.
// bcb_name A pointer to a human-readable string that indicates what kind of
// BCB we're writing. This string will only be used in log messages.
// ofs1 If there is one chips, the index of the
// ofs2
// ofs_mchip If there are multiple chips, the index of the search area to write
// on both chips.
// end The number of consecutive search areas to be written.
// size The size of the BCB data to be written.
// ecc Indicates whether or not to use hardware ECC.
//------------------------------------------------------------------------------
int mtd_commit_bcb(struct mtd_info *mtd,
struct mtd_config *cfg,
struct mtd_bootblock *bootblock,
char *bcb_name,
loff_t ofs1, loff_t ofs2, loff_t ofs_mchip,
loff_t end, size_t size)
{
int chip;
loff_t end_index, search_area_indices[2], o;
int err = 0, r = -1;
int i;
int j;
unsigned stride_size_in_bytes;
unsigned search_area_size_in_strides;
unsigned search_area_size_in_bytes;
size_t nbytes = 0;
unsigned char *readbuf = NULL;
unsigned count;
readbuf = malloc(mtd->writesize);
if (NULL == readbuf)
return -1;
//----------------------------------------------------------------------
// Compute some important facts about geometry.
//----------------------------------------------------------------------
#ifdef MX_USE_SINGLE_PAGE_STRIDE /* mx23, mx28 */
stride_size_in_bytes = mtd->erasesize;
search_area_size_in_strides = 4;
search_area_size_in_bytes = search_area_size_in_strides * stride_size_in_bytes;
count = 2; //write two copy on mx23/28
#else
stride_size_in_bytes = PAGES_PER_STRIDE * mtd->writesize;
search_area_size_in_strides = 1 << cfg->search_exponent;
search_area_size_in_bytes = search_area_size_in_strides * stride_size_in_bytes;
count = 1; //only write one copy
#endif
/* NOTE (hpalacio): For i.MX28 we are not defining MX_USE_SINGLE_PAGE_STRIDE and
* so we are getting into the 'else' above. The calculated figures result the same
* except for the 'count' field. Having the 'count' field to a value of 2 simply
* loops to write twice the four FCB or DBBT copies.
*/
//----------------------------------------------------------------------
// Check whether there are multiple chips and set up the two search area
// indices accordingly.
//----------------------------------------------------------------------
if (cfg->chip_count > 1)
search_area_indices[0] = search_area_indices[1] = ofs_mchip;
else {
search_area_indices[0] = ofs1;
search_area_indices[1] = ofs2;
}
//----------------------------------------------------------------------
// Loop over search areas for this BCB.
//----------------------------------------------------------------------
for (i = 0; !err && i < count; i++) {
//--------------------------------------------------------------
// Compute the search area index that marks the end of the
// writing on this chip.
//--------------------------------------------------------------
end_index = search_area_indices[i] + end;
//--------------------------------------------------------------
// Figure out which chip we're writing.
//--------------------------------------------------------------
chip = (cfg->chip_count > 1) ? i : 0;
//--------------------------------------------------------------
// Loop over consecutive search areas to write.
//--------------------------------------------------------------
for (; search_area_indices[i] < end_index; search_area_indices[i]++) {
//------------------------------------------------------
// Compute the byte offset of the beginning of this
// search area.
//------------------------------------------------------
o = search_area_indices[i] * search_area_size_in_bytes;
//------------------------------------------------------
// Loop over strides in this search area.
//------------------------------------------------------
for (j = 0; j < search_area_size_in_strides; j++, o += stride_size_in_bytes) {
//----------------------------------------------
// If we're crossing into a new block, erase it
// first.
//----------------------------------------------
if (llmod(o, mtd->erasesize) == 0) {
if (cfg->flags & F_VERBOSE) {
fprintf(stdout, "erasing block at 0x%llx\n", o);
}
if (!(cfg->flags & F_DRYRUN)) {
r = nand_erase(mtd, o, mtd->erasesize);
if (r < 0) {
fprintf(stderr, "mtd: Failed to erase block @0x%llx\n", o);
err++;
continue;
}
}
}
//----------------------------------------------
// Write & verify the page.
//----------------------------------------------
if (cfg->flags & F_VERBOSE)
fprintf(stdout, "mtd: Writing %s%d @%d:0x%llx(%x)\n",
bcb_name, j, chip, o, size);
if (!(cfg->flags & F_DRYRUN)) {
if (size == mtd->writesize + mtd->oobsize) {
/* We're going to write a raw page (data+oob) */
mtd_oob_ops_t ops = {
.datbuf = bootblock->buf,
.len = mtd->writesize,
.mode = MTD_OPS_RAW,
};
r = mtd_write_oob(mtd, o, &ops);
if (r) {
fprintf(stderr, "mtd: Failed to write %s @%d: 0x%llx (%d)\n",
bcb_name, chip, o, r);
err ++;
}
//------------------------------------------------------
// Verify the written data
//------------------------------------------------------
ops.datbuf = (u8 *)readbuf;
r = mtd_read_oob(mtd, o, &ops);
if (r) {
fprintf(stderr, "mtd: Failed to read @0x%llx (%d)\n", o, r);
err++;
goto _error;
}
if (memcmp(bootblock->buf, readbuf, mtd->writesize)) {
fprintf(stderr, "mtd: Verification error @0x%llx\n", o);
err++;
goto _error;
}
if (cfg->flags & F_VERBOSE)
fprintf(stdout, "mtd: Verified %s%d @%d:0x%llx(%x)\n",
bcb_name, j, chip, o, size);
}
else {
r = nand_write_skip_bad(mtd, o, &size, &nbytes, mtd->size, bootblock->buf, WITH_WR_VERIFY);
if (r || nbytes != size) {
fprintf(stderr, "mtd: Failed to write %s @%d: 0x%llx (%d)\n",
bcb_name, chip, o, r);
err ++;
}
//------------------------------------------------------
// Verify the written data
//------------------------------------------------------
r = nand_read_skip_bad(mtd, o, &size, &nbytes, mtd->size, readbuf);
if (r || nbytes != size) {
fprintf(stderr, "mtd: Failed to read @0x%llx (%d)\n", o, r);
err++;
goto _error;
}
if (memcmp(bootblock->buf, readbuf, size)) {
fprintf(stderr, "mtd: Verification error @0x%llx\n", o);
err++;
goto _error;
}
if (cfg->flags & F_VERBOSE)
fprintf(stdout, "mtd: Verified %s%d @%d:0x%llx(%x)\n",
bcb_name, j, chip, o, size);
}
}
}
}
}
if (cfg->flags & F_VERBOSE)
printf("%s(%s): status %d\n", __func__, bcb_name, err);
_error:
free(readbuf);
return err;
}
int v6_rom_mtd_commit_structures(struct mtd_info *mtd,
struct mtd_config *cfg,
struct mtd_bootblock *bootblock,
unsigned long bs_start_address,
unsigned int boot_stream_size_in_bytes)
{
int size, r;
/* [1] Write the FCB search area. */
size = mtd->writesize + mtd->oobsize;
r = fcb_encrypt(&bootblock->fcb, bootblock->buf, size, 3);
if (r < 0)
return r;
mtd_commit_bcb(mtd, cfg, bootblock, "FCB", 0, 0, 0, 1, size);
/* [2] Write the DBBT search area. */
memset(bootblock->buf, 0, size);
memcpy(bootblock->buf, &(bootblock->dbbt28), sizeof(bootblock->dbbt28));
mtd_commit_bcb(mtd, cfg, bootblock, "DBBT", 1, 1, 1, 1, mtd->writesize);
/* [3] Write the two boot streams using a 1K padding. */
return write_firmware(mtd, cfg, bootblock, bs_start_address,
boot_stream_size_in_bytes, 1024);
}
int write_bootstream(struct part_info *part,
unsigned long bs_start_address,
int bs_size)
{
/* TODO: considering chip = 0 */
int chip = 0;
struct mtd_info *mtd = &nand_info[chip];
int r = -1;
struct mtd_config cfg;
struct mtd_bootblock bootblock;
/* copy defaults */
memcpy(&cfg, &default_mtd_config, sizeof(cfg));
/* flags (TODO: parse command arguments?) */
//cfg.flags |= F_VERBOSE;
//cfg.flags |= F_DRYRUN;
/* alloc buffer */
bootblock.buf = malloc(mtd->writesize + mtd->oobsize);
if (NULL == bootblock.buf) {
fprintf(stderr, "mtd: unable to allocate page buffer\n");
return -1;
}
printf("Writing bootstream...");
#if defined(CONFIG_MX6ULL) || defined(CONFIG_MX6UL)
r = v1_rom_mtd_init(mtd, &cfg, &bootblock, bs_size, part->size);
#endif
if (r < 0) {
printf("mtd_init failed!\n");
}
else {
#if defined(CONFIG_MX6ULL) || defined(CONFIG_MX6UL)
r = v6_rom_mtd_commit_structures(mtd, &cfg, &bootblock, bs_start_address, bs_size);
#endif
}
/* free buffer */
free(bootblock.buf);
if (r)
printf("FAILED\n");
else
printf("OK\n");
return r;
}
#endif /* CONFIG_CMD_BOOTSTREAM && CONFIG_CMD_NAND */

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2011 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*/
/*
* Bootstream support for Freescale platforms
*/
#ifndef __DIGI_CMD_BOOTSTREAM_H
#define __DIGI_CMD_BOOTSTREAM_H
#include <jffs2/load_kernel.h>
#include <mtd/mtd-abi.h>
#include "BootControlBlocks.h"
/* flags */
#define F_VERBOSE (1 << 0)
#define F_DRYRUN (1 << 1)
struct mtd_config {
int chip_count;
int chip_0_offset;
int chip_0_size;
int chip_1_offset;
int chip_1_size;
int search_exponent;
int data_setup_time;
int data_hold_time;
int address_setup_time;
int data_sample_time;
int row_address_size;
int column_address_size;
int read_command_code1;
int read_command_code2;
int boot_stream_major_version;
int boot_stream_minor_version;
int boot_stream_sub_version;
int ncb_version;
int boot_stream_1_address;
int boot_stream_2_address;
int flags;
};
//------------------------------------------------------------------------------
// This structure represents an MTD device in which we will write boot
// information.
//------------------------------------------------------------------------------
struct mtd_part {
// A bit set where each bit corresponds to a block in a given MTD.
uint32_t *bad_blocks;
// The number of bad blocks appearing in this MTD.
int nrbad;
int oobinfochanged;
struct nand_oobinfo old_oobinfo;
int ecc;
};
/* partially implements mtd_data in kobs-ng */
struct mtd_bootblock {
struct mtd_part part[2];
/* writesize + oobsize buffer */
void *buf;
/* NCBs */
NCB_BootBlockStruct_t *curr_ncb;
NCB_BootBlockStruct_t ncb[2];
loff_t ncb_ofs[2];
int ncb_version; /* 0, 1, or 3. Negative means error */
/* LDLBs */
NCB_BootBlockStruct_t *curr_ldlb;
NCB_BootBlockStruct_t ldlb[2];
loff_t ldlb_ofs[2];
/* DBBTs */
NCB_BootBlockStruct_t *curr_dbbt;
NCB_BootBlockStruct_t dbbt[2];
loff_t dbbt_ofs[2];
/* the 2 NANDs */
BadBlockTableNand_t *bbtn[2];
/* In fact, we can reuse the boot block
* struct for mx53 on mx28, it's compatible
*/
/* FCB */
BCB_ROM_BootBlockStruct_t fcb;
/* DBBT */
BCB_ROM_BootBlockStruct_t dbbt28;
};
#define PAGES_PER_STRIDE 64
/* Functions */
int ncb_get_version(void *ncb_candidate, NCB_BootBlockStruct_t **result);
int fcb_encrypt(BCB_ROM_BootBlockStruct_t *fcb, void *target, size_t size, int version);
int write_bootstream(struct part_info *part,
unsigned long bs_start_address,
int bs_size);
#endif /* __DIGI_CMD_BOOTSTREAM_H */

View File

@ -0,0 +1,227 @@
/*
* Freescale i.MX28 image generator
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
/*
* Default BCB layout.
*
* TWEAK this if you have blown any OCOTP fuses.
*/
#define STRIDE_PAGES 64
#define STRIDE_COUNT 4
/*
* Layout for 256Mb big NAND with 2048b page size, 64b OOB size and
* 128kb erase size.
*
* TWEAK this if you have different kind of NAND chip.
*/
uint32_t nand_writesize = 2048;
uint32_t nand_oobsize = 64;
uint32_t nand_erasesize = 128 * 1024;
/*
* Sector on which the SigmaTel boot partition (0x53) starts.
*/
uint32_t sd_sector = 2048;
/*
* Each of the U-Boot bootstreams is at maximum 1MB big.
*
* TWEAK this if, for some wild reason, you need to boot bigger image.
*/
#define MAX_BOOTSTREAM_SIZE (1 * 1024 * 1024)
/* i.MX28 NAND controller-specific constants. DO NOT TWEAK! */
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
#define MXS_NAND_METADATA_SIZE 10
#define MXS_NAND_COMMAND_BUFFER_SIZE 32
struct mx28_nand_fcb {
uint32_t checksum;
uint32_t fingerprint;
uint32_t version;
struct {
uint8_t data_setup;
uint8_t data_hold;
uint8_t address_setup;
uint8_t dsample_time;
uint8_t nand_timing_state;
uint8_t rea;
uint8_t rloh;
uint8_t rhoh;
} timing;
uint32_t page_data_size;
uint32_t total_page_size;
uint32_t sectors_per_block;
uint32_t number_of_nands; /* Ignored */
uint32_t total_internal_die; /* Ignored */
uint32_t cell_type; /* Ignored */
uint32_t ecc_block_n_ecc_type;
uint32_t ecc_block_0_size;
uint32_t ecc_block_n_size;
uint32_t ecc_block_0_ecc_type;
uint32_t metadata_bytes;
uint32_t num_ecc_blocks_per_page;
uint32_t ecc_block_n_ecc_level_sdk; /* Ignored */
uint32_t ecc_block_0_size_sdk; /* Ignored */
uint32_t ecc_block_n_size_sdk; /* Ignored */
uint32_t ecc_block_0_ecc_level_sdk; /* Ignored */
uint32_t num_ecc_blocks_per_page_sdk; /* Ignored */
uint32_t metadata_bytes_sdk; /* Ignored */
uint32_t erase_threshold;
uint32_t boot_patch;
uint32_t patch_sectors;
uint32_t firmware1_starting_sector;
uint32_t firmware2_starting_sector;
uint32_t sectors_in_firmware1;
uint32_t sectors_in_firmware2;
uint32_t dbbt_search_area_start_address;
uint32_t badblock_marker_byte;
uint32_t badblock_marker_start_bit;
uint32_t bb_marker_physical_offset;
};
struct mx28_nand_dbbt {
uint32_t checksum;
uint32_t fingerprint;
uint32_t version;
uint32_t number_bb;
uint32_t number_2k_pages_bb;
};
struct mx28_nand_bbt {
uint32_t nand;
uint32_t number_bb;
uint32_t badblock[510];
};
struct mx28_sd_drive_info {
uint32_t chip_num;
uint32_t drive_type;
uint32_t tag;
uint32_t first_sector_number;
uint32_t sector_count;
};
struct mx28_sd_config_block {
uint32_t signature;
uint32_t primary_boot_tag;
uint32_t secondary_boot_tag;
uint32_t num_copies;
struct mx28_sd_drive_info drv_info[1];
};
static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength)
{
return ecc_strength * 13;
}
static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size)
{
if (page_data_size == 2048) {
#if defined(CONFIG_MX28)
return 8;
#elif defined(CONFIG_MX6UL)
return 4;
#endif
}
if (page_data_size == 4096) {
if (page_oob_size == 128)
return 8;
if (page_oob_size == 218)
return 16;
}
return 0;
}
static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size,
uint32_t ecc_strength)
{
uint32_t chunk_data_size_in_bits;
uint32_t chunk_ecc_size_in_bits;
uint32_t chunk_total_size_in_bits;
uint32_t block_mark_chunk_number;
uint32_t block_mark_chunk_bit_offset;
uint32_t block_mark_bit_offset;
chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8;
chunk_ecc_size_in_bits = mx28_nand_ecc_size_in_bits(ecc_strength);
chunk_total_size_in_bits =
chunk_data_size_in_bits + chunk_ecc_size_in_bits;
/* Compute the bit offset of the block mark within the physical page. */
block_mark_bit_offset = page_data_size * 8;
/* Subtract the metadata bits. */
block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8;
/*
* Compute the chunk number (starting at zero) in which the block mark
* appears.
*/
block_mark_chunk_number =
block_mark_bit_offset / chunk_total_size_in_bits;
/*
* Compute the bit offset of the block mark within its chunk, and
* validate it.
*/
block_mark_chunk_bit_offset = block_mark_bit_offset -
(block_mark_chunk_number * chunk_total_size_in_bits);
if (block_mark_chunk_bit_offset > chunk_data_size_in_bits)
return 1;
/*
* Now that we know the chunk number in which the block mark appears,
* we can subtract all the ECC bits that appear before it.
*/
block_mark_bit_offset -=
block_mark_chunk_number * chunk_ecc_size_in_bits;
return block_mark_bit_offset;
}
uint32_t mx28_nand_mark_byte_offset(void)
{
uint32_t ecc_strength;
ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize);
return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) >> 3;
}
uint32_t mx28_nand_mark_bit_offset(void)
{
uint32_t ecc_strength;
ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize);
return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) & 0x7;
}

View File

@ -0,0 +1,497 @@
/*
* ncb.c - verify and encode NCB
*
* Copyright (c) 2008 by Embedded Alley Solution 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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define _GNU_SOURCE
#include <common.h>
#include <malloc.h>
#include <linux/string.h>
#include <asm/errno.h>
#include "cmd_bootstream.h"
//#include "config.h"
#include "rom_nand_hamming_code_ecc.h"
#include "BootControlBlocks.h"
#include "bch.h"
static inline int even_number_of_1s(uint8_t byte)
{
int even = 1;
while (byte > 0) {
even ^= (byte & 0x1);
byte >>= 1;
}
return even;
}
#define BIT(v,n) (((v) >> (n)) & 0x1)
#define B(n) (BIT(d,n))
#define BSEQ(a1,a2,a3,a4,a5,a6,a7,a8) \
(B(a1) ^ B(a2) ^ B(a3) ^ B(a4) ^ B(a5) ^ B(a6) ^ B(a7) ^ B(a8))
static uint8_t calculate_parity_22_16(uint16_t d)
{
uint8_t p = 0;
if (d == 0 || d == 0xFFFF)
return 0; /* optimization :) */
p |= BSEQ(15, 12, 11, 8, 5, 4, 3, 2) << 0;
p |= BSEQ(13, 12, 11, 10, 9, 7, 3, 1) << 1;
p |= BSEQ(15, 14, 13, 11, 10, 9, 6, 5) << 2;
p |= BSEQ(15, 14, 13, 8, 7, 6, 4, 0) << 3;
p |= BSEQ(12, 9, 8, 7, 6, 2, 1, 0) << 4;
p |= BSEQ(14, 10, 5, 4, 3, 2, 1, 0) << 5;
return p;
}
static uint8_t calculate_parity_13_8(uint8_t d)
{
uint8_t p = 0;
p |= (B(6) ^ B(5) ^ B(3) ^ B(2)) << 0;
p |= (B(7) ^ B(5) ^ B(4) ^ B(2) ^ B(1)) << 1;
p |= (B(7) ^ B(6) ^ B(5) ^ B(1) ^ B(0)) << 2;
p |= (B(7) ^ B(4) ^ B(3) ^ B(0)) << 3;
p |= (B(6) ^ B(4) ^ B(3) ^ B(2) ^ B(1) ^ B(0)) << 4;
return p;
}
#undef BIT
#undef B
#undef BSEQ
static int encode_hamming_code_22_16(void *source_block, size_t source_size,
void *target_block, size_t target_size)
{
int i, j, bit_index;
uint16_t *src;
uint8_t *dst;
uint8_t np;
uint8_t ecc[NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES];
uint8_t data[NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES];
memset(data, 0, ARRAY_SIZE(data));
memcpy(data, source_block, source_size);
src = (uint16_t *) data;
dst = (uint8_t *) target_block;
/* create THREE copies of source block */
for (i = 0; i < NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES; i++) {
dst[i + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY] =
dst[i + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY] =
dst[i + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY] =
((uint8_t *) src)[i];
}
/* finally, point to the end of populated data */
for (bit_index = j = i = 0;
j < NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES / sizeof(uint16_t);
j++) {
np = calculate_parity_22_16(src[j]);
switch (bit_index) {
case 0:
ecc[i] = np & 0x3F;
break;
case 2:
ecc[i++] |= (np & 0x03) << 6;
ecc[i] = (np & 0x3C) >> 2;
break;
case 4:
ecc[i++] |= (np & 0x0F) << 4;
ecc[i] = (np & 0x30) >> 4;
break;
case 6:
ecc[i++] |= (np & 0x3F) << 2;
break;
}
bit_index = (bit_index + 2) % 8;
}
for (i = 0; i < NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES; i++) {
dst[i + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY] =
dst[i + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY] =
dst[i + NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY] = ecc[i];
}
return 0;
}
static int encode_hamming_code_13_8(void *source_block, size_t source_size,
void *target_block, size_t target_size)
{
uint8_t ecc[NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES];
uint8_t data[NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES];
int i;
memset(ecc, 0, ARRAY_SIZE(ecc));
memset(data, 0, ARRAY_SIZE(data));
memcpy(data, source_block, source_size);
for (i = 0; i < source_size; i ++)
ecc[i] = calculate_parity_13_8(data[i]);
memcpy((uint8_t*)target_block + BCB_MAGIC_OFFSET, data, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
memcpy((uint8_t*)target_block + BCB_MAGIC_OFFSET + NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
ecc, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
return 0;
}
static int ncb_single_pair_check(uint8_t *n1, uint8_t *p1, uint8_t *n2, uint8_t *p2)
{
return (memcmp(n1, n2, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES) == 0 &&
memcmp(p1, p2, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES) == 0);
}
static int ncb_triple_check(void *page)
{
uint8_t *n1 = (uint8_t*)page + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY,
*n2 = (uint8_t*)page + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY,
*n3 = (uint8_t*)page + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY,
*p1 = (uint8_t*)page + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY,
*p2 = (uint8_t*)page + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY,
*p3 = (uint8_t*)page + NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY;
if (ncb_single_pair_check(n1, p1, n2, p2))
return 1;
if (ncb_single_pair_check(n2, p2, n3, p3))
return 2;
if (ncb_single_pair_check(n1, p1, n3, p3))
return 1;
return -1;
}
static int lookup_single_error_22_16(uint8_t syndrome)
{
int i;
uint8_t syndrome_table[] = {
0x38, 0x32, 0x31, 0x23, 0x29, 0x25, 0x1C, 0x1A,
0x19, 0x16, 0x26, 0x07, 0x13, 0x0E, 0x2C, 0x0D,
0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
};
for (i = 0; i < ARRAY_SIZE(syndrome_table); i ++)
if (syndrome_table[i] == syndrome)
return i;
return -ENOENT;
}
static int lookup_single_error_13_8(uint8_t syndrome)
{
int i;
uint8_t syndrome_table[] = {
0x1C, 0x16, 0x13, 0x19,
0x1A, 0x07, 0x15, 0x0E,
0x01, 0x02, 0x04, 0x08,
0x10,
};
for (i = 0; i < ARRAY_SIZE(syndrome_table); i ++)
if (syndrome_table[i] == syndrome)
return i;
return -ENOENT;
}
static inline NCB_BootBlockStruct_t *ncb_verify_hamming_22_16(void *page)
{
int r;
uint16_t* n1 = (uint16_t*)((uint8_t*)page + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY),
*n2 = ((uint16_t*)(uint8_t*)page + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY),
*n, *data;
uint8_t *p1 = (uint8_t*)page + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY,
*p2 = (uint8_t*)page + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY,
*parity, p, np, syndrome;
int bit_index, i, j, bit_to_flip;
r = ncb_triple_check(page);
if (r < 0)
return NULL;
if (r == 1) {
data = n = n1;
parity = p1;
}
else if (r == 2) {
data = n = n2;
parity = p2;
}
else {
fprintf(stderr, "internal error: %s, r = %d\n", __func__, r);
return NULL;
}
for (bit_index = i = j = 0, r = 0;
i < NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES / sizeof(uint16_t) && r == 0;
i ++, data++) {
switch (bit_index) {
case 0:
p = parity[j] & 0x3F;
break;
case 2:
p = (parity[j++] & 0xC0) >> 6;
p |= (parity[j] & 0x0F) << 2;
break;
case 4:
p = (parity[j++] & 0xF0) >> 4;
p |= (parity[j] & 0x03) << 4;
break;
case 6:
p = (parity[j++] & 0xFC) >> 2;
break;
default:
fprintf(stderr, "internal error at %s:%d\n", __func__, __LINE__);
return NULL;
}
bit_index = (bit_index + 2) % 8;
np = calculate_parity_22_16(*data);
syndrome = np ^ p;
if (syndrome == 0) /* cool */ {
continue;
}
if (even_number_of_1s(syndrome)) {
r = i;
break;
}
bit_to_flip = lookup_single_error_22_16(syndrome);
if (bit_to_flip < 0) {
r = i;
break;
}
if (bit_to_flip < 16)
*data ^= (1 << bit_to_flip);
}
return r == 0 ? (NCB_BootBlockStruct_t*)n : NULL;
}
static inline NCB_BootBlockStruct_t *ncb_verify_hamming_13_8(void *ncb_page)
{
int i, bit_to_flip;
uint8_t *data, *parity, np, syndrome;
data = (uint8_t*)ncb_page + 12;
parity = (uint8_t*)ncb_page + 12 + NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES;
for (i = 0; i < NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES; i ++, data++) {
np = calculate_parity_13_8(*data);
syndrome = np ^ parity[i];
if (syndrome == 0)
continue;
if (even_number_of_1s(syndrome))
return NULL;
bit_to_flip = lookup_single_error_13_8(syndrome);
if (bit_to_flip < 0)
return NULL;
if (bit_to_flip < 8)
*data ^= (1 << bit_to_flip);
}
return (NCB_BootBlockStruct_t*)(ncb_page + 12);
}
/**
* ncb_encrypt - Encrypt the NCB block, assuming that target system uses NCB
* version 'version'
*
* ncb: Points to valid NCB_BootBlockStruct_t structure
* target: Points to a buffer large enough to contain an entire NAND Flash page
* (both data and OOB).
* size: The size of an entire NAND Flash page (both data and OOB).
* version: The version number of the NCB.
*/
int ncb_encrypt(NCB_BootBlockStruct_t *ncb, void *target, size_t size, int version)
{
assert(version == 0 || version == 1 || version == 3);
assert(size >= sizeof(NCB_BootBlockStruct_t));
memset(target, 0, size);
switch (version)
{
case 0:
memcpy(target, ncb, sizeof(*ncb));
return size;
case 1:
return encode_hamming_code_22_16(ncb, sizeof(*ncb), target, size);
case 3:
return encode_hamming_code_13_8(ncb, sizeof(*ncb), target, size);
default:
fprintf(stderr, "NCB version == %d? Something is wrong!\n", version);
return -EINVAL;
}
}
/**
* fcb_encrypt - Encrypt the FCB block, assuming that target system uses NCB
* version 'version'
*
* fcb: Points to valid imx28_BootBlockStruct_t structure.
* target: Points to a buffer large enough to contain an entire NAND Flash page
* (both data and OOB).
* size: The size of an entire NAND Flash page (both data and OOB).
* version: The version number of the NCB.
*
*/
int fcb_encrypt(BCB_ROM_BootBlockStruct_t *fcb, void *target, size_t size, int version)
{
uint32_t accumulator;
uint8_t *p;
uint8_t *q;
//----------------------------------------------------------------------
// Check for nonsense.
//----------------------------------------------------------------------
assert(version == 1);
assert(size >= sizeof(BCB_ROM_BootBlockStruct_t));
//----------------------------------------------------------------------
// Clear out the target.
//----------------------------------------------------------------------
memset(target, 0, size);
//----------------------------------------------------------------------
// Compute the checksum.
//
// Note that we're computing the checksum only over the FCB itself,
// whereas it's actually supposed to reflect the entire 508 bytes
// in the FCB page between the base of the of FCB and the base of the
// ECC bytes. However, the entire space between the top of the FCB and
// the base of the ECC bytes will be all zeros, so this is OK.
//----------------------------------------------------------------------
p = ((uint8_t *) fcb) + 4;
q = (uint8_t *) (fcb + 1);
accumulator = 0;
for (; p < q; p++) {
accumulator += *p;
}
accumulator ^= 0xffffffff;
fcb->m_u32Checksum = accumulator;
//----------------------------------------------------------------------
// Compute the ECC bytes.
//----------------------------------------------------------------------
switch (version)
{
case 0:
memcpy(target, fcb, sizeof(*fcb));
return size;
case 1:
return encode_hamming_code_13_8(fcb, sizeof(*fcb), target, size);
case 2:
return encode_bch_ecc(fcb, sizeof(*fcb), target, size, version);
case 3:
return encode_bch_ecc(fcb, sizeof(*fcb), target, size, version);
default:
fprintf(stderr, "FCB version == %d? Something is wrong!\n", version);
return -EINVAL;
}
}
/**
* ncb_get_version - parse the boot block ncb_candidate and on success store
* the pointer to NCB to result
*
* returns version of found NCB, or -1 otherwise - try further scanning..
*/
int ncb_get_version(void *ncb_candidate, NCB_BootBlockStruct_t **result)
{
NCB_BootBlockStruct_t *bbs, *p1, *p2, *p3;
assert(result != NULL);
printf("%s(*result=%p)\n", __FUNCTION__, *result);
*result = NULL;
/* first of all, check version 3 of ncb */
bbs = (NCB_BootBlockStruct_t*)((uint8_t*)ncb_candidate + 12);
if (bbs->m_u32FingerPrint1 == NCB_FINGERPRINT1 &&
bbs->m_u32FingerPrint2 == NCB_FINGERPRINT2 &&
bbs->m_u32FingerPrint3 == NCB_FINGERPRINT3) {
/* fingerprints match, so it is either v3 or corrupted NCB */
*result = ncb_verify_hamming_13_8(ncb_candidate);
printf("%d: *result=%p\n", __LINE__, *result);
if (*result)
return 3;
fprintf(stderr, "ncb_verify_hamming_13_8 failed!\n");
return -1;
}
/*
* then, check if it is version 1 (yes, there was not NCBv2
*
* To match the v1, there should be at least two identical copies
* of NCB
*/
p1 = (NCB_BootBlockStruct_t*)ncb_candidate /* + NAND_HC_OFFSET_FIRST_DATA_COPY */;
p2 = (NCB_BootBlockStruct_t*)((uint8_t*)ncb_candidate + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY);
p3 = (NCB_BootBlockStruct_t*)((uint8_t*)ncb_candidate + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY);
if (memcmp(p1, p2, sizeof(NCB_BootBlockStruct_t)) == 0 ||
memcmp(p1, p3, sizeof(NCB_BootBlockStruct_t)) == 0 ||
memcmp(p2, p3, sizeof(NCB_BootBlockStruct_t)) == 0) {
/*
* we found at least two identical copies of NCB; verify
* against the ECC
*/
*result = ncb_verify_hamming_22_16(ncb_candidate);
printf("%d: *result=%p\n", __LINE__, *result);
if (*result)
return 1;
return -1;
}
/*
* and finally, does it look like NCBv0 ?
*/
if (p1->m_u32FingerPrint1 == NCB_FINGERPRINT1 &&
p1->m_u32FingerPrint2 == NCB_FINGERPRINT2 &&
p1->m_u32FingerPrint3 == NCB_FINGERPRINT3) {
*result = p1;
printf("%d: *result=%p\n", __LINE__, *result);
return 0;
}
/*
* we did try.
*/
return -1;
}

View File

@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////////////////////////
//! \addtogroup rom_nand_boot
//! @{
//
// Copyright (c) 2006 SigmaTel, Inc.
//
//! \file rom_nand_hamming_code_ecc.h
//! \brief This file provides header info for hamming code ecc.
//!
////////////////////////////////////////////////////////////////////////////////
#ifndef _ROM_NAND_HAMMING_CODE_ECC_H
#define _ROM_NAND_HAMMING_CODE_ECC_H
////////////////////////////////////////////////////////////////////////////////
// Definitions
////////////////////////////////////////////////////////////////////////////////
//!< Bytes per NCB data block
#define NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES (512)
//! Size of a parity block in bytes for all 16-bit data blocks present inside one 512 byte NCB block.
#define NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES ((((512*8)/16)*6)/8)
//! Offset to first copy of NCB in a NAND page
#define NAND_HC_ECC_OFFSET_FIRST_DATA_COPY (0)
//! Offset to second copy of NCB in a NAND page
#define NAND_HC_ECC_OFFSET_SECOND_DATA_COPY (NAND_HC_ECC_OFFSET_FIRST_DATA_COPY+NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
//! Offset to third copy of NCB in a NAND page
#define NAND_HC_ECC_OFFSET_THIRD_DATA_COPY (NAND_HC_ECC_OFFSET_SECOND_DATA_COPY+NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
//! Offset to first copy of Parity block in a NAND page
#define NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY (NAND_HC_ECC_OFFSET_THIRD_DATA_COPY+NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
//! Offset to second copy of Parity block in a NAND page
#define NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY (NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY+NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
//! Offset to third copy of Parity block in a NAND page
#define NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY (NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY+NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
#define BITMASK_HAMMINGCHECKED_ALL_THREE_COPIES 0x7 //!< to indicate all three copies of NCB in first page are processed with Hamming codes.
#define BITMASK_HAMMINGCHECKED_FIRST_COPY 0x1 //!< to indicate first copy of NCB is processed with Hamming codes.
#define BITMASK_HAMMINGCHECKED_SECOND_COPY 0x2 //!< to indicate second copy of NCB is processed with Hamming codes.
#define BITMASK_HAMMINGCHECKED_THIRD_COPY 0x4 //!< to indicate third copy of NCB is processed with Hamming codes.
int TripleRedundancyCheck(uint8_t * pNCBCopy1, uint8_t * pNCBCopy2,
uint8_t * pNCBCopy3, uint8_t * pP1, uint8_t * pP2,
uint8_t * pP3, uint8_t * pu8HammingCopy);
int IsNumOf1sEven(uint8_t u8);
void CalculateParity(uint16_t d, uint8_t * p);
int TableLookupSingleErrors(uint8_t u8Synd, uint8_t * pu8BitToFlip);
int HammingCheck(uint8_t * pNCB, uint8_t * pParityBlock);
#endif /* */
////////////////////////////////////////////////////////////////////////////////
// End of file
////////////////////////////////////////////////////////////////////////////////
// @}

View File

@ -0,0 +1,368 @@
/*
* Copyright (C) 2017 i2SOM Team.
* Copyright (C) 2016 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*/
#include <asm/imx-common/boot_mode.h>
#include <common.h>
#include <jffs2/load_kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <nand.h>
#include <otf_update.h>
#include <ubi_uboot.h>
#include "helper.h"
#ifdef CONFIG_CMD_BOOTSTREAM
#include "cmd_bootstream/cmd_bootstream.h"
#endif
DECLARE_GLOBAL_DATA_PTR;
enum {
ERR_WRITE = 1,
ERR_READ,
ERR_VERIFY,
};
static int write_firmware(unsigned long loadaddr, unsigned long filesize,
struct part_info *part, char *ubivolname)
{
char cmd[CONFIG_SYS_CBSIZE] = "";
unsigned long verifyaddr, u, m;
if (filesize > part->size) {
printf("File size (%lu bytes) exceeds partition size (%lu bytes)!\n",
filesize, (unsigned long)part->size);
return -1;
}
if (ubivolname) {
/* A UBI volume exists in the partition, use 'ubi write' */
sprintf(cmd, "ubi write %lx %s %lx", loadaddr, ubivolname,
filesize);
} else {
/* raw-write firmware command (erase first) */
sprintf(cmd, "if nand erase.part %s; then "
"nand write %lx %s %lx;fi", part->name,
loadaddr, part->name, filesize);
}
if (run_command(cmd, 0))
return ERR_WRITE;
#ifdef CONFIG_I2SOM_UBI
/* If it is a UBIFS file system, verify it using a special function */
if (ubivolname) {
printf("Verifying firmware...\n");
if (ubi_volume_verify(part->name, (char *)loadaddr, 0,
filesize, 0))
return ERR_VERIFY;
printf("Update was successful\n");
return 0;
}
#endif
/*
* If there is enough RAM to hold two copies of the firmware,
* verify written firmware.
* +--------|---------------------|------------------|--------------+
* | L V | U-Boot+Stack |
* +--------|---------------------|------------------|--------------+
* P U M
*
* P = PHYS_SDRAM (base address of SDRAM)
* L = $loadaddr
* V = $verifyaddr
* M = last address of SDRAM (CONFIG_DDR_MB (size of SDRAM) + P)
* U = SDRAM address where U-Boot is located (plus margin)
*/
verifyaddr = getenv_ulong("verifyaddr", 16, 0);
m = PHYS_SDRAM + gd->ram_size;
u = m - CONFIG_UBOOT_RESERVED;
/*
* ($loadaddr + firmware size) must not exceed $verifyaddr
* ($verifyaddr + firmware size) must not exceed U.
*/
if ((loadaddr + filesize) < verifyaddr &&
(verifyaddr + filesize) < u) {
unsigned long filesize_padded;
int i;
/* Read back data... */
printf("Reading back firmware...\n");
sprintf(cmd, "nand read %lx %s %lx", verifyaddr, part->name,
filesize);
if (run_command(cmd, 0))
return ERR_READ;
/*
* ...then compare by 32-bit words (faster than by bytes)
* padding with zeros any bytes at the end to make the size
* be a multiple of 4.
*
* Reference: http://stackoverflow.com/a/2022252
*/
printf("Verifying firmware...\n");
filesize_padded = (filesize + (4 - 1)) & ~(4 - 1);
for (i = filesize; i < filesize_padded; i++) {
*((char *)loadaddr + i) = 0;
*((char *)verifyaddr + i) = 0;
}
sprintf(cmd, "cmp.l %lx %lx %lx", loadaddr, verifyaddr,
(filesize_padded / 4));
if (run_command(cmd, 0))
return ERR_VERIFY;
printf("Update was successful\n");
} else {
printf("Firmware updated but not verified "
"(not enough available RAM to verify)\n");
}
return 0;
}
/*
* Attaches an MTD partition to UBI and gets its volume name.
* If a volume does not exist, it creates one with the same name of the
* partition.
*/
static int ubi_attach_getcreatevol(char *partname, const char **volname)
{
char cmd[CONFIG_SYS_CBSIZE] = "";
int ret = -1;
*volname = NULL;
/* Attach partition and get volume name */
if (!ubi_part(partname, NULL)) {
*volname = ubi_get_volume_name(0);
if (!*volname) {
/* Create UBI volume with the name of the partition */
sprintf(cmd, "ubi createvol %s", partname);
if (run_command(cmd, 0))
return -1;
*volname = partname;
}
ret = 0;
}
return ret;
}
static int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
int ret;
int otf = 0;
unsigned long loadaddr;
unsigned long verifyaddr;
unsigned long filesize = 0;
struct mtd_device *dev;
struct part_info *part;
char *ubootpartname = CONFIG_UBOOT_PARTITION;
char *partname;
u8 pnum;
int ubifs_ext = 0;
const char *ubivolname = NULL;
struct load_fw fwinfo;
char cmd[CONFIG_SYS_CBSIZE] = "";
if (argc < 2)
return CMD_RET_USAGE;
memset(&fwinfo, 0, sizeof(fwinfo));
/* Initialize partitions */
if (mtdparts_init()) {
printf("Cannot initialize MTD partitions\n");
return -1;
}
/* Get data of partition to be updated (might be a reserved name) */
if (!strcmp(argv[1], "uboot"))
partname = ubootpartname;
else
partname = argv[1];
if (find_dev_and_part(partname, &dev, &pnum, &part)) {
printf("Cannot find '%s' partition\n", partname);
return -1;
}
if (dev->id->type != MTD_DEV_TYPE_NAND) {
printf("not a NAND device\n");
return -1;
}
/* Ask for confirmation if needed */
if (getenv_yesno("forced_update") != 1) {
/* Confirm programming */
if (!strcmp(part->name, CONFIG_UBOOT_PARTITION) &&
!confirm_msg("Do you really want to program "
"the boot loader? <y/N> "))
return CMD_RET_FAILURE;
}
/* Get source of update firmware file */
if (get_source(argc, argv, &fwinfo))
return CMD_RET_FAILURE;
loadaddr = getenv_ulong("loadaddr", 16, CONFIG_LOADADDR);
/*
* If undefined, calculate 'verifyaddr' as halfway through the RAM
* from $loadaddr.
*/
if (NULL == getenv("verifyaddr")) {
verifyaddr = loadaddr +
((gd->ram_size - (loadaddr - PHYS_SDRAM)) / 2);
if (verifyaddr > loadaddr &&
verifyaddr < (PHYS_SDRAM + gd->ram_size))
setenv_hex("verifyaddr", verifyaddr);
}
if (fwinfo.src == SRC_RAM) {
/* Get address in RAM where firmware file is */
if (argc > 3)
loadaddr = simple_strtol(argv[3], NULL, 16);
/* Get filesize */
if (argc > 4)
filesize = simple_strtol(argv[4], NULL, 16);
} else {
/*
* TODO: If otf-update is undefined, check if there is enough
* RAM to hold the largest possible file that fits into
* the destiny partition.
*/
/* Get firmware file name */
ret = get_fw_filename(argc, argv, &fwinfo);
if (ret) {
/* Filename was not provided. Look for default one */
fwinfo.filename = get_default_filename(argv[1],
CMD_UPDATE);
if (!fwinfo.filename) {
printf("Error: need a filename\n");
return CMD_RET_USAGE;
}
}
/* Check if the filename has extension UBIFS */
if (!strcmp(get_filename_ext(fwinfo.filename), "ubifs"))
ubifs_ext = 1;
}
#ifdef CONFIG_I2SOM_UBI
/*
* If the partition is not U-Boot (whose sectors must be raw-read),
* check if the partition is UBI formatted.
*/
if (strcmp(part->name, CONFIG_UBOOT_PARTITION)) {
bool erase_ubi = 0;
if (is_ubi_partition(part)) {
/* Silent UBI commands during the update */
run_command("ubi silent 1", 0);
/* Attach partition and get volume name */
if (ubi_attach_getcreatevol(partname, &ubivolname)) {
/* On error, erase partition and retry */
erase_ubi = 1;
}
}
/*
* If the partition does not have a valid UBI volume
* but we are updating a *.ubifs filename, erase the
* partition and create the UBI volume.
*/
if ((ubifs_ext && ubivolname == NULL) || erase_ubi) {
sprintf(cmd, "nand erase.part %s", partname);
if (run_command(cmd, 0)) {
ret = CMD_RET_FAILURE;
goto _ret;
}
/* Attach partition and get volume name */
if (ubi_attach_getcreatevol(partname, &ubivolname)) {
ret = CMD_RET_FAILURE;
goto _ret;
}
}
}
#endif /* CONFIG_I2SOM_UBI */
/* TODO: Activate on-the-fly update if needed */
if (fwinfo.src != SRC_RAM) {
/*
* Load firmware file to RAM (this process may write the file
* to the target media if OTF mechanism is enabled).
*/
fwinfo.loadaddr = "$loadaddr";
ret = load_firmware(&fwinfo);
if (ret == LDFW_ERROR) {
printf("Error loading firmware file to RAM\n");
ret = CMD_RET_FAILURE;
goto _ret;
} else if (ret == LDFW_LOADED && otf) {
ret = CMD_RET_SUCCESS;
goto _ret;
}
}
/* Write firmware file from RAM to storage */
if (!filesize)
filesize = getenv_ulong("filesize", 16, 0);
#ifdef CONFIG_CMD_BOOTSTREAM
/* U-Boot is written in a special way */
if (!strcmp(partname, CONFIG_UBOOT_PARTITION)) {
ret = write_bootstream(part, loadaddr, filesize);
goto _ret;
}
#endif
ret = write_firmware(loadaddr, filesize, part, (char *)ubivolname);
if (ret) {
if (ret == ERR_READ)
printf("Error while reading back written firmware!\n");
else if (ret == ERR_VERIFY)
printf("Error while verifying written firmware!\n");
else
printf("Error writing firmware!\n");
ret = CMD_RET_FAILURE;
goto _ret;
}
_ret:
#ifdef CONFIG_I2SOM_UBI
/* restore UBI commands verbosity */
run_command("ubi silent 0", 0);
#endif
return ret;
}
U_BOOT_CMD(
update, 6, 0, do_update,
"i2SOM modules update command",
"<partition> [source] [extra-args...]\n"
" Description: updates <partition> in NAND via <source>\n"
" If the partition is UBI formatted, or a filename with\n"
" extension *.ubifs is passed, writing a UBIFS is assumed\n"
" Otherwise, this command raw-writes the file to the partition.\n"
"\n"
" Arguments:\n"
" - partition: a partition index, a GUID partition name, or one\n"
" of the reserved names: uboot\n"
" - [source]: " CONFIG_UPDATE_SUPPORTED_SOURCES_LIST "\n"
" - [extra-args]: extra arguments depending on 'source'\n"
"\n"
CONFIG_UPDATE_SUPPORTED_SOURCES_ARGS_HELP
);

715
board/i2som/common/helper.c Normal file
View File

@ -0,0 +1,715 @@
/*
* Copyright (C) 2017 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*/
#include <common.h>
#include <asm/errno.h>
#include <malloc.h>
#include <nand.h>
#include <version.h>
#include <watchdog.h>
#ifdef CONFIG_OF_LIBFDT
#include <fdt_support.h>
#endif
#include <otf_update.h>
#include "helper.h"
DECLARE_GLOBAL_DATA_PTR;
#if defined(CONFIG_CMD_UPDATE_MMC) || defined(CONFIG_CMD_UPDATE_NAND)
#define CONFIG_CMD_UPDATE
#endif
#if defined(CONFIG_CMD_UPDATE) || defined(CONFIG_CMD_DBOOT)
enum {
FWLOAD_NO,
FWLOAD_YES,
FWLOAD_TRY,
};
static const char *src_strings[] = {
[SRC_TFTP] = "tftp",
[SRC_NFS] = "nfs",
[SRC_NAND] = "nand",
[SRC_USB] = "usb",
[SRC_MMC] = "mmc",
[SRC_RAM] = "ram",
[SRC_SATA] = "sata",
};
/* hook for on-the-fly update and register function */
static int (*otf_update_hook)(otf_data_t *data) = NULL;
/* Data struct for on-the-fly update */
static otf_data_t otfd;
#endif
#ifdef CONFIG_AUTO_BOOTSCRIPT
#define AUTOSCRIPT_TFTP_MSEC 100
#define AUTOSCRIPT_TFTP_CNT 15
#define AUTOSCRIPT_START_AGAIN 100
extern ulong TftpRRQTimeoutMSecs;
extern int TftpRRQTimeoutCountMax;
extern unsigned long NetStartAgainTimeout;
int DownloadingAutoScript = 0;
int RunningAutoScript = 0;
#endif
int confirm_msg(char *msg)
{
#ifdef CONFIG_AUTO_BOOTSCRIPT
/* From autoscript we shouldn't expect user's confirmations.
* Assume yes is the correct answer here to avoid halting the script.
*/
if (RunningAutoScript)
return 1;
#endif
printf(msg);
if (confirm_yesno())
return 1;
puts("Operation aborted by user\n");
return 0;
}
#if defined(CONFIG_CMD_UPDATE) || defined(CONFIG_CMD_DBOOT)
int get_source(int argc, char * const argv[], struct load_fw *fwinfo)
{
int i;
char *src;
#ifdef CONFIG_CMD_MTDPARTS
struct mtd_device *dev;
u8 pnum;
char *partname;
#endif
if (argc < 3) {
fwinfo->src = SRC_TFTP; /* default to TFTP */
return 0;
}
src = argv[2];
for (i = 0; i < ARRAY_SIZE(src_strings); i++) {
if (!strncmp(src_strings[i], src, strlen(src))) {
if (1 << i & CONFIG_SUPPORTED_SOURCES) {
break;
} else {
fwinfo->src = SRC_UNSUPPORTED;
goto _err;
}
}
}
if (i >= ARRAY_SIZE(src_strings)) {
fwinfo->src = SRC_UNDEFINED;
goto _err;
}
switch (i) {
case SRC_USB:
case SRC_MMC:
case SRC_SATA:
/* Get device:partition and file system */
if (argc > 3)
fwinfo->devpartno = (char *)argv[3];
if (argc > 4)
fwinfo->fs = (char *)argv[4];
break;
case SRC_NAND:
#ifdef CONFIG_CMD_MTDPARTS
/* Initialize partitions */
if (mtdparts_init()) {
printf("Cannot initialize MTD partitions\n");
goto _err;
}
/*
* Use partition name if provided, or else search for a
* partition with the same name as the OS.
*/
if (argc > 3)
partname = argv[3];
else
partname = argv[1];
if (find_dev_and_part(partname, &dev, &pnum, &fwinfo->part)) {
printf("Cannot find '%s' partition\n", partname);
goto _err;
}
#endif
break;
}
fwinfo->src = i;
return 0;
_err:
if (fwinfo->src == SRC_UNSUPPORTED)
printf("Error: '%s' is not supported as source\n", argv[2]);
else if (fwinfo->src == SRC_UNDEFINED)
printf("Error: undefined source\n");
return -1;
}
const char *get_source_string(int src)
{
if (SRC_UNDEFINED != src && src < ARRAY_SIZE(src_strings))
return src_strings[src];
return "";
}
int get_fw_filename(int argc, char * const argv[], struct load_fw *fwinfo)
{
switch (fwinfo->src) {
case SRC_TFTP:
case SRC_NFS:
if (argc > 3) {
fwinfo->filename = argv[3];
return 0;
}
break;
case SRC_MMC:
case SRC_USB:
case SRC_SATA:
if (argc > 5) {
fwinfo->filename = argv[5];
return 0;
}
break;
case SRC_NAND:
if (argc > 4) {
fwinfo->filename = argv[4];
return 0;
}
break;
case SRC_RAM:
return 0; /* No file is needed */
default:
return -1;
}
return -1;
}
char *get_default_filename(char *partname, int cmd)
{
switch(cmd) {
case CMD_DBOOT:
if (!strcmp(partname, "linux") ||
!strcmp(partname, "android")) {
return "$" CONFIG_DBOOT_DEFAULTKERNELVAR;
}
break;
case CMD_UPDATE:
if (!strcmp(partname, "uboot")) {
return "$uboot_file";
} else {
/* Read the default filename from a variable called
* after the partition name: <partname>_file
*/
char varname[100];
sprintf(varname, "%s_file", partname);
return getenv(varname);
}
break;
}
return NULL;
}
int get_default_devpartno(int src, char *devpartno)
{
char *dev, *part;
switch (src) {
case SRC_MMC:
dev = getenv("mmcdev");
if (dev == NULL)
return -1;
part = getenv("mmcpart");
/* If mmcpart not defined, default to 1 */
if (part == NULL)
sprintf(devpartno, "%s:1", dev);
else
sprintf(devpartno, "%s:%s", dev, part);
break;
case SRC_USB: // TODO
case SRC_SATA: // TODO
default:
return -1;
}
return 0;
}
#ifdef CONFIG_DIGI_UBI
bool is_ubi_partition(struct part_info *part)
{
struct mtd_info *nand = &nand_info[0];
size_t rsize = nand->writesize;
unsigned char *page;
unsigned long ubi_magic = 0x23494255; /* "UBI#" */
bool ret = false;
/*
* Check if the partition is UBI formatted by reading the first word
* in the first page, which should contain the UBI magic "UBI#".
* Then verify it contains a UBI volume and get its name.
*/
page = malloc(rsize);
if (page) {
if (!nand_read_skip_bad(nand, part->offset, &rsize, NULL,
part->size, page)) {
unsigned long *magic = (unsigned long *)page;
if (*magic == ubi_magic)
ret = true;
}
free(page);
}
return ret;
}
#endif /* CONFIG_DIGI_UBI */
#endif /* CONFIG_CMD_UPDATE || CONFIG_CMD_DBOOT */
#ifdef CONFIG_CMD_UPDATE
void register_fs_otf_update_hook(int (*hook)(otf_data_t *data),
disk_partition_t *partition)
{
otf_update_hook = hook;
/* Initialize data for new transfer */
otfd.buf = NULL;
otfd.part = partition;
otfd.flags = OTF_FLAG_INIT;
otfd.offset = 0;
}
void unregister_fs_otf_update_hook(void)
{
otf_update_hook = NULL;
}
/* On-the-fly update for files in a filesystem on mass storage media
* The function returns:
* 0 if the file was loaded successfully
* -1 on error
*/
static int write_file_fs_otf(int src, char *filename, char *devpartno)
{
char cmd[CONFIG_SYS_CBSIZE] = "";
unsigned long filesize;
unsigned long remaining;
unsigned long offset = 0;
/* Obtain file size */
sprintf(cmd, "size %s %s %s", src_strings[src], devpartno, filename);
if (run_command(cmd, 0)) {
printf("Couldn't determine file size\n");
return -1;
}
filesize = getenv_ulong("filesize", 16, 0);
remaining = filesize;
/* Init otf data */
otfd.loadaddr = getenv_ulong("loadaddr", 16, 0);
while (remaining > 0) {
debug("%lu remaining bytes\n", remaining);
/* Determine chunk length to write */
if (remaining > CONFIG_OTF_CHUNK) {
otfd.len = CONFIG_OTF_CHUNK;
} else {
otfd.flags |= OTF_FLAG_FLUSH;
otfd.len = remaining;
}
/* Load 'len' bytes from file[offset] into RAM */
sprintf(cmd, "load %s %s $loadaddr %s %x %x", src_strings[src],
devpartno, filename, otfd.len, (unsigned int)offset);
if (run_command(cmd, 0)) {
printf("Couldn't load file\n");
return -1;
}
/* Write chunk */
if (otf_update_hook(&otfd)) {
printf("Error writing on-the-fly. Aborting\n");
return -1;
}
/* Update local target offset */
offset += otfd.len;
/* Update remaining bytes */
remaining -= otfd.len;
}
return 0;
}
/* A variable determines if the file must be loaded.
* The function returns:
* LDFW_LOADED if the file was loaded successfully
* LDFW_NOT_LOADED if the file was not loaded, but isn't required
* LDFW_ERROR on error
*/
int load_firmware(struct load_fw *fwinfo)
{
char cmd[CONFIG_SYS_CBSIZE] = "";
char def_devpartno[] = "0:1";
int ret;
int fwload = FWLOAD_YES;
/* 'fwinfo->varload' determines if the file must be loaded:
* - yes|NULL: the file must be loaded. Return error otherwise.
* - try: the file may be loaded. Return ok even if load fails.
* - no: skip the load.
*/
if (NULL != fwinfo->varload) {
if (!strcmp(fwinfo->varload, "no"))
return LDFW_NOT_LOADED; /* skip load and return ok */
else if (!strcmp(fwinfo->varload, "try"))
fwload = FWLOAD_TRY;
}
/* Use default values if not provided */
if (NULL == fwinfo->devpartno) {
if (get_default_devpartno(fwinfo->src, def_devpartno))
strcpy(def_devpartno, "0:1");
fwinfo->devpartno = def_devpartno;
}
switch (fwinfo->src) {
case SRC_TFTP:
sprintf(cmd, "tftpboot %s %s", fwinfo->loadaddr,
fwinfo->filename);
break;
case SRC_NFS:
sprintf(cmd, "nfs %s $rootpath/%s", fwinfo->loadaddr,
fwinfo->filename);
break;
case SRC_MMC:
case SRC_USB:
case SRC_SATA:
if (otf_update_hook) {
ret = write_file_fs_otf(fwinfo->src, fwinfo->filename,
fwinfo->devpartno);
goto _ret;
} else {
sprintf(cmd, "load %s %s %s %s", src_strings[fwinfo->src],
fwinfo->devpartno, fwinfo->loadaddr,
fwinfo->filename);
}
break;
case SRC_NAND:
#ifdef CONFIG_DIGI_UBI
/*
* If the partition is UBI formatted, use 'ubiload' to read
* a file from the UBIFS file system. Otherwise use a raw
* read using 'nand read'.
*/
if (is_ubi_partition(fwinfo->part)) {
sprintf(cmd,
"if ubi part %s;then "
"if ubifsmount ubi0:%s;then "
"ubifsload %s %s;"
"ubifsumount;"
"fi;"
"fi;",
fwinfo->part->name, fwinfo->part->name,
fwinfo->loadaddr, fwinfo->filename);
} else
#endif
{
sprintf(cmd, "nand read %s %s %x", fwinfo->part->name,
fwinfo->loadaddr, (u32)fwinfo->part->size);
}
break;
case SRC_RAM:
ret = LDFW_NOT_LOADED; /* file is already in RAM */
goto _ret;
default:
return -1;
}
ret = run_command(cmd, 0);
_ret:
if (FWLOAD_TRY == fwload) {
if (ret)
return LDFW_NOT_LOADED;
else
return LDFW_LOADED;
}
if (ret)
return LDFW_ERROR;
return LDFW_LOADED; /* ok, file was loaded */
}
#endif /* CONFIG_CMD_UPDATE */
#if defined(CONFIG_SOURCE) && defined(CONFIG_AUTO_BOOTSCRIPT)
void run_auto_bootscript(void)
{
#ifdef CONFIG_CMD_NET
int ret;
char *bootscript;
/* Save original timeouts */
ulong saved_rrqtimeout_msecs = TftpRRQTimeoutMSecs;
int saved_rrqtimeout_count = TftpRRQTimeoutCountMax;
ulong saved_startagain_timeout = NetStartAgainTimeout;
unsigned long saved_flags = gd->flags;
char *retrycnt = getenv("netretry");
bootscript = getenv("bootscript");
if (bootscript) {
printf("Bootscript from TFTP... ");
/* Silence console */
gd->flags |= GD_FLG_SILENT;
/* set timeouts for bootscript */
TftpRRQTimeoutMSecs = AUTOSCRIPT_TFTP_MSEC;
TftpRRQTimeoutCountMax = AUTOSCRIPT_TFTP_CNT;
NetStartAgainTimeout = AUTOSCRIPT_START_AGAIN;
/* set retrycnt */
setenv("netretry", "no");
/* Silence net commands during the bootscript download */
DownloadingAutoScript = 1;
ret = run_command("tftp ${loadaddr} ${bootscript}", 0);
/* First restore original values of global variables
* and then evaluate the result of the run_command */
DownloadingAutoScript = 0;
/* Restore original timeouts */
TftpRRQTimeoutMSecs = saved_rrqtimeout_msecs;
TftpRRQTimeoutCountMax = saved_rrqtimeout_count;
NetStartAgainTimeout = saved_startagain_timeout;
/* restore retrycnt */
if (retrycnt)
setenv("netretry", retrycnt);
else
setenv("netretry", "");
/* Restore flags */
gd->flags = saved_flags;
if (ret)
goto error;
printf("[ready]\nRunning bootscript...\n");
RunningAutoScript = 1;
/* Launch bootscript */
run_command("source ${loadaddr}", 0);
RunningAutoScript = 0;
return;
error:
printf( "[not available]\n" );
}
#endif
}
#endif
int strtou32(const char *str, unsigned int base, u32 *result)
{
char *ep;
*result = simple_strtoul(str, &ep, base);
if (ep == str || *ep != '\0')
return -EINVAL;
return 0;
}
int confirm_prog(void)
{
puts("Warning: Programming fuses is an irreversible operation!\n"
" This may brick your system.\n"
" Use this command only if you are sure of "
"what you are doing!\n"
"\nReally perform this fuse programming? <y/N>\n");
if (getc() == 'y') {
int c;
putc('y');
c = getc();
putc('\n');
if (c == '\r')
return 1;
}
puts("Fuse programming aborted\n");
return 0;
}
#if defined(CONFIG_OF_BOARD_SETUP)
void fdt_fixup_mac(void *fdt, char *varname, char *node, char *property)
{
char *tmp, *end;
unsigned char mac_addr[6];
int i;
if ((tmp = getenv(varname)) != NULL) {
for (i = 0; i < 6; i++) {
mac_addr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
if (tmp)
tmp = (*end) ? end+1 : end;
}
do_fixup_by_path(fdt, node, property, &mac_addr, 6, 1);
}
}
void fdt_fixup_regulatory(void *fdt)
{
unsigned int val;
char *regdomain = getenv("regdomain");
if (regdomain != NULL) {
val = simple_strtoul(regdomain, NULL, 16);
if (val < DIGI_MAX_CERT) {
sprintf(regdomain, "0x%x", val);
do_fixup_by_path(fdt, "/wireless",
"regulatory-domain", regdomain,
strlen(regdomain) + 1, 1);
}
}
}
#endif /* CONFIG_OF_BOARD_SETUP */
void fdt_fixup_uboot_info(void *fdt) {
do_fixup_by_path(fdt, "/", "digi,uboot,version", version_string,
strlen(version_string), 1);
#ifdef CONFIG_DYNAMIC_ENV_LOCATION
do_fixup_by_path(fdt, "/", "digi,uboot,dynamic-env", NULL, 0, 1);
#endif
}
const char *get_filename_ext(const char *filename)
{
const char *dot;
if (NULL == filename)
return "";
dot = strrchr(filename, '.');
if (!dot || dot == filename)
return "";
return dot + 1;
}
#define STR_HEX_CHUNK 8
/*
* Convert string with hexadecimal characters into a hex number
* @in: Pointer to input string
* @out Pointer to output number array
* @len Number of elements in the output array
*/
void strtohex(char *in, unsigned long *out, int len)
{
char tmp[] = "ffffffff";
int i, j;
for (i = 0, j = 0; j < len; i += STR_HEX_CHUNK, j++) {
strncpy(tmp, &in[i], STR_HEX_CHUNK);
out[j] = cpu_to_be32(simple_strtol(tmp, NULL, 16));
}
}
/*
* Verifies if a MAC address has a default value (dummy) and prints a warning
* if so.
* @var: Variable to check
* @default_mac: Default MAC to check with (as a string)
*/
void verify_mac_address(char *var, char *default_mac)
{
char *mac;
mac = getenv(var);
if (NULL == mac)
printf(" WARNING: MAC not set in '%s'\n", var);
else if (!strcmp(mac, default_mac))
printf(" WARNING: Dummy default MAC in '%s'\n", var);
}
/*
* Check if the storage media address/block is empty
* @in: Address/offset in media
* @in: Partition index, only applies for MMC
* The function returns:
* 1 if the block is empty
* 0 if the block is not empty
* -1 on error
*/
int media_block_is_empty(u32 addr, uint hwpart)
{
size_t len;
int ret = -1;
int i;
uint64_t empty_pattern = 0;
uint64_t *readbuf = NULL;
if (strcmp(CONFIG_SYS_STORAGE_MEDIA, "nand") == 0)
empty_pattern = ~0;
len = media_get_block_size();
if (!len)
return ret;
readbuf = malloc(len);
if (!readbuf)
return ret;
if (media_read_block(addr, (unsigned char *)readbuf, hwpart))
goto out_free;
ret = 1; /* media block empty */
for (i = 0; i < len / 8; i++) {
if (readbuf[i] != empty_pattern) {
ret = 0; /* media block not empty */
break;
}
}
out_free:
free(readbuf);
return ret;
}
/**
* Parses a string into a number. The number stored at ptr is
* potentially suffixed with K (for kilobytes, or 1024 bytes),
* M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
* 1073741824). If the number is suffixed with K, M, or G, then
* the return value is the number multiplied by one kilobyte, one
* megabyte, or one gigabyte, respectively.
*
* @param ptr where parse begins
* @param retptr output pointer to next char after parse completes (output)
* @return resulting unsigned int
*/
u64 memsize_parse(const char *const ptr, const char **retptr)
{
u64 ret = simple_strtoull(ptr, (char **)retptr, 0);
switch (**retptr) {
case 'G':
case 'g':
ret <<= 10;
case 'M':
case 'm':
ret <<= 10;
case 'K':
case 'k':
ret <<= 10;
(*retptr)++;
default:
break;
}
return ret;
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2014 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*/
#ifndef __DIGI_HELPER_H
#define __DIGI_HELPER_H
#include <jffs2/load_kernel.h>
enum {
SRC_UNDEFINED = -2,
SRC_UNSUPPORTED = -1,
SRC_TFTP,
SRC_NFS,
SRC_NAND,
SRC_USB,
SRC_MMC,
SRC_RAM,
SRC_SATA,
};
enum {
CMD_DBOOT,
CMD_UPDATE,
};
enum {
LDFW_ERROR = -1,
LDFW_NOT_LOADED,
LDFW_LOADED,
};
struct load_fw {
int src;
char *filename;
char *devpartno;
char *fs;
char *loadaddr;
char *varload;
struct part_info *part;
};
int confirm_msg(char *msg);
int get_source(int argc, char * const argv[], struct load_fw *fwinfo);
const char *get_source_string(int src);
int get_fw_filename(int argc, char * const argv[], struct load_fw *fwinfo);
char *get_default_filename(char *partname, int cmd);
#ifdef CONFIG_DIGI_UBI
bool is_ubi_partition(struct part_info *part);
#endif
int strtou32(const char *str, unsigned int base, u32 *result);
int confirm_prog(void);
void fdt_fixup_mac(void *fdt, char *varname, char *node, char *property);
void fdt_fixup_regulatory(void *fdt);
void fdt_fixup_uboot_info(void *fdt);
int load_firmware(struct load_fw *fwinfo);
const char *get_filename_ext(const char *filename);
void strtohex(char *in, unsigned long *out, int len);
void verify_mac_address(char *var, char *default_mac);
int get_partition_offset(char *part_name, u32 *offset);
int media_block_is_empty(u32 addr, uint hwpart);
int media_read_block(u32 addr, unsigned char *readbuf, uint hwpart);
int media_write_block(u32 addr, unsigned char *readbuf, uint hwpart);
void media_erase_fskey(u32 addr, uint hwpart);
size_t media_get_block_size(void);
unsigned int get_filesystem_key_offset(void);
uint get_env_hwpart(void);
u64 memsize_parse(const char *const ptr, const char **retptr);
#endif /* __DIGI_HELPER_H */

View File

@ -46,4 +46,10 @@ static inline uint64_t lldiv(uint64_t dividend, uint32_t divisor)
return(__res);
}
static inline uint64_t llmod(uint64_t dividend, uint32_t divisor)
{
uint64_t __res = dividend;
return (do_div(__res, divisor));
}
#endif /* _ASM_GENERIC_DIV64_H */

26
include/otf_update.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2014 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version2 as published by
* the Free Software Foundation.
*/
#ifndef __OTF_UPDATE_H
#define __OTF_UPDATE_H
/* OTF flags */
#define OTF_FLAG_FLUSH (1 << 0) /* flag to write last chunk */
#define OTF_FLAG_INIT (1 << 1) /* flag to write first chunk */
typedef struct otf_data {
unsigned int loadaddr; /* address in RAM to load data to */
unsigned int offset; /* offset in media to write data to */
unsigned char *buf; /* buffer with data to write */
unsigned int len; /* length of chunk to write */
disk_partition_t *part; /* partition data */
unsigned int flags; /* on-the-fly flags */
}otf_data_t;
#endif /* __OTF_UPDATE_H */