Linux中实现了SMBIO内核模块,它是通过/proc文件系统,以一种用户可理解的格式或纯粹的二进制格式来访问SMBIOS结构的信息。sourceforge上有这个内核模块的源代码,地址为http://sourceforge.net/projects/smbios/,是在Linux 2.4内核中的实现,它同时也实现了DMI。注意Linux 2.6中的内核驱动程序模块结构与2.4中的基本相同,只是有一些少许的变化,这里就不展开了。
1、bios.h文件:
(1)头文件中定义搜索起始地址0xF0000,固定字符串"_SM_"和"_DMI_"。
(2)定义SMBIOS EPS表smbios_entry_point_struct,SMBIOS结构头部smbios_struct、DMI的EPS表及结构头部。
(3)定义了模块在proc文件系统中的位置。有两种访问模式,即原始二进制格式(raw),用户可理解格式(cooked),因些访问位置有目录/proc/smbios,/proc/smbios/raw,/proc/smbios/cooked。
(4)定义搜索EPS表的函数smbios_find_entry_point(void*),以及获取结构长度、在proc中创建或删除节点、从proc文件系统中读取原始的或可理解格式的SMBIOS数据,等等。
这里的设计体现了初步的数据封装思想,即把数据结构和操作这些数据结构的函数封装在一个文件中。
这里__attribute__ ((packed))是GCC的扩展,其作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。__attribute__关键字主要是用来在函数或数据声明中设置属性。给函数赋予属性的主要目的在于让编译器进行优化。例如函数声明中的__attribute__((noreturn)),就是告诉编译器这个函数不会返回给调用者,以便编译器在优化时去掉不必要的函数返回代码。__attribute__可以设置函数属性、变量属性和类型属性。书写时要放置在声明的尾部,在分号“;”之前。函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。GCC需要使用–Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。这里的packed属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
2、cooking.h和strgdef.h文件: 由于要以用户可理解的方式显示SMBIOS信息,这需要把原始的SMBIOS结构数据映射到一些可理解的字符串上,以显示给用户看。在cooking.h中定义了各个SMBIOS结构以及解析这些结构的函数。有Type 0-Type 13、Type 16、Type 17、Type 19、Type 20、Type 32、Type 127共20个SMBIOS结构数据。这些结构在SMBIOS规范中都有清晰的描述,定义它们是比较直接的。例如Type 0和Type 1的定义如下:
bios_cook_type_0()等函数就是把解析后的SMBIOS数据写入到/proc文件中。其他的结构就不列举了,在cooking.h中都有。strgdef.h中定义了各结构数据要解析成的友好信息,如结构中各个域的名称、字符串区域中各个字符串的值等,这在SMBIOS规范中也都有清晰的描述,定义时比较直接。例如对Type 0结构的信息,如下:
3、实现文件cooking.c和bios.c: 文件cooking.c中的各个函数,如bios_cook_type_0(),bios_cook_type_1(),是对相应SMBIOS结构数据的解析。各个函数做的工作类似,基本流程如下:
(1)分配足够的空间(char[]型数组file)来存放整个结构的解析数据;
(2)解析结构头部信息,把strgdef.h中的相应信息用strcpy写入到file中;
(3)解析各个字符串信息,把strgdef.h中的相应字符串写入到file中;
(4)用kmalloc分配proc文件的内存,然后把用memcpy把file数组写入到proc文件中。
bios.c主要是实现对proc文件系统的操作,从内存的BIOS区域中(注意SMBIOS数据在BIOS启动时会被载入到内存的BIOS区域中),读取SMBIOS原始数据或解析成可理解的格式,然后写到proc文件系统中,由于proc文件系统在内存中,因此核心的实现就是把读取的数据用memcpy()直接拷贝到内存的proc区域中。实现的函数有:
(1)smbios_find_entry_point(void* base):从base开始搜索SMBIOS EPS表,只要搜索到固定字符串"_SM_"即可;
(2)dmibios_find_entry_point(void* base):从base开始搜索DMI EPS表,只要搜索到固定字符串"_DMI"即可;
(3)smbios_check_entry_point (void *addr):校验EPS表格;
(4)smbios_get_struct_length():返回指定类型的SMBIOS结构长度;
(5)bios_read_raw_proc():当应用程序需要打开本驱动程序创建的proc文件中,本函数就会被内核调用,以获取proc文件中的原始数据;
(6)dmibios_read_raw_proc():跟前一个类似。
(7)bios_read_cooked_proc():跟前面类似,只 不过读取的是友好格式的数据;
(8)smbios_make_version_entry():在proc文件系统中创建smbios_version文件;
(9)smbios_make_dir_entries():在proc文件系统中创建多个文件,每个类型的SMBIOS结构对应一个文件type[-subtype].instance;
(10)smbios_destroy_dir_entries():删除指定proc目录下的所有SMBIOS文件;
(11)smbios_get_readable_name():把SMBIOS类型转换成可理解的格式;
(12)make_file_entries():在指定的proc目录中创建一个文件。
4、main.c文件: 包含本驱动模块的框架性函数。
(1)init_module():映射SMBIOS存储段地址(映射到虚拟地址上)、搜索SMBIOS或DMI的EPS表、映射SMBIOS结构的物理地址、创建目录结点/proc/smbios/raw和/proc/smbios/cooked、在其中创建各个类型的SMBIOS文件。
(2)cleanup_module():删除proc中的各个SMBIOS文件、删除相应目录、解除地址映射。