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 <stdlib.h> 23 #include <string.h> 24 #include <errno.h> 25 #include <gpxe/dhcp.h> 26 #include <gpxe/nvs.h> 27 #include <gpxe/nvo.h> 28 29 /** @file 30 * 31 * Non-volatile stored options 32 * 33 */ 34 35 /** 36 * Calculate checksum over non-volatile stored options 37 * 38 * @v nvo Non-volatile options block 39 * @ret sum Checksum 40 */ 41 static unsigned int nvo_checksum ( struct nvo_block *nvo ) { 42 uint8_t *data = nvo->data; 43 uint8_t sum = 0; 44 unsigned int i; 45 46 for ( i = 0 ; i < nvo->total_len ; i++ ) { 47 sum += *(data++); 48 } 49 return sum; 50 } 51 52 /** 53 * Load non-volatile stored options from non-volatile storage device 54 * 55 * @v nvo Non-volatile options block 56 * @ret rc Return status code 57 */ 58 static int nvo_load ( struct nvo_block *nvo ) { 59 void *data = nvo->data; 60 struct nvo_fragment *frag; 61 int rc; 62 63 /* Read data a fragment at a time */ 64 for ( frag = nvo->fragments ; frag->len ; frag++ ) { 65 if ( ( rc = nvs_read ( nvo->nvs, frag->address, data, 66 frag->len ) ) != 0 ) { 67 DBGC ( nvo, "NVO %p could not read %zd bytes at " 68 "%#04x\n", nvo, frag->len, frag->address ); 69 return rc; 70 } 71 data += frag->len; 72 } 73 74 DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); 75 return 0; 76 } 77 78 /** 79 * Save non-volatile stored options back to non-volatile storage device 80 * 81 * @v nvo Non-volatile options block 82 * @ret rc Return status code 83 */ 84 static int nvo_save ( struct nvo_block *nvo ) { 85 void *data = nvo->data; 86 uint8_t *checksum = data; 87 struct nvo_fragment *frag; 88 int rc; 89 90 /* Recalculate checksum */ 91 *checksum -= nvo_checksum ( nvo ); 92 93 /* Write data a fragment at a time */ 94 for ( frag = nvo->fragments ; frag->len ; frag++ ) { 95 if ( ( rc = nvs_write ( nvo->nvs, frag->address, data, 96 frag->len ) ) != 0 ) { 97 DBGC ( nvo, "NVO %p could not write %zd bytes at " 98 "%#04x\n", nvo, frag->len, frag->address ); 99 return rc; 100 } 101 data += frag->len; 102 } 103 104 DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); 105 return 0; 106 } 107 108 /** 109 * Parse stored options 110 * 111 * @v nvo Non-volatile options block 112 * 113 * Verifies that the options data is valid, and configures the DHCP 114 * options block. If the data is not valid, it is replaced with an 115 * empty options block. 116 */ 117 static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { 118 uint8_t *options_data; 119 size_t options_len; 120 121 /* Steal one byte for the checksum */ 122 options_data = ( nvo->data + 1 ); 123 options_len = ( nvo->total_len - 1 ); 124 125 /* If checksum fails, or options data starts with a zero, 126 * assume the whole block is invalid. This should capture the 127 * case of random initial contents. 128 */ 129 if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { 130 DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " 131 "assuming empty\n", nvo, nvo_checksum ( nvo ), 132 options_data[0] ); 133 memset ( nvo->data, 0, nvo->total_len ); 134 } 135 136 dhcpopt_init ( &nvo->dhcpopts, options_data, options_len ); 137 } 138 139 /** 140 * Store value of NVO setting 141 * 142 * @v settings Settings block 143 * @v setting Setting to store 144 * @v data Setting data, or NULL to clear setting 145 * @v len Length of setting data 146 * @ret rc Return status code 147 */ 148 static int nvo_store ( struct settings *settings, struct setting *setting, 149 const void *data, size_t len ) { 150 struct nvo_block *nvo = 151 container_of ( settings, struct nvo_block, settings ); 152 int rc; 153 154 /* Update stored options */ 155 if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag, 156 data, len ) ) != 0 ) { 157 DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n", 158 nvo, len, strerror ( rc ) ); 159 return rc; 160 } 161 162 /* Save updated options to NVS */ 163 if ( ( rc = nvo_save ( nvo ) ) != 0 ) 164 return rc; 165 166 return 0; 167 } 168 169 /** 170 * Fetch value of NVO setting 171 * 172 * @v settings Settings block 173 * @v setting Setting to fetch 174 * @v data Buffer to fill with setting data 175 * @v len Length of buffer 176 * @ret len Length of setting data, or negative error 177 * 178 * The actual length of the setting will be returned even if 179 * the buffer was too small. 180 */ 181 static int nvo_fetch ( struct settings *settings, struct setting *setting, 182 void *data, size_t len ) { 183 struct nvo_block *nvo = 184 container_of ( settings, struct nvo_block, settings ); 185 186 return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len ); 187 } 188 189 /** NVO settings operations */ 190 static struct settings_operations nvo_settings_operations = { 191 .store = nvo_store, 192 .fetch = nvo_fetch, 193 }; 194 195 /** 196 * Initialise non-volatile stored options 197 * 198 * @v nvo Non-volatile options block 199 * @v nvs Underlying non-volatile storage device 200 * @v fragments List of option-containing fragments 201 * @v refcnt Containing object reference counter, or NULL 202 */ 203 void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, 204 struct nvo_fragment *fragments, struct refcnt *refcnt ) { 205 nvo->nvs = nvs; 206 nvo->fragments = fragments; 207 settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 208 "nvo", 0 ); 209 } 210 211 /** 212 * Register non-volatile stored options 213 * 214 * @v nvo Non-volatile options block 215 * @v parent Parent settings block, or NULL 216 * @ret rc Return status code 217 */ 218 int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { 219 struct nvo_fragment *fragment = nvo->fragments; 220 int rc; 221 222 /* Calculate total length of all fragments */ 223 for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) 224 nvo->total_len += fragment->len; 225 226 /* Allocate memory for options and read in from NVS */ 227 nvo->data = malloc ( nvo->total_len ); 228 if ( ! nvo->data ) { 229 DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", 230 nvo, nvo->total_len ); 231 rc = -ENOMEM; 232 goto err_malloc; 233 } 234 if ( ( rc = nvo_load ( nvo ) ) != 0 ) 235 goto err_load; 236 237 /* Verify and register options */ 238 nvo_init_dhcpopts ( nvo ); 239 if ( ( rc = register_settings ( &nvo->settings, parent ) ) != 0 ) 240 goto err_register; 241 242 DBGC ( nvo, "NVO %p registered\n", nvo ); 243 return 0; 244 245 err_register: 246 err_load: 247 free ( nvo->data ); 248 nvo->data = NULL; 249 err_malloc: 250 return rc; 251 } 252 253 /** 254 * Unregister non-volatile stored options 255 * 256 * @v nvo Non-volatile options block 257 */ 258 void unregister_nvo ( struct nvo_block *nvo ) { 259 unregister_settings ( &nvo->settings ); 260 free ( nvo->data ); 261 nvo->data = NULL; 262 DBGC ( nvo, "NVO %p unregistered\n", nvo ); 263 } 264