Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / interface / smbios / smbios.c
1 /*
2  * Copyright (C) 2007 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
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stdint.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <ipxe/uaccess.h>
27 #include <ipxe/smbios.h>
28
29 /** @file
30  *
31  * System Management BIOS
32  *
33  */
34
35 /** SMBIOS entry point descriptor */
36 static struct smbios smbios = {
37         .address = UNULL,
38 };
39
40 /**
41  * Scan for SMBIOS entry point structure
42  *
43  * @v start             Start address of region to scan
44  * @v len               Length of region to scan
45  * @v entry             SMBIOS entry point structure to fill in
46  * @ret rc              Return status code
47  */
48 int find_smbios_entry ( userptr_t start, size_t len,
49                         struct smbios_entry *entry ) {
50         uint8_t buf[256]; /* 256 is maximum length possible */
51         static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
52         size_t entry_len;
53         unsigned int i;
54         uint8_t sum;
55
56         /* Try to find SMBIOS */
57         for ( ; offset < len ; offset += 0x10 ) {
58
59                 /* Read start of header and verify signature */
60                 copy_from_user ( entry, start, offset, sizeof ( *entry ) );
61                 if ( entry->signature != SMBIOS_SIGNATURE )
62                         continue;
63
64                 /* Read whole header and verify checksum */
65                 entry_len = entry->len;
66                 assert ( entry_len <= sizeof ( buf ) );
67                 copy_from_user ( buf, start, offset, entry_len );
68                 for ( i = 0, sum = 0 ; i < entry_len ; i++ ) {
69                         sum += buf[i];
70                 }
71                 if ( sum != 0 ) {
72                         DBG ( "SMBIOS at %08lx has bad checksum %02x\n",
73                               user_to_phys ( start, offset ), sum );
74                         continue;
75                 }
76
77                 /* Fill result structure */
78                 DBG ( "Found SMBIOS v%d.%d entry point at %08lx\n",
79                       entry->major, entry->minor,
80                       user_to_phys ( start, offset ) );
81                 return 0;
82         }
83
84         DBG ( "No SMBIOS found\n" );
85         return -ENODEV;
86 }
87
88 /**
89  * Find SMBIOS strings terminator
90  *
91  * @v offset            Offset to start of strings
92  * @ret offset          Offset to strings terminator, or 0 if not found
93  */
94 static size_t find_strings_terminator ( size_t offset ) {
95         size_t max_offset = ( smbios.len - 2 );
96         uint16_t nulnul;
97
98         for ( ; offset <= max_offset ; offset++ ) {
99                 copy_from_user ( &nulnul, smbios.address, offset, 2 );
100                 if ( nulnul == 0 )
101                         return ( offset + 1 );
102         }
103         return 0;
104 }
105
106 /**
107  * Find specific structure type within SMBIOS
108  *
109  * @v type              Structure type to search for
110  * @v instance          Instance of this type of structure
111  * @v structure         SMBIOS structure descriptor to fill in
112  * @ret rc              Return status code
113  */
114 int find_smbios_structure ( unsigned int type, unsigned int instance,
115                             struct smbios_structure *structure ) {
116         unsigned int count = 0;
117         size_t offset = 0;
118         size_t strings_offset;
119         size_t terminator_offset;
120         int rc;
121
122         /* Find SMBIOS */
123         if ( ( smbios.address == UNULL ) &&
124              ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
125                 return rc;
126         assert ( smbios.address != UNULL );
127
128         /* Scan through list of structures */
129         while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len )
130                 && ( count < smbios.count ) ) {
131
132                 /* Read next SMBIOS structure header */
133                 copy_from_user ( &structure->header, smbios.address, offset,
134                                  sizeof ( structure->header ) );
135
136                 /* Determine start and extent of strings block */
137                 strings_offset = ( offset + structure->header.len );
138                 if ( strings_offset > smbios.len ) {
139                         DBG ( "SMBIOS structure at offset %zx with length "
140                               "%x extends beyond SMBIOS\n", offset,
141                               structure->header.len );
142                         return -ENOENT;
143                 }
144                 terminator_offset = find_strings_terminator ( strings_offset );
145                 if ( ! terminator_offset ) {
146                         DBG ( "SMBIOS structure at offset %zx has "
147                               "unterminated strings section\n", offset );
148                         return -ENOENT;
149                 }
150                 structure->strings_len = ( terminator_offset - strings_offset);
151
152                 DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
153                       "strings length %zx\n", offset, structure->header.type,
154                       structure->header.len, structure->strings_len );
155
156                 /* If this is the structure we want, return */
157                 if ( ( structure->header.type == type ) &&
158                      ( instance-- == 0 ) ) {
159                         structure->offset = offset;
160                         return 0;
161                 }
162
163                 /* Move to next SMBIOS structure */
164                 offset = ( terminator_offset + 1 );
165                 count++;
166         }
167
168         DBG ( "SMBIOS structure type %d not found\n", type );
169         return -ENOENT;
170 }
171
172 /**
173  * Copy SMBIOS structure
174  *
175  * @v structure         SMBIOS structure descriptor
176  * @v data              Buffer to hold SMBIOS structure
177  * @v len               Length of buffer
178  * @ret rc              Return status code
179  */
180 int read_smbios_structure ( struct smbios_structure *structure,
181                             void *data, size_t len ) {
182
183         assert ( smbios.address != UNULL );
184
185         if ( len > structure->header.len )
186                 len = structure->header.len;
187         copy_from_user ( data, smbios.address, structure->offset, len );
188         return 0;
189 }
190
191 /**
192  * Find indexed string within SMBIOS structure
193  *
194  * @v structure         SMBIOS structure descriptor
195  * @v index             String index
196  * @v data              Buffer for string
197  * @v len               Length of string buffer
198  * @ret rc              Length of string, or negative error
199  */
200 int read_smbios_string ( struct smbios_structure *structure,
201                          unsigned int index, void *data, size_t len ) {
202         size_t strings_start = ( structure->offset + structure->header.len );
203         size_t strings_end = ( strings_start + structure->strings_len );
204         size_t offset;
205         size_t string_len;
206
207         assert ( smbios.address != UNULL );
208
209         /* String numbers start at 1 (0 is used to indicate "no string") */
210         if ( ! index )
211                 return -ENOENT;
212
213         for ( offset = strings_start ; offset < strings_end ;
214               offset += ( string_len + 1 ) ) {
215                 /* Get string length.  This is known safe, since the
216                  * smbios_strings struct is constructed so as to
217                  * always end on a string boundary.
218                  */
219                 string_len = strlen_user ( smbios.address, offset );
220                 if ( --index == 0 ) {
221                         /* Copy string, truncating as necessary. */
222                         if ( len > string_len )
223                                 len = string_len;
224                         copy_from_user ( data, smbios.address, offset, len );
225                         return string_len;
226                 }
227         }
228
229         DBG ( "SMBIOS string index %d not found\n", index );
230         return -ENOENT;
231 }
232
233 /**
234  * Get SMBIOS version
235  *
236  * @ret version         Version, or negative error
237  */
238 int smbios_version ( void ) {
239         int rc;
240
241         /* Find SMBIOS */
242         if ( ( smbios.address == UNULL ) &&
243              ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
244                 return rc;
245         assert ( smbios.address != UNULL );
246
247         return smbios.version;
248 }