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