iPXE
nvo.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <errno.h>
00030 #include <ipxe/dhcp.h>
00031 #include <ipxe/nvs.h>
00032 #include <ipxe/nvo.h>
00033 
00034 /** @file
00035  *
00036  * Non-volatile stored options
00037  *
00038  */
00039 
00040 /**
00041  * Calculate checksum over non-volatile stored options
00042  *
00043  * @v nvo               Non-volatile options block
00044  * @ret sum             Checksum
00045  */
00046 static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
00047         uint8_t *data = nvo->data;
00048         uint8_t sum = 0;
00049         unsigned int i;
00050 
00051         for ( i = 0 ; i < nvo->len ; i++ ) {
00052                 sum += *(data++);
00053         }
00054         return sum;
00055 }
00056 
00057 /**
00058  * Reallocate non-volatile stored options block
00059  *
00060  * @v nvo               Non-volatile options block
00061  * @v len               New length
00062  * @ret rc              Return status code
00063  */
00064 static int nvo_realloc ( struct nvo_block *nvo, size_t len ) {
00065         void *new_data;
00066 
00067         /* Reallocate data */
00068         new_data = realloc ( nvo->data, len );
00069         if ( ! new_data ) {
00070                 DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
00071                        nvo, len );
00072                 return -ENOMEM;
00073         }
00074         nvo->data = new_data;
00075         nvo->len = len;
00076 
00077         /* Update DHCP option block */
00078         if ( len ) {
00079                 nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ );
00080                 nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ );
00081         } else {
00082                 nvo->dhcpopts.data = NULL;
00083                 nvo->dhcpopts.used_len = 0;
00084                 nvo->dhcpopts.alloc_len = 0;
00085         }
00086 
00087         return 0;
00088 }
00089 
00090 /**
00091  * Reallocate non-volatile stored options DHCP option block
00092  *
00093  * @v options           DHCP option block
00094  * @v len               New length
00095  * @ret rc              Return status code
00096  */
00097 static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) {
00098         struct nvo_block *nvo =
00099                 container_of ( options, struct nvo_block, dhcpopts );
00100         int rc;
00101 
00102         /* Refuse to reallocate if we have no way to resize the block */
00103         if ( ! nvo->resize )
00104                 return dhcpopt_no_realloc ( options, len );
00105 
00106         /* Allow one byte for the checksum (if any data is present) */
00107         if ( len )
00108                 len += 1;
00109 
00110         /* Resize underlying non-volatile options block */
00111         if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) {
00112                 DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n",
00113                        nvo, len, strerror ( rc ) );
00114                 return rc;
00115         }
00116 
00117         /* Reallocate in-memory options block */
00118         if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 )
00119                 return rc;
00120 
00121         return 0;
00122 }
00123 
00124 /**
00125  * Load non-volatile stored options from non-volatile storage device
00126  *
00127  * @v nvo               Non-volatile options block
00128  * @ret rc              Return status code
00129  */
00130 static int nvo_load ( struct nvo_block *nvo ) {
00131         uint8_t *options_data = nvo->dhcpopts.data;
00132         int rc;
00133 
00134         /* Skip reading zero-length NVO fields */
00135         if ( nvo->len == 0 ) {
00136                 DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo );
00137                 return 0;
00138         }
00139 
00140         /* Read data */
00141         if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data,
00142                                nvo->len ) ) != 0 ) {
00143                 DBGC ( nvo, "NVO %p could not read %zd bytes at %#04x: %s\n",
00144                        nvo, nvo->len, nvo->address, strerror ( rc ) );
00145                 return rc;
00146         }
00147 
00148         /* If checksum fails, or options data starts with a zero,
00149          * assume the whole block is invalid.  This should capture the
00150          * case of random initial contents.
00151          */
00152         if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
00153                 DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
00154                        "assuming empty\n", nvo, nvo_checksum ( nvo ),
00155                        options_data[0] );
00156                 memset ( nvo->data, 0, nvo->len );
00157         }
00158 
00159         /* Rescan DHCP option block */
00160         dhcpopt_update_used_len ( &nvo->dhcpopts );
00161 
00162         DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
00163         return 0;
00164 }
00165 
00166 /**
00167  * Save non-volatile stored options back to non-volatile storage device
00168  *
00169  * @v nvo               Non-volatile options block
00170  * @ret rc              Return status code
00171  */
00172 static int nvo_save ( struct nvo_block *nvo ) {
00173         uint8_t *checksum = nvo->data;
00174         int rc;
00175 
00176         /* Recalculate checksum, if applicable */
00177         if ( nvo->len > 0 )
00178                 *checksum -= nvo_checksum ( nvo );
00179 
00180         /* Write data */
00181         if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data,
00182                                 nvo->len ) ) != 0 ) {
00183                 DBGC ( nvo, "NVO %p could not write %zd bytes at %#04x: %s\n",
00184                        nvo, nvo->len, nvo->address, strerror ( rc ) );
00185                 return rc;
00186         }
00187 
00188         DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo );
00189         return 0;
00190 }
00191 
00192 /**
00193  * Check applicability of NVO setting
00194  *
00195  * @v settings          Settings block
00196  * @v setting           Setting
00197  * @ret applies         Setting applies within this settings block
00198  */
00199 int nvo_applies ( struct settings *settings __unused,
00200                   const struct setting *setting ) {
00201 
00202         return ( ( setting->scope == NULL ) &&
00203                  dhcpopt_applies ( setting->tag ) );
00204 }
00205 
00206 /**
00207  * Store value of NVO setting
00208  *
00209  * @v settings          Settings block
00210  * @v setting           Setting to store
00211  * @v data              Setting data, or NULL to clear setting
00212  * @v len               Length of setting data
00213  * @ret rc              Return status code
00214  */
00215 static int nvo_store ( struct settings *settings, const struct setting *setting,
00216                        const void *data, size_t len ) {
00217         struct nvo_block *nvo =
00218                 container_of ( settings, struct nvo_block, settings );
00219         int rc;
00220 
00221         /* Update stored options */
00222         if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag,
00223                                     data, len ) ) != 0 ) {
00224                 DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n",
00225                        nvo, len, strerror ( rc ) );
00226                 return rc;
00227         }
00228 
00229         /* Save updated options to NVS */
00230         if ( ( rc = nvo_save ( nvo ) ) != 0 )
00231                 return rc;
00232 
00233         return 0;
00234 }
00235 
00236 /**
00237  * Fetch value of NVO setting
00238  *
00239  * @v settings          Settings block
00240  * @v setting           Setting to fetch
00241  * @v data              Buffer to fill with setting data
00242  * @v len               Length of buffer
00243  * @ret len             Length of setting data, or negative error
00244  *
00245  * The actual length of the setting will be returned even if
00246  * the buffer was too small.
00247  */
00248 static int nvo_fetch ( struct settings *settings, struct setting *setting,
00249                        void *data, size_t len ) {
00250         struct nvo_block *nvo =
00251                 container_of ( settings, struct nvo_block, settings );
00252 
00253         return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len );
00254 }
00255 
00256 /** NVO settings operations */
00257 static struct settings_operations nvo_settings_operations = {
00258         .applies = nvo_applies,
00259         .store = nvo_store,
00260         .fetch = nvo_fetch,
00261 };
00262 
00263 /**
00264  * Initialise non-volatile stored options
00265  *
00266  * @v nvo               Non-volatile options block
00267  * @v nvs               Underlying non-volatile storage device
00268  * @v address           Address within NVS device
00269  * @v len               Length of non-volatile options data
00270  * @v resize            Resize method
00271  * @v refcnt            Containing object reference counter, or NULL
00272  */
00273 void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
00274                 size_t address, size_t len,
00275                 int ( * resize ) ( struct nvo_block *nvo, size_t len ),
00276                 struct refcnt *refcnt ) {
00277         nvo->nvs = nvs;
00278         nvo->address = address;
00279         nvo->len = len;
00280         nvo->resize = resize;
00281         dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt );
00282         settings_init ( &nvo->settings, &nvo_settings_operations,
00283                         refcnt, NULL );
00284 }
00285 
00286 /**
00287  * Register non-volatile stored options
00288  *
00289  * @v nvo               Non-volatile options block
00290  * @v parent            Parent settings block, or NULL
00291  * @ret rc              Return status code
00292  */
00293 int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
00294         int rc;
00295 
00296         /* Allocate memory for options */
00297         if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 )
00298                 goto err_realloc;
00299 
00300         /* Read data from NVS */
00301         if ( ( rc = nvo_load ( nvo ) ) != 0 )
00302                 goto err_load;
00303 
00304         /* Register settings */
00305         if ( ( rc = register_settings ( &nvo->settings, parent,
00306                                         NVO_SETTINGS_NAME ) ) != 0 )
00307                 goto err_register;
00308 
00309         DBGC ( nvo, "NVO %p registered\n", nvo );
00310         return 0;
00311         
00312  err_register:
00313  err_load:
00314         nvo_realloc ( nvo, 0 );
00315  err_realloc:
00316         return rc;
00317 }
00318 
00319 /**
00320  * Unregister non-volatile stored options
00321  *
00322  * @v nvo               Non-volatile options block
00323  */
00324 void unregister_nvo ( struct nvo_block *nvo ) {
00325         unregister_settings ( &nvo->settings );
00326         nvo_realloc ( nvo, 0 );
00327         DBGC ( nvo, "NVO %p unregistered\n", nvo );
00328 }