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