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數據,等等。
這裏的設計體現了初步的數據封裝思想,即把數據結構和操作這些數據結構的函數封裝在一個文件中。
- /** /文件 bios.h
- * DMI-BIOS和SM-BIOS的原型及聲明
- */
- #ifndef __BIOS_H__
- #define __BIOS_H__
- /*
- * 用來幫助調試的宏
- */
- #undef PDEBUG /* 取消先前的定義(如果有的話),以防萬一 */
- #ifdef _DEBUG_ /* 定義調試宏 */
- # define PDEBUG(fmt, args...) printk( KERN_DEBUG "smbios: " fmt, ## args)
- #else
- # define PDEBUG(fmt, args...) /* 不調試:不做任何事 */
- #endif
- #define BIOS_START_ADDRESS 0xF0000 /* 搜索SM-BIOS和DMI-BIOS的BISO段起始地址 */
- #define BIOS_MAP_LENGTH 0x10000 /* 搜索的BIOS區域長度 */
- #define SMBIOS_MAGIC_DWORD 0x5F4D535F /* 固定字符串 "_SM_" */
- #define DMIBIOS_MAGIC_DWORD 0x494d445f /* 固定字符串 "_DMI" */
- #define DMI_STRING "_DMI_"
- /** 有子類型的SMBIOS結構類型,可擴充! */
- #define TYPES_WITH_SUBTYPES 185, 187, 208, 209, 210, 211, 212, 254
- #define PROC_BLOCK_SIZE (3*1024) /* proc read函數最大的塊大小 */
- /** 模式 原始二進制模式/烹調好的(即用戶可理解模式) */
- #define FILE_MODE_RAW 0
- #define FILE_MODE_COOKED 1
- /* SMBIOS EPS表 */
- typedef struct smbios_entry_point_struct
- {
- __u32 anchor_string __attribute__ ((packed));
- __u8 entry_point_checksum __attribute__ ((packed));
- __u8 entry_point_length __attribute__ ((packed));
- __u8 major_version __attribute__ ((packed));
- __u8 minor_version __attribute__ ((packed));
- __u16 max_struct_size __attribute__ ((packed));
- __u8 revision __attribute__ ((packed));
- __u8 formated_area[5] __attribute__ ((packed));
- __u8 intermediate_string[5] __attribute__ ((packed));
- __u8 intermediate_checksum __attribute__ ((packed));
- __u16 struct_table_length __attribute__ ((packed));
- __u32 struct_table_address __attribute__ ((packed));
- __u16 no_of_structures __attribute__ ((packed));
- __u8 bcd_revision __attribute__ ((packed));
- } smbios_entry_point_struct;
- /** SM-BIOS和DMI-BIOS結構的頭部 */
- typedef struct smbios_struct
- {
- __u8 type __attribute__ ((packed));
- __u8 length __attribute__ ((packed));
- __u16 handle __attribute__ ((packed));
- __u8 subtype __attribute__ ((packed));
- } smbios_struct;
- /** DMI-BIOS結構的頭部 */
- typedef struct dmibios_table_entry_struct
- {
- __u16 size __attribute__ ((packed));
- __u16 handle __attribute__ ((packed));
- __u32 procedure __attribute__ ((packed));
- } dmibios_table_entry_struct;
- /** DMI-BIOS的EPS表 */
- typedef struct dmibios_entry_point_struct
- {
- __u8 signature[10] __attribute__ ((packed));
- __u8 revision __attribute__ ((packed));
- dmibios_table_entry_struct entry[1] __attribute__ ((packed));
- } dmibios_entry_point_struct;
- /*
- * 變量
- */
- extern struct proc_dir_entry * smbios_proc_dir; /* /proc/smbios */
- extern struct proc_dir_entry * smbios_raw_proc_dir; /* /proc/smbios/raw */
- extern struct proc_dir_entry * smbios_cooked_proc_dir; /* /proc/smbios/cooked */
- extern void * smbios_base; /* F-Segment */
- extern smbios_entry_point_struct * smbios_entry_point; /* SMBIOS在F-Segment中的起始地址 */
- extern dmibios_entry_point_struct * dmibios_entry_point; /* DMIBIOS在F-Segment中的起始地址 */
- extern void * smbios_structures_base; /* SMBIOS結構信息的基地址 */
- extern unsigned char smbios_types_with_subtypes[];
- extern char smbios_version_string[32]; /* 支持的SMBIOS版本,例如V2.31 */
- /* 搜索函數 */
- smbios_entry_point_struct * smbios_find_entry_point(void * base);
- dmibios_entry_point_struct * dmibios_find_entry_point(void * base);
- unsigned char smbios_check_entry_point(void * addr);
- int smbios_type_has_subtype(unsigned char type);
- int smbios_get_struct_length(smbios_struct * struct_ptr);
- int dmibios_get_struct_length(smbios_struct * struct_ptr);
- int smbios_version_proc (char *page, char **start, off_t off, int count, int *eof, void *data);
- int bios_read_raw_proc(char * page, char ** start, off_t off, int count, int * eof, void * data);
- int bios_read_cooked_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
- int dmibios_read_raw_proc(char * page, char ** start, off_t off, int count, int * eof, void * data);
- int dmibios_read_cooked_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
- int smbios_make_dir_entries(struct proc_dir_entry *smbiosdir, struct proc_dir_entry *rawdir, struct proc_dir_entry *cookeddir);
- int smbios_make_version_entry(struct proc_dir_entry *smbiosdir);
- int dmibios_make_dir_entries(struct proc_dir_entry * smbiosdir, struct proc_dir_entry * rawdir, struct proc_dir_entry * cookeddir);
- void smbios_destroy_dir_entries(struct proc_dir_entry * dir);
- unsigned int smbios_get_readable_name_ext(char *readable_name, smbios_struct *struct_ptr);
- unsigned int smbios_get_readable_name(char *readable_name, smbios_struct *struct_ptr);
- int make_file_entries (char *filename, struct proc_dir_entry *dir, smbios_struct *struct_ptr, int mode);
- #endif /* __BIOS_H__ */
這裏__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的定義如下:
- /* 解析成可理解的格式 */
- unsigned char * bios_cook_type_0 (smbios_struct * smbiostype, unsigned int *length);
- unsigned char * bios_cook_type_1 (smbios_struct * smbiostype, unsigned int *length);
- typedef struct smbios_type_0
- {
- smbios_header header;
- __u8 vendor __attribute__ ((packed));
- __u8 version __attribute__ ((packed));
- __u16 startaddr __attribute__ ((packed));
- __u8 reldate __attribute__ ((packed));
- __u8 romsize __attribute__ ((packed));
- __u64 characteristics __attribute__ ((packed));
- __u8 ext1 __attribute__ ((packed));
- __u8 ext2 __attribute__ ((packed));
- } smbios_type_0;
- typedef struct smbios_type_1
- {
- smbios_header header;
- __u8 manufacturer __attribute__ ((packed));
- __u8 productname __attribute__ ((packed));
- __u8 version __attribute__ ((packed));
- __u8 serialnumber __attribute__ ((packed));
- __u8 uuid[16] __attribute__ ((packed));
- __u8 wakeuptype __attribute__ ((packed));
- } smbios_type_1;
bios_cook_type_0()等函數就是把解析後的SMBIOS數據寫入到/proc文件中。其他的結構就不列舉了,在cooking.h中都有。strgdef.h中定義了各結構數據要解析成的友好信息,如結構中各個域的名稱、字符串區域中各個字符串的值等,這在SMBIOS規範中也都有清晰的描述,定義時比較直接。例如對Type 0結構的信息,如下:
- /*
- * Type 0 - Bios
- */
- #define TYPE0_NAME "(BIOS Information)"
- #define TYPE0_VENDOR "Vendor"
- #define TYPE0_VERSION "Version"
- #define TYPE0_ADR_SEG "Starting Adr Seg"
- #define TYPE0_REL_DATE "Rel. Date"
- #define TYPE0_ROM_SIZE "ROM Size"
- #define TYPE0_CHAR "Characteristics"
- #define TYPE0_CHAR_RES "Reserved"
- #define TYPE0_CHAR_UNKNOWN "Unknown"
- #define TYPE0_CHAR_NOTSUP "Not Supported"
- #define TYPE0_CHAR_ISA "ISA"
- #define TYPE0_CHAR_MCA "MCA"
- #define TYPE0_CHAR_EISA "EISA"
- #define TYPE0_CHAR_PCI "PCI"
- #define TYPE0_CHAR_PCMCIA "PCMCIA"
- #define TYPE0_CHAR_PNP "Plug and Play"
- #define TYPE0_CHAR_APM "Advanced Power Management"
- #define TYPE0_CHAR_FLASH "Flash"
- #define TYPE0_CHAR_SHADOWING "Shadowing"
- #define TYPE0_CHAR_VL "VL-Vesa"
- #define TYPE0_CHAR_ESCD "ESCD"
- #define TYPE0_CHAR_BOOTCD "Boot from CD"
- #define TYPE0_CHAR_SELBOOT "Selectable Boot"
- #define TYPE0_CHAR_BIOS_IS_SOCKETED "Bios Rom is socketed"
- #define TYPE0_CHAR_PCMCIA_BOOT "Boot from PCMCIA"
- #define TYPE0_CHAR_ENH_DISK_DRIVE "Enhanced Disk Drive"
- #define TYPE0_CHAR_FD_NEC "Int13h - japanese Floppy NEC 1,2 MB"
- #define TYPE0_CHAR_FD_TOSHIBA "Int13h - japanese Floppy Toshiba 1,2 MB"
- #define TYPE0_CHAR_360 "Int13h - 360 kB Floppy"
- #define TYPE0_CHAR_1200 "Int13h - 1,2 MB Floppy"
- #define TYPE0_CHAR_720 "Int13h - 720 kB Floppy"
- #define TYPE0_CHAR_2880 "Int13h - 2,88 MB Floppy"
- #define TYPE0_CHAR_PRINT_SCREEN "Int5h - Print Screen"
- #define TYPE0_CHAR_KEYBOARD "Int9h - 8042 Keyboard"
- #define TYPE0_CHAR_SER_SERVICES "Int14h - Serial Services"
- #define TYPE0_CHAR_PRINT_SERVICES "Int17h - Printer Services"
- #define TYPE0_CHAR_VIDEO_SERVICES "Int10h - CGA,Mono Video Services"
- #define TYPE0_CHAR_PC98 "NEC PC-98"
- #define TYPE0_EXT1_ACPI "ACPI"
- #define TYPE0_EXT1_USB "USB Legacy"
- #define TYPE0_EXT1_AGP "AGP"
- #define TYPE0_EXT1_I2O_BOOT "I2O Boot"
- #define TYPE0_EXT1_LS120 "LS-120"
- #define TYPE0_EXT1_ATAPI_ZIP_BOOT "ATAPI ZIP Boot"
- #define TYPE0_EXT1_1394_BOOT "1394 Boot"
- #define TYPE0_EXT1_SMART_BATTERY "Smart Battery"
- #define TYPE0_EXT2_BBS "Bios Boot Spec."
- #define TYPE0_EXT2_NETWORK_BOOT "Function key initiated Network Service Boot"
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文件: 包含本驅動模塊的框架性函數。
- /** /文件 main.c
- * smbios內核模塊的內核接口函數
- */
- #ifndef __KERNEL__
- # define __KERNEL__
- #endif
- #ifndef MODULE
- # define MODULE
- #endif
- #define __NO_VERSION__ /* 在module.h中不要定義kernel_version */
- #include <linux/module.h>
- #include <linux/version.h>
- char kernel_version[] = UTS_RELEASE;
- #include <linux/kernel.h> /* printk() */
- #include <linux/errno.h> /* 錯誤碼 */
- #include <linux/types.h> /* size_t */
- #include <linux/proc_fs.h>
- #include <asm/io.h> /* ioremap() */
- #include "strgdef.h" /* 所有解析成的字符串定義 */
- #include "bios.h" /* 本地定義 */
- EXPORT_NO_SYMBOLS;
- /** /fn 函數int init_module (void)
- * /brief 模塊初始化,成功返回0,否則返回一個錯誤碼
- */
- int
- init_module (void)
- {
- int err = 0;
- PDEBUG ("starting module initialization/n");
- /*
- * 映射SMBIOS存儲段:ioremap把一個物理地址映射到虛擬地址,例如把BIOS起始地址映射成Bios F-段,
- * 即返回的起始虛擬地址smbios_base
- */
- if (!(smbios_base = ioremap (BIOS_START_ADDRESS, BIOS_MAP_LENGTH)))
- {
- PDEBUG ("ioremap() for entry point failed/n");
- err = -ENXIO;
- goto ioremap_for_entry_point_failed;
- }
- PDEBUG ("BIOS base set to 0x%p/n", smbios_base);
- /*
- * 搜索SMBIOS或DMIBIOS入口點(EPS表起始地址)
- */
- if (!(smbios_entry_point = smbios_find_entry_point (smbios_base)))
- {
- PDEBUG ("SM-BIOS entry point not found/n");
- if (!(dmibios_entry_point = dmibios_find_entry_point (smbios_base)))
- {
- PDEBUG ("DMI-BIOS entry point not found. Aborting.../n");
- err = -ENXIO;
- goto find_entry_point_failed;
- }
- }
- /*
- * 對SM-BIOS:檢查指向DMI結構的指針是否存在。中間字符串_DMI_不是以'/0'結尾,因此strncmp()傳入大小爲sizeof(DMI_STRING)-1
- */
- if (smbios_entry_point)
- {
- if (strncmp((char *) &(smbios_entry_point->intermediate_string),
- DMI_STRING, sizeof (DMI_STRING) - 1))
- {
- PDEBUG ("Pointer to DMI structures not found!/n");
- err = -ENXIO;
- goto check_dmi_failed;
- }
- }
- /*
- * 映射SMBIOS結構的物理地址段,返回的smbios_structures_base包含了SMBIOS結構數據起始地址
- */
- if (smbios_entry_point)
- {
- if (!(smbios_structures_base =
- ioremap (smbios_entry_point->struct_table_address,
- (unsigned long) smbios_entry_point->struct_table_length)))
- {
- PDEBUG ("ioremap() for structures table failed/n");
- err = -ENXIO;
- goto ioremap_for_structures_table_failed;
- }
- }
- /*
- * 另一方面,如果我們有專有的DMI Bios,則smbios_structures_base會包含指向表格的指針,這個表格包含了到每個sm/dmi bios結構的偏移
- */
- if (dmibios_entry_point)
- {
- if (!(smbios_structures_base = dmibios_entry_point->entry))
- {
- PDEBUG ("invalid structure table entry%p,%p/n", smbios_structures_base,
- dmibios_entry_point->entry);
- err = -ENXIO;
- goto ioremap_for_structures_table_failed;
- }
- }
- PDEBUG ("DMI structures base set to 0x%p/n", smbios_structures_base);
- /*
- * 創建/proc節點
- */
- /* 創建/proc/smbios目錄 */
- if (!(smbios_proc_dir =
- create_proc_entry (PROC_DIR_STRING, S_IFDIR, &proc_root)))
- {
- err = -ENOMEM;
- PDEBUG ("failed to create /proc/smbios directory entry/n");
- goto create_smbios_dir_failed;
- }
- PDEBUG ("/proc/smbios directory created./n");
- /* 創建/proc/smbios/raw目錄 */
- if (!(smbios_raw_proc_dir =
- create_proc_entry (PROC_DIR_STRING_RAW, S_IFDIR, smbios_proc_dir)))
- {
- err = -ENOMEM;
- PDEBUG ("failed to create /proc/smbios/raw directory entry/n");
- goto create_smbios_raw_dir_failed;
- }
- PDEBUG ("/proc/smbios/raw directory created./n");
- /* 創建/proc/smbios/cooked目錄 */
- if (!(smbios_cooked_proc_dir =
- create_proc_entry (PROC_DIR_STRING_COOKED, S_IFDIR, smbios_proc_dir)))
- {
- err = -ENOMEM;
- PDEBUG ("failed to create /proc/smbios/cooked directory entry/n");
- goto create_smbios_cooked_dir_failed;
- }
- PDEBUG ("/proc/smbios/cooked directory created./n");
- /* 創建版本文件 */
- if (smbios_entry_point)
- {
- if ((err = smbios_make_version_entry (smbios_proc_dir)))
- goto smbios_make_version_entry_failed;
- }
- /* 創建各個SMBIOS結構對應的文件 */
- if (smbios_entry_point)
- {
- if ((err = smbios_make_dir_entries (smbios_proc_dir, smbios_raw_proc_dir, smbios_cooked_proc_dir)))
- goto make_smbios_dir_entries_failed;
- }
- /* 創建各個DMI BIOS結構對應的文件 */
- if (dmibios_entry_point)
- {
- if ((err = dmibios_make_dir_entries (smbios_proc_dir, smbios_raw_proc_dir, smbios_cooked_proc_dir)))
- goto make_smbios_dir_entries_failed;
- }
- PDEBUG ("module loaded succesfully/n");
- return 0;
- /*
- * 發生錯誤時就會到達這裏,需要對發生錯誤之前做的操作進行回滾處理(如申請了資源,則需要進行釋放)
- */
- make_smbios_dir_entries_failed:
- /* remove /proc/smbios/cooked files */
- smbios_destroy_dir_entries (smbios_cooked_proc_dir);
- /* remove /proc/smbios/raw files */
- smbios_destroy_dir_entries (smbios_raw_proc_dir);
- /* remove /proc/smbios files */
- smbios_destroy_dir_entries (smbios_proc_dir);
- smbios_make_version_entry_failed:
- /* remove /proc/smbios/cooked directory */
- remove_proc_entry(PROC_DIR_STRING_COOKED, smbios_proc_dir);
- create_smbios_cooked_dir_failed :
- /* remove /proc/smbios/raw directory */
- remove_proc_entry(PROC_DIR_STRING_RAW, smbios_proc_dir);
- create_smbios_raw_dir_failed:
- /* remove /proc/smbios directory */
- remove_proc_entry(PROC_DIR_STRING, &proc_root);
- create_smbios_dir_failed:
- /* unmap the virtual to physical memory binding */
- if (smbios_entry_point)
- iounmap (smbios_structures_base);
- ioremap_for_structures_table_failed:
- check_dmi_failed:
- find_entry_point_failed:
- /* unmap the virtual to physical memory binding */
- iounmap (smbios_base);
- ioremap_for_entry_point_failed:
- return err;
- }
- /** /fn 函數int cleanup_module (void)
- * /brief 模塊清理
- */
- void
- cleanup_module (void)
- {
- /* 刪除/proc/smbios/cooked下的各個文件 */
- smbios_destroy_dir_entries (smbios_cooked_proc_dir);
- /* 刪除/proc/smbios/raw下的各個文件 */
- smbios_destroy_dir_entries (smbios_raw_proc_dir);
- /* 刪除/proc/smbios下的文件 */
- smbios_destroy_dir_entries (smbios_proc_dir);
- /* 刪除/proc/smbios/cooked目錄 */
- remove_proc_entry(PROC_DIR_STRING_COOKED, smbios_proc_dir);
- /* 刪除/proc/smbios/raw目錄 */
- remove_proc_entry(PROC_DIR_STRING_RAW, smbios_proc_dir);
- /* 刪除/proc/smbios目錄 */
- remove_proc_entry(PROC_DIR_STRING, &proc_root);
- /* 取消虛擬地址到物理地址的映射 */
- if (smbios_entry_point)
- iounmap (smbios_structures_base);
- iounmap (smbios_base);
- PDEBUG ("module unloaded/n");
- }
(1)init_module():映射SMBIOS存儲段地址(映射到虛擬地址上)、搜索SMBIOS或DMI的EPS表、映射SMBIOS結構的物理地址、創建目錄結點/proc/smbios/raw和/proc/smbios/cooked、在其中創建各個類型的SMBIOS文件。
(2)cleanup_module():刪除proc中的各個SMBIOS文件、刪除相應目錄、解除地址映射。