def _ParseHeader(self): self.header_length = 0 magic = self.file.read(3) if magic != b'P5\x0a': raise errors.YandException( 'Input file {0:s} is not a PGM picture (bad magic \'{1!s}\')'. format(self.filepath, magic)) self.header_length += 3 comment = b'' bb = self.file.read(1) self.header_length += 1 while bb != b'\x0a': # Comment comment += bb bb = self.file.read(1) self.header_length += 1 while self.file.read(1) == b'\x0a': self.header_length += 1 self.file.seek(-1, SEEK_CUR) dimensions = b'' bb = self.file.read(1) while bb != b'\x0a': dimensions += bb bb = self.file.read(1) self.header_length += len(dimensions) + 1 self.width, self.height = [int(d) for d in dimensions.split(b' ')] if self.file.read(4) != b'255\x0a': raise errors.YandException( ('Input file {0:s} is not a PGM picture ' '(should have 255 as max value)').format(self.filepath)) self.header_length += 4
def Write(self, data, command=False, address=False): """Write a set of bytes to the device. Args: data(bytearray): the data to write. command(bool): if it is a command. address(bool): if it is an address. Raises: errors.YandException: if both command & address types are set. """ cmd_type = 0 if command and address: raise errors.YandException( 'Can\'t set command and address latch simultaneously') if command: cmd_type |= 0x40 elif address: cmd_type |= 0x80 if not self.write_protect: cmd_type |= 0x20 cmds = [ftdi.Ftdi.WRITE_EXTENDED, cmd_type, 0, data[0]] for i in range(len(data) - 1): cmds += [ftdi.Ftdi.WRITE_SHORT, 0, data[i + 1]] self.ftdi.write_data(bytearray(cmds))
def WriteFileToFlash(self, filename, write_check=False): """Overwrite file to NAND Flash. Args: filename(str): path to the dump to write. write_check(bool): Whether to check every page written. Raises: errors.YandException: if filename has more data than the NAND Flash. """ filesize = os.stat(filename).st_size if filesize > self.GetTotalSize(): raise errors.YandException( 'Input file is {0:d} bytes, more than the current NAND Flash size ({1:d})'.format( filesize, self.GetTotalSize())) if filesize < self.GetTotalSize(): self.logger.debug( 'input file is {0:d}, less than the current NAND Flash size ({1:d})'.format( filesize, self.GetTotalSize())) progress_bar = tqdm( total=self.GetTotalSize(), unit_scale=True, unit_divisor=1024, unit='B' ) with open(filename, 'rb') as input_file: for page_number in range(self.GetTotalPages()): page_data = input_file.read(self.page_size) self.WritePage(page_number, page_data, write_check=write_check) progress_bar.update(self.page_size)
def DumpFlashToFile(self, destination, start_page=0, end_page=None): """Reads all pages from the flash, and writes it to a file. Args: destination(str): the destination file. start_page(int): Page to start dumping from. end_page(int): Page to stop dumping at. Default is to the end. Raises: errors.YandException: if no destination file is provided. """ if not destination: raise errors.YandException('Please specify where to write') if not end_page: end_page = self.GetTotalPages() if destination == "-": for page in range(start_page, end_page): sys.stdout.buffer.write(self.ReadPage(page)) else: progress_bar = tqdm( total=(end_page - start_page) * self.page_size, unit_scale=True, unit_divisor=1024, unit='B' ) with open(destination, 'wb') as dest_file: for page in range(start_page, end_page): dest_file.write(self.ReadPage(page)) progress_bar.update(self.page_size)
def _SetupFlash(self): """Setup the flash configuration.""" # First we check if we can gather information form ONFI self.SendCommand(self.NAND_CMD_READID) self.SendAddress(self.NAND_ADDR_ONFI) onfi_result = self.ftdi_device.Read(4) if onfi_result == b'ONFI': self.SendCommand(self.NAND_CMD_READ_PARAM_PAGE) self.SendAddress(self.NAND_ADDR_ID) self.ftdi_device.WaitReady() onfi_data = self.ftdi_device.Read(self.NAND_SIZE_ONFI) self._ParseONFIData(onfi_data) else: if onfi_result.hex() == '00000000': raise errors.YandException( 'Flash returned "00000000" as self identification\n' 'It is either defective or not connected properly') raise errors.YandException( 'Warning: Could not read ONFI info. Please provide geometry\n' 'Flash returned "{0:s}"'.format(onfi_result.hex()))
def WaitReady(self): """Waits for the FTDI device to be ready. Raises: errors.YandException: if the device is not ready. """ while 1: self.ftdi.write_data(bytearray([ftdi.Ftdi.GET_BITS_HIGH])) data = self.ftdi.read_data_bytes(1) if not data: data = self.ftdi.read_data_bytes(1) if not data: raise errors.YandException( 'FTDI device not responding. Try restarting it.') if data[0] & 2 == 0x2: break
def Setup(self): """Sets up the FTDI device.""" self.ftdi = ftdi.Ftdi() try: self.ftdi.open(self.DEFAULT_USB_VENDOR, self.DEFAULT_USB_DEVICEID, interface=self.DEFAULT_INTERFACE_NUMBER) except OSError: raise errors.YandException('Could not open FTDI device\n' 'Check USB connections') self.ftdi.set_bitmode(0, ftdi.Ftdi.BITMODE_MCU) self.ftdi.write_data(bytearray([ftdi.Ftdi.DISABLE_CLK_DIV5])) self.ftdi.purge_buffers() self.ftdi.write_data(bytearray([ftdi.Ftdi.SET_BITS_HIGH, 0x0, 0x1])) self.WaitReady()
def _ParseONFIData(self, onfi_data): """Parses a ONFI data block.""" # Check ONFI magic if onfi_data[0:4] != bytearray([0x4F, 0x4E, 0x46, 0x49]): raise errors.YandException('ONFI data block does not start with \'ONFI\'') # Parses ONFI version support _ = onfi_data[4:6] # Parses features support _ = onfi_data[6:8] # Parses optional commands support _ = onfi_data[8:10] # extended page parameter length _ = onfi_data[12:14] # Number of parameter pages _ = onfi_data[14] self.device_manufacturer = onfi_data[32:44].decode() self.device_model = onfi_data[44:64].decode() # 1 byte manufacturer ID self.manufacturer_id = onfi_data[64] # 'user data' bytes per page user_size = int.from_bytes(onfi_data[80:84], byteorder='little') # spare/OOB size per page self.oob_size = int.from_bytes(onfi_data[84:86], byteorder='little') self.page_size = user_size + self.oob_size self.pages_per_block = int.from_bytes(onfi_data[92:96], byteorder='little') # Number of blocks per LUN number_blocks_per_lun = int.from_bytes(onfi_data[96:100], byteorder='little') # Number of LUNs per chip enable number_lun_per_chip = onfi_data[100] self.number_of_blocks = number_blocks_per_lun * number_lun_per_chip address_cycles = onfi_data[101] self.address_cycles = (address_cycles & 0x0f) + ((address_cycles & 0xf0) >> 4)
def WritePage(self, page_number, data, write_check=False): """Writes a page to the NAND Flash Args: page_number(int): the number of the page. data(bytearray): the data to program. write_check(bool): Whether to check every page written by reading it. Raises: errors.YandException: if trying to write more data than a block length. """ if not len(data) == self.page_size: raise errors.YandException( 'Trying to write data that is different than page_size: {0:d} != {1:d}'.format( len(data), self.page_size)) self.ftdi_device.write_protect = False page_address = page_number << 16 self.SendCommand(self.NAND_CMD_PROG_PAGE) # I've had good results when keeping just one "WaitReady" here. # Feel free to uncomment if your writes are not super clean # self.ftdi_device.WaitReady() self.SendAddress(page_address, self.address_cycles) # self.ftdi_device.WaitReady() self.ftdi_device.Write(data) self.SendCommand(self.NAND_CMD_PROG_PAGE_START) self.ftdi_device.WaitReady() self.CheckStatus() self.logger.debug('written page {0:d} (addr: {1:d})'.format(page_number, page_address)) if write_check: data_read = self.ReadPage(page_number) diff = helpers.Diff(data, data_read) if diff != 0: self.logger.debug( 'data written & data read differ by {0:d} bytes at page {1:d}'.format( diff, page_number)) self.ftdi_device.write_protect = True
def Main(self): """Main function""" options = self.ParseArguments() if options.version: print('{0:s}: {1:s}'.format(__file__, __version__)) sys.exit(0) if options.logfile: logging.basicConfig( filename=options.logfile, level=logging.DEBUG, format='%(asctime)s %(message)s', datefmt='[%Y-%m-%d %H:%M:%S]' ) ftdi_nand = nand_interface.NandInterface() if not ftdi_nand: self.parser.print_help() raise errors.YandException('Need a source to read from') # Set up geometry if options.page_size: try: page_size, oob_size = [ int(opt) for opt in options.page_size.split(',')] ftdi_nand.oob_size = oob_size ftdi_nand.page_size = oob_size + page_size except ValueError as value_error: raise errors.YandException( 'Please specify page size as such : "user_data,oob". For example: "2048,128"' ) from value_error if options.pages_per_block: ftdi_nand.pages_per_block = int(options.pages_per_block) if options.number_of_blocks: ftdi_nand.number_of_blocks = int(options.number_of_blocks) ftdi_nand.Setup() infos = 'Chip info: '+ftdi_nand.GetInfos() logging.debug(infos) if not options.file == '-': print(infos) if options.read: if not options.file: Die('Need a destination file (hint: -f)') if os.path.exists(options.file): if not Confirm( 'Destination file {0:s} already exists. Proceed?'.format(options.file), options.yes): Die() logging.debug( 'Starting a read operation (start={0:d}, end={1:d}, destination={2:s})'.format( options.start, options.end or -1, options.file)) ftdi_nand.DumpFlashToFile(options.file, start_page=options.start, end_page=options.end) elif options.write: if not Confirm( 'Reminder: ' 'You need to erase the entire flash with -e for this to work as expected\n\n' 'About to write the content of "{0:s}" to NAND Flash. Proceed?'.format( options.file), options.yes): Die() logging.debug( 'Starting an Dump write operation with file {0:s} (write check is {1!s})'.format( options.file, options.write_check)) ftdi_nand.WriteFileToFlash(options.file, write_check=options.write_check) elif options.erase: if not Confirm('About to erase NAND Flash blocks. Proceed?', options.yes): Die() logging.debug( 'Starting an erase operation (start={0:d}, end={1:d})'.format( options.start, options.end or -1)) ftdi_nand.Erase(start_block=options.start, end_block=options.end) elif options.write_value is not None: if not Confirm( 'About to write value {0:d} in NAND Flash. Proceed?'.format( options.write_value), options.yes): Die() logging.debug( 'Starting a fill value operation ' '(start={0:d}, end={1:d}, value={2:d}, write check is {3!s})'.format( options.start, options.end or -1, options.write_value, options.write_check)) ftdi_nand.FillWithValue( options.write_value, start_page=options.start, end_page=options.end, write_check=options.write_check) elif options.write_pgm: if not Confirm( 'About to write content of {0:s} in NAND Flash. Proceed?'.format( options.file), options.yes): sys.exit(1) logging.debug( 'Starting a write pgm operation ' '(start={0:d}, end={1:d}, pgm_file={2:s}, write check is {3!s})'.format( options.start, options.end or -1, options.file, options.write_check)) ftdi_nand.WritePGMToFlash( options.file, start_page=options.start, end_page=options.end, write_check=options.write_check)