Basic non-volatile storage support
[people/xl0/gpxe.git] / src / core / nvs.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
24 /** @file
25  *
26  * Non-volatile storage
27  *
28  */
29
30 static size_t nvs_options_len ( struct nvs_device *nvs ) {
31         struct dhcp_option *option;
32         uint8_t sum;
33         unsigned int i;
34         size_t len;
35
36         for ( sum = 0, i = 0 ; i < nvs->len ; i++ ) {
37                 sum += * ( ( uint8_t * ) ( nvs->options->data + i ) );
38         }
39         if ( sum != 0 ) {
40                 DBG ( "NVS %p has bad checksum %02x; assuming empty\n",
41                       nvs, sum );
42                 return 0;
43         }
44
45         option = nvs->options->data;
46         if ( option->tag == DHCP_PAD ) {
47                 DBG ( "NVS %p has bad start; assuming empty\n", nvs );
48                 return 0;
49         }
50         
51         option = find_dhcp_option ( nvs->options, DHCP_END );
52         if ( ! option ) {
53                 DBG ( "NVS %p has no end tag; assuming empty\n", nvs );
54                 return 0;
55         }
56
57         len = ( ( void * ) option - nvs->options->data + 1 );
58         DBG ( "NVS %p contains %zd bytes of options (maximum %zd)\n",
59               nvs, len, nvs->len );
60
61         return len;
62 }
63
64 int nvs_register ( struct nvs_device *nvs ) {
65         struct dhcp_option *option;
66         int rc;
67
68         nvs->options = alloc_dhcp_options ( nvs->len );
69         if ( ! nvs->options ) {
70                 DBG ( "NVS %p could not allocate %zd bytes\n", nvs, nvs->len );
71                 rc = -ENOMEM;
72                 goto err;
73         }
74
75         if ( ( rc = nvs->op->read ( nvs, 0, nvs->options->data,
76                                     nvs->len ) ) != 0 ) {
77                 DBG ( "NVS %p could not read [0,%zd)\n", nvs, nvs->len );
78                 goto err;
79         }
80
81         nvs->options->len = nvs->options->max_len;
82         nvs->options->len = nvs_options_len ( nvs );
83         if ( ! nvs->options->len ) {
84                 option = nvs->options->data;
85                 option->tag = DHCP_END;
86                 nvs->options->len = 1;
87         }
88
89         register_dhcp_options ( nvs->options );
90
91         return 0;
92         
93  err:
94         
95         free_dhcp_options ( nvs->options );
96         nvs->options = NULL;
97         return rc;
98 }
99
100 void nvs_unregister ( struct nvs_device *nvs ) {
101         if ( nvs->options ) {
102                 unregister_dhcp_options ( nvs->options );
103                 free_dhcp_options ( nvs->options );
104                 nvs->options = NULL;
105         }
106 }