Added quick and dirty commands for testing the new NVO code.
[people/mcb30/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 <string.h>
21 #include <gpxe/dhcp.h>
22 #include <gpxe/nvs.h>
23 #include <gpxe/nvo.h>
24
25 /** @file
26  *
27  * Non-volatile stored options
28  *
29  */
30
31 #warning "Temporary hack"
32 struct nvo_block *ugly_nvo_hack = NULL;
33
34 /**
35  * Calculate checksum over non-volatile stored options
36  *
37  * @v nvo               Non-volatile options block
38  * @ret sum             Checksum
39  */
40 static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
41         uint8_t *data = nvo->options->data;
42         uint8_t sum = 0;
43         unsigned int i;
44
45         for ( i = 0 ; i < nvo->total_len ; i++ ) {
46                 sum += *(data++);
47         }
48         return sum;
49 }
50
51 /**
52  * Load non-volatile stored options from non-volatile storage device
53  *
54  * @v nvo               Non-volatile options block
55  * @ret rc              Return status code
56  */
57 static int nvo_load ( struct nvo_block *nvo ) {
58         void *data = nvo->options->data;
59         struct nvo_fragment *fragment;
60         int rc;
61
62         /* Read data a fragment at a time */
63         for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
64                 if ( ( rc = nvs_read ( nvo->nvs, fragment->address,
65                                        data, fragment->len ) ) != 0 ) {
66                         DBG ( "NVO %p could not read %zd bytes at %#04x\n",
67                               nvo, fragment->len, fragment->address );
68                         return rc;
69                 }
70                 data += fragment->len;
71         }
72         
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 int nvo_save ( struct nvo_block *nvo ) {
83         void *data = nvo->options->data;
84         uint8_t *checksum = ( data + nvo->total_len - 1 );
85         struct nvo_fragment *fragment;
86         int rc;
87
88         /* Recalculate checksum */
89         *checksum -= nvo_checksum ( nvo );
90
91         /* Write data a fragment at a time */
92         for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
93                 if ( ( rc = nvs_write ( nvo->nvs, fragment->address,
94                                         data, fragment->len ) ) != 0 ) {
95                         DBG ( "NVO %p could not write %zd bytes at %#04x\n",
96                               nvo, fragment->len, fragment->address );
97                         return rc;
98                 }
99                 data += fragment->len;
100         }
101         
102         return 0;
103 }
104
105 /**
106  * Parse stored options
107  *
108  * @v nvo               Non-volatile options block
109  *
110  * Verifies that the options data is valid, and configures the DHCP
111  * options block.  If the data is not valid, it is replaced with an
112  * empty options block.
113  */
114 static void nvo_init_dhcp ( struct nvo_block *nvo ) {
115         struct dhcp_option_block *options = nvo->options;
116         struct dhcp_option *option;
117
118         /* Steal one byte for the checksum */
119         options->max_len = ( nvo->total_len - 1 );
120
121         /* Verify checksum over whole block */
122         if ( nvo_checksum ( nvo ) != 0 ) {
123                 DBG ( "NVO %p has bad checksum %02x; assuming empty\n",
124                       nvo, nvo_checksum ( nvo ) );
125                 goto empty;
126         }
127
128         /* Check that we don't just have a block full of zeroes */
129         option = options->data;
130         if ( option->tag == DHCP_PAD ) {
131                 DBG ( "NVO %p has bad start; assuming empty\n", nvo );
132                 goto empty;
133         }
134         
135         /* Search for the DHCP_END tag */
136         options->len = options->max_len;
137         option = find_dhcp_option ( options, DHCP_END );
138         if ( ! option ) {
139                 DBG ( "NVO %p has no end tag; assuming empty\n", nvo );
140                 goto empty;
141         }
142
143         /* Set correct length of DHCP options */
144         options->len = ( ( void * ) option - options->data + 1 );
145         DBG ( "NVO %p contains %zd bytes of options (maximum %zd)\n",
146               nvo, options->len, options->max_len );
147         return;
148
149  empty:
150         /* No options found; initialise an empty options block */
151         option = options->data;
152         option->tag = DHCP_END;
153         options->len = 1;
154         return;
155 }
156
157 /**
158  * Register non-volatile stored options
159  *
160  * @v nvo               Non-volatile options block
161  * @ret rc              Return status code
162  */
163 int nvo_register ( struct nvo_block *nvo ) {
164         struct nvo_fragment *fragment = nvo->fragments;
165         int rc;
166
167         /* Calculate total length of all fragments */
168         nvo->total_len = 0;
169         for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
170                 nvo->total_len += fragment->len;
171         }
172
173         /* Allocate memory for options and read in from NVS */
174         nvo->options = alloc_dhcp_options ( nvo->total_len );
175         if ( ! nvo->options ) {
176                 DBG ( "NVO %p could not allocate %zd bytes\n",
177                       nvo, nvo->total_len );
178                 rc = -ENOMEM;
179                 goto err;
180         }
181         if ( ( rc = nvo_load ( nvo ) ) != 0 )
182                 goto err;
183
184         /* Verify and register options */
185         nvo_init_dhcp ( nvo );
186         register_dhcp_options ( nvo->options );
187
188         ugly_nvo_hack = nvo;
189
190         return 0;
191         
192  err:
193         free_dhcp_options ( nvo->options );
194         nvo->options = NULL;
195         return rc;
196 }
197
198 /**
199  * Unregister non-volatile stored options
200  *
201  * @v nvo               Non-volatile options block
202  */
203 void nvo_unregister ( struct nvo_block *nvo ) {
204         if ( nvo->options ) {
205                 unregister_dhcp_options ( nvo->options );
206                 free_dhcp_options ( nvo->options );
207                 nvo->options = NULL;
208         }
209
210         ugly_nvo_hack = NULL;
211 }