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