You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smbios.c 6.9KB

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