2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
30 #include <ipxe/dhcp.h>
36 * Non-volatile stored options
41 * Calculate checksum over non-volatile stored options
43 * @v nvo Non-volatile options block
46 static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
47 uint8_t *data = nvo->data;
51 for ( i = 0 ; i < nvo->len ; i++ ) {
58 * Reallocate non-volatile stored options block
60 * @v nvo Non-volatile options block
62 * @ret rc Return status code
64 static int nvo_realloc ( struct nvo_block *nvo, size_t len ) {
68 new_data = realloc ( nvo->data, len );
70 DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
77 /* Update DHCP option block */
79 nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ );
80 nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ );
82 nvo->dhcpopts.data = NULL;
83 nvo->dhcpopts.used_len = 0;
84 nvo->dhcpopts.alloc_len = 0;
91 * Reallocate non-volatile stored options DHCP option block
93 * @v options DHCP option block
95 * @ret rc Return status code
97 static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) {
98 struct nvo_block *nvo =
99 container_of ( options, struct nvo_block, dhcpopts );
102 /* Refuse to reallocate if we have no way to resize the block */
104 return dhcpopt_no_realloc ( options, len );
106 /* Allow one byte for the checksum (if any data is present) */
110 /* Resize underlying non-volatile options block */
111 if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) {
112 DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n",
113 nvo, len, strerror ( rc ) );
117 /* Reallocate in-memory options block */
118 if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 )
125 * Load non-volatile stored options from non-volatile storage device
127 * @v nvo Non-volatile options block
128 * @ret rc Return status code
130 static int nvo_load ( struct nvo_block *nvo ) {
131 uint8_t *options_data = nvo->dhcpopts.data;
134 /* Skip reading zero-length NVO fields */
135 if ( nvo->len == 0 ) {
136 DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo );
141 if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data,
142 nvo->len ) ) != 0 ) {
143 DBGC ( nvo, "NVO %p could not read %zd bytes at %#04x: %s\n",
144 nvo, nvo->len, nvo->address, strerror ( rc ) );
148 /* If checksum fails, or options data starts with a zero,
149 * assume the whole block is invalid. This should capture the
150 * case of random initial contents.
152 if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
153 DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
154 "assuming empty\n", nvo, nvo_checksum ( nvo ),
156 memset ( nvo->data, 0, nvo->len );
159 /* Rescan DHCP option block */
160 dhcpopt_update_used_len ( &nvo->dhcpopts );
162 DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
167 * Save non-volatile stored options back to non-volatile storage device
169 * @v nvo Non-volatile options block
170 * @ret rc Return status code
172 static int nvo_save ( struct nvo_block *nvo ) {
173 uint8_t *checksum = nvo->data;
176 /* Recalculate checksum, if applicable */
178 *checksum -= nvo_checksum ( nvo );
181 if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data,
182 nvo->len ) ) != 0 ) {
183 DBGC ( nvo, "NVO %p could not write %zd bytes at %#04x: %s\n",
184 nvo, nvo->len, nvo->address, strerror ( rc ) );
188 DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo );
193 * Check applicability of NVO setting
195 * @v settings Settings block
197 * @ret applies Setting applies within this settings block
199 int nvo_applies ( struct settings *settings __unused,
200 const struct setting *setting ) {
202 return ( ( setting->scope == NULL ) &&
203 dhcpopt_applies ( setting->tag ) );
207 * Store value of NVO setting
209 * @v settings Settings block
210 * @v setting Setting to store
211 * @v data Setting data, or NULL to clear setting
212 * @v len Length of setting data
213 * @ret rc Return status code
215 static int nvo_store ( struct settings *settings, const struct setting *setting,
216 const void *data, size_t len ) {
217 struct nvo_block *nvo =
218 container_of ( settings, struct nvo_block, settings );
221 /* Update stored options */
222 if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag,
223 data, len ) ) != 0 ) {
224 DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n",
225 nvo, len, strerror ( rc ) );
229 /* Save updated options to NVS */
230 if ( ( rc = nvo_save ( nvo ) ) != 0 )
237 * Fetch value of NVO setting
239 * @v settings Settings block
240 * @v setting Setting to fetch
241 * @v data Buffer to fill with setting data
242 * @v len Length of buffer
243 * @ret len Length of setting data, or negative error
245 * The actual length of the setting will be returned even if
246 * the buffer was too small.
248 static int nvo_fetch ( struct settings *settings, struct setting *setting,
249 void *data, size_t len ) {
250 struct nvo_block *nvo =
251 container_of ( settings, struct nvo_block, settings );
253 return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len );
256 /** NVO settings operations */
257 static struct settings_operations nvo_settings_operations = {
258 .applies = nvo_applies,
264 * Initialise non-volatile stored options
266 * @v nvo Non-volatile options block
267 * @v nvs Underlying non-volatile storage device
268 * @v address Address within NVS device
269 * @v len Length of non-volatile options data
270 * @v resize Resize method
271 * @v refcnt Containing object reference counter, or NULL
273 void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
274 size_t address, size_t len,
275 int ( * resize ) ( struct nvo_block *nvo, size_t len ),
276 struct refcnt *refcnt ) {
278 nvo->address = address;
280 nvo->resize = resize;
281 dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt );
282 settings_init ( &nvo->settings, &nvo_settings_operations,
287 * Register non-volatile stored options
289 * @v nvo Non-volatile options block
290 * @v parent Parent settings block, or NULL
291 * @ret rc Return status code
293 int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
296 /* Allocate memory for options */
297 if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 )
300 /* Read data from NVS */
301 if ( ( rc = nvo_load ( nvo ) ) != 0 )
304 /* Register settings */
305 if ( ( rc = register_settings ( &nvo->settings, parent,
306 NVO_SETTINGS_NAME ) ) != 0 )
309 DBGC ( nvo, "NVO %p registered\n", nvo );
314 nvo_realloc ( nvo, 0 );
320 * Unregister non-volatile stored options
322 * @v nvo Non-volatile options block
324 void unregister_nvo ( struct nvo_block *nvo ) {
325 unregister_settings ( &nvo->settings );
326 nvo_realloc ( nvo, 0 );
327 DBGC ( nvo, "NVO %p unregistered\n", nvo );