Home | History | Annotate | Download | only in nvs
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of the
      7  * License, or any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write to the Free Software
     16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <stdint.h>
     22 #include <string.h>
     23 #include <errno.h>
     24 #include <assert.h>
     25 #include <gpxe/nvs.h>
     26 
     27 /** @file
     28  *
     29  * Non-volatile storage
     30  *
     31  */
     32 
     33 /**
     34  * Read from non-volatile storage device
     35  *
     36  * @v nvs		NVS device
     37  * @v address		Address from which to read
     38  * @v data		Data buffer
     39  * @v len		Length of data buffer
     40  * @ret rc		Return status code
     41  */
     42 int nvs_read ( struct nvs_device *nvs, unsigned int address,
     43 	       void *data, size_t len ) {
     44 	size_t frag_len;
     45 	int rc;
     46 
     47 	/* We don't even attempt to handle buffer lengths that aren't
     48 	 * an integral number of words.
     49 	 */
     50 	assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 );
     51 
     52 	while ( len ) {
     53 
     54 		/* Calculate space remaining up to next block boundary */
     55 		frag_len = ( ( nvs->block_size -
     56 			       ( address & ( nvs->block_size - 1 ) ) )
     57 			     << nvs->word_len_log2 );
     58 
     59 		/* Limit to space remaining in buffer */
     60 		if ( frag_len > len )
     61 			frag_len = len;
     62 
     63 		/* Read this portion of the buffer from the device */
     64 		if ( ( rc = nvs->read ( nvs, address, data, frag_len ) ) != 0 )
     65 			return rc;
     66 
     67 		/* Update parameters */
     68 		data += frag_len;
     69 		address += ( frag_len >> nvs->word_len_log2 );
     70 		len -= frag_len;
     71 	}
     72 
     73 	return 0;
     74 }
     75 
     76 /**
     77  * Verify content of non-volatile storage device
     78  *
     79  * @v nvs		NVS device
     80  * @v address		Address from which to read
     81  * @v data		Data to compare against
     82  * @v len		Length of data buffer
     83  * @ret rc		Return status code
     84  */
     85 static int nvs_verify ( struct nvs_device *nvs, unsigned int address,
     86 			const void *data, size_t len ) {
     87 	uint8_t read_data[len];
     88 	int rc;
     89 
     90 	/* Read data into temporary buffer */
     91 	if ( ( rc = nvs_read ( nvs, address, read_data, len ) ) != 0 )
     92 		return rc;
     93 
     94 	/* Compare data */
     95 	if ( memcmp ( data, read_data, len ) != 0 ) {
     96 		DBG ( "NVS %p verification failed at %#04x+%zd\n",
     97 		      nvs, address, len );
     98 		return -EIO;
     99 	}
    100 
    101 	return 0;
    102 }
    103 
    104 /**
    105  * Write to non-volatile storage device
    106  *
    107  * @v nvs		NVS device
    108  * @v address		Address to which to write
    109  * @v data		Data buffer
    110  * @v len		Length of data buffer
    111  * @ret rc		Return status code
    112  */
    113 int nvs_write ( struct nvs_device *nvs, unsigned int address,
    114 		const void *data, size_t len ) {
    115 	size_t frag_len;
    116 	int rc;
    117 
    118 	/* We don't even attempt to handle buffer lengths that aren't
    119 	 * an integral number of words.
    120 	 */
    121 	assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 );
    122 
    123 	while ( len ) {
    124 
    125 		/* Calculate space remaining up to next block boundary */
    126 		frag_len = ( ( nvs->block_size -
    127 			       ( address & ( nvs->block_size - 1 ) ) )
    128 			     << nvs->word_len_log2 );
    129 
    130 		/* Limit to space remaining in buffer */
    131 		if ( frag_len > len )
    132 			frag_len = len;
    133 
    134 		/* Write this portion of the buffer to the device */
    135 		if ( ( rc = nvs->write ( nvs, address, data, frag_len ) ) != 0)
    136 			return rc;
    137 
    138 		/* Read back and verify data */
    139 		if ( ( rc = nvs_verify ( nvs, address, data, frag_len ) ) != 0)
    140 			return rc;
    141 
    142 		/* Update parameters */
    143 		data += frag_len;
    144 		address += ( frag_len >> nvs->word_len_log2 );
    145 		len -= frag_len;
    146 	}
    147 
    148 	return 0;
    149 }
    150