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., 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 | } |
---|