1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
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.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <ipxe/dhcp.h>
31 #include <ipxe/nvs.h>
32 #include <ipxe/nvo.h>
33
34 /** @file
35 *
36 * Non-volatile stored options
37 *
38 */
39
40 /**
41 * Calculate checksum over non-volatile stored options
42 *
43 * @v nvo Non-volatile options block
44 * @ret sum Checksum
45 */
nvo_checksum(struct nvo_block * nvo)46 static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
47 uint8_t *data = nvo->data;
48 uint8_t sum = 0;
49 unsigned int i;
50
51 for ( i = 0 ; i < nvo->len ; i++ ) {
52 sum += *(data++);
53 }
54 return sum;
55 }
56
57 /**
58 * Reallocate non-volatile stored options block
59 *
60 * @v nvo Non-volatile options block
61 * @v len New length
62 * @ret rc Return status code
63 */
nvo_realloc(struct nvo_block * nvo,size_t len)64 static int nvo_realloc ( struct nvo_block *nvo, size_t len ) {
65 void *new_data;
66
67 /* Reallocate data */
68 new_data = realloc ( nvo->data, len );
69 if ( ! new_data ) {
70 DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
71 nvo, len );
72 return -ENOMEM;
73 }
74 nvo->data = new_data;
75 nvo->len = len;
76
77 /* Update DHCP option block */
78 if ( len ) {
79 nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ );
80 nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ );
81 } else {
82 nvo->dhcpopts.data = NULL;
83 nvo->dhcpopts.used_len = 0;
84 nvo->dhcpopts.alloc_len = 0;
85 }
86
87 return 0;
88 }
89
90 /**
91 * Reallocate non-volatile stored options DHCP option block
92 *
93 * @v options DHCP option block
94 * @v len New length
95 * @ret rc Return status code
96 */
nvo_realloc_dhcpopt(struct dhcp_options * options,size_t len)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 );
100 int rc;
101
102 /* Refuse to reallocate if we have no way to resize the block */
103 if ( ! nvo->resize )
104 return dhcpopt_no_realloc ( options, len );
105
106 /* Allow one byte for the checksum (if any data is present) */
107 if ( len )
108 len += 1;
109
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 ) );
114 return rc;
115 }
116
117 /* Reallocate in-memory options block */
118 if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 )
119 return rc;
120
121 return 0;
122 }
123
124 /**
125 * Load non-volatile stored options from non-volatile storage device
126 *
127 * @v nvo Non-volatile options block
128 * @ret rc Return status code
129 */
nvo_load(struct nvo_block * nvo)130 static int nvo_load ( struct nvo_block *nvo ) {
131 uint8_t *options_data = nvo->dhcpopts.data;
132 int rc;
133
134 /* Skip reading zero-length NVO fields */
135 if ( nvo->len == 0 ) {
136 DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo );
137 return 0;
138 }
139
140 /* Read data */
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 ) );
145 return rc;
146 }
147
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.
151 */
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 ),
155 options_data[0] );
156 memset ( nvo->data, 0, nvo->len );
157 }
158
159 /* Rescan DHCP option block */
160 dhcpopt_update_used_len ( &nvo->dhcpopts );
161
162 DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
163 return 0;
164 }
165
166 /**
167 * Save non-volatile stored options back to non-volatile storage device
168 *
169 * @v nvo Non-volatile options block
170 * @ret rc Return status code
171 */
nvo_save(struct nvo_block * nvo)172 static int nvo_save ( struct nvo_block *nvo ) {
173 uint8_t *checksum = nvo->data;
174 int rc;
175
176 /* Recalculate checksum, if applicable */
177 if ( nvo->len > 0 )
178 *checksum -= nvo_checksum ( nvo );
179
180 /* Write data */
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 ) );
185 return rc;
186 }
187
188 DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo );
189 return 0;
190 }
191
192 /**
193 * Check applicability of NVO setting
194 *
195 * @v settings Settings block
196 * @v setting Setting
197 * @ret applies Setting applies within this settings block
198 */
nvo_applies(struct settings * settings __unused,const struct setting * setting)199 int nvo_applies ( struct settings *settings __unused,
200 const struct setting *setting ) {
201
202 return ( ( setting->scope == NULL ) &&
203 dhcpopt_applies ( setting->tag ) );
204 }
205
206 /**
207 * Store value of NVO setting
208 *
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
214 */
nvo_store(struct settings * settings,const struct setting * setting,const void * data,size_t len)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 );
219 int rc;
220
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 ) );
226 return rc;
227 }
228
229 /* Save updated options to NVS */
230 if ( ( rc = nvo_save ( nvo ) ) != 0 )
231 return rc;
232
233 return 0;
234 }
235
236 /**
237 * Fetch value of NVO setting
238 *
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
244 *
245 * The actual length of the setting will be returned even if
246 * the buffer was too small.
247 */
nvo_fetch(struct settings * settings,struct setting * setting,void * data,size_t len)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 );
252
253 return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len );
254 }
255
256 /** NVO settings operations */
257 static struct settings_operations nvo_settings_operations = {
258 .applies = nvo_applies,
259 .store = nvo_store,
260 .fetch = nvo_fetch,
261 };
262
263 /**
264 * Initialise non-volatile stored options
265 *
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
272 */
nvo_init(struct nvo_block * nvo,struct nvs_device * nvs,size_t address,size_t len,int (* resize)(struct nvo_block * nvo,size_t len),struct refcnt * refcnt)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 ) {
277 nvo->nvs = nvs;
278 nvo->address = address;
279 nvo->len = len;
280 nvo->resize = resize;
281 dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt );
282 settings_init ( &nvo->settings, &nvo_settings_operations,
283 refcnt, NULL );
284 }
285
286 /**
287 * Register non-volatile stored options
288 *
289 * @v nvo Non-volatile options block
290 * @v parent Parent settings block, or NULL
291 * @ret rc Return status code
292 */
register_nvo(struct nvo_block * nvo,struct settings * parent)293 int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
294 int rc;
295
296 /* Allocate memory for options */
297 if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 )
298 goto err_realloc;
299
300 /* Read data from NVS */
301 if ( ( rc = nvo_load ( nvo ) ) != 0 )
302 goto err_load;
303
304 /* Register settings */
305 if ( ( rc = register_settings ( &nvo->settings, parent,
306 NVO_SETTINGS_NAME ) ) != 0 )
307 goto err_register;
308
309 DBGC ( nvo, "NVO %p registered\n", nvo );
310 return 0;
311
312 err_register:
313 err_load:
314 nvo_realloc ( nvo, 0 );
315 err_realloc:
316 return rc;
317 }
318
319 /**
320 * Unregister non-volatile stored options
321 *
322 * @v nvo Non-volatile options block
323 */
unregister_nvo(struct nvo_block * nvo)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 );
328 }
329