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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 FILE_LICENCE ( GPL2_OR_LATER );
20 
21 #include <stdint.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <gpxe/uaccess.h>
26 #include <gpxe/smbios.h>
27 
28 /** @file
29  *
30  * System Management BIOS
31  *
32  */
33 
34 /** SMBIOS entry point descriptor */
35 static struct smbios smbios = {
36 	.address = UNULL,
37 };
38 
39 /**
40  * Find SMBIOS strings terminator
41  *
42  * @v offset		Offset to start of strings
43  * @ret offset		Offset to strings terminator, or 0 if not found
44  */
find_strings_terminator(size_t offset)45 static size_t find_strings_terminator ( size_t offset ) {
46 	size_t max_offset = ( smbios.len - 2 );
47 	uint16_t nulnul;
48 
49 	for ( ; offset <= max_offset ; offset++ ) {
50 		copy_from_user ( &nulnul, smbios.address, offset, 2 );
51 		if ( nulnul == 0 )
52 			return ( offset + 1 );
53 	}
54 	return 0;
55 }
56 
57 /**
58  * Find specific structure type within SMBIOS
59  *
60  * @v type		Structure type to search for
61  * @v structure		SMBIOS structure descriptor to fill in
62  * @ret rc		Return status code
63  */
find_smbios_structure(unsigned int type,struct smbios_structure * structure)64 int find_smbios_structure ( unsigned int type,
65 			    struct smbios_structure *structure ) {
66 	unsigned int count = 0;
67 	size_t offset = 0;
68 	size_t strings_offset;
69 	size_t terminator_offset;
70 	int rc;
71 
72 	/* Find SMBIOS */
73 	if ( ( smbios.address == UNULL ) &&
74 	     ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
75 		return rc;
76 	assert ( smbios.address != UNULL );
77 
78 	/* Scan through list of structures */
79 	while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len )
80 		&& ( count < smbios.count ) ) {
81 
82 		/* Read next SMBIOS structure header */
83 		copy_from_user ( &structure->header, smbios.address, offset,
84 				 sizeof ( structure->header ) );
85 
86 		/* Determine start and extent of strings block */
87 		strings_offset = ( offset + structure->header.len );
88 		if ( strings_offset > smbios.len ) {
89 			DBG ( "SMBIOS structure at offset %zx with length "
90 			      "%x extends beyond SMBIOS\n", offset,
91 			      structure->header.len );
92 			return -ENOENT;
93 		}
94 		terminator_offset = find_strings_terminator ( strings_offset );
95 		if ( ! terminator_offset ) {
96 			DBG ( "SMBIOS structure at offset %zx has "
97 			      "unterminated strings section\n", offset );
98 			return -ENOENT;
99 		}
100 		structure->strings_len = ( terminator_offset - strings_offset);
101 
102 		DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
103 		      "strings length %zx\n", offset, structure->header.type,
104 		      structure->header.len, structure->strings_len );
105 
106 		/* If this is the structure we want, return */
107 		if ( structure->header.type == type ) {
108 			structure->offset = offset;
109 			return 0;
110 		}
111 
112 		/* Move to next SMBIOS structure */
113 		offset = ( terminator_offset + 1 );
114 		count++;
115 	}
116 
117 	DBG ( "SMBIOS structure type %d not found\n", type );
118 	return -ENOENT;
119 }
120 
121 /**
122  * Copy SMBIOS structure
123  *
124  * @v structure		SMBIOS structure descriptor
125  * @v data		Buffer to hold SMBIOS structure
126  * @v len		Length of buffer
127  * @ret rc		Return status code
128  */
read_smbios_structure(struct smbios_structure * structure,void * data,size_t len)129 int read_smbios_structure ( struct smbios_structure *structure,
130 			    void *data, size_t len ) {
131 
132 	assert ( smbios.address != UNULL );
133 
134 	if ( len > structure->header.len )
135 		len = structure->header.len;
136 	copy_from_user ( data, smbios.address, structure->offset, len );
137 	return 0;
138 }
139 
140 /**
141  * Find indexed string within SMBIOS structure
142  *
143  * @v structure		SMBIOS structure descriptor
144  * @v index		String index
145  * @v data		Buffer for string
146  * @v len		Length of string buffer
147  * @ret rc		Length of string, or negative error
148  */
read_smbios_string(struct smbios_structure * structure,unsigned int index,void * data,size_t len)149 int read_smbios_string ( struct smbios_structure *structure,
150 			 unsigned int index, void *data, size_t len ) {
151 	size_t strings_start = ( structure->offset + structure->header.len );
152 	size_t strings_end = ( strings_start + structure->strings_len );
153 	size_t offset;
154 	size_t string_len;
155 
156 	assert ( smbios.address != UNULL );
157 
158 	/* String numbers start at 1 (0 is used to indicate "no string") */
159 	if ( ! index )
160 		return -ENOENT;
161 
162 	for ( offset = strings_start ; offset < strings_end ;
163 	      offset += ( string_len + 1 ) ) {
164 		/* Get string length.  This is known safe, since the
165 		 * smbios_strings struct is constructed so as to
166 		 * always end on a string boundary.
167 		 */
168 		string_len = strlen_user ( smbios.address, offset );
169 		if ( --index == 0 ) {
170 			/* Copy string, truncating as necessary. */
171 			if ( len > string_len )
172 				len = string_len;
173 			copy_from_user ( data, smbios.address, offset, len );
174 			return string_len;
175 		}
176 	}
177 
178 	DBG ( "SMBIOS string index %d not found\n", index );
179 	return -ENOENT;
180 }
181