因爲使用Dell Vostro,在Arch下看不到風扇的轉速(固然更無法控制風扇)。
看了一下,發現是i8k的問題,因而拿來代碼研究一下(雖然我是小白……
i8k的代碼在:http://khali.linux-fr.org/devel/lm-sensors/drivers/i8k/
包括一個Makefile和一個i8k.c。 html
$make $sudo insmod i8k.ko固然以後確定是不能用的(要否則我也不折騰了*_*
$dmesg | grep i8k [ 3527.390324] i8k: unable to get SMM BIOS version
緣由是無法獲取SMM的BIOS版本信息。下面打開i8k.c文件
首先找module_init和module_exit,在最下面 linux
module_init(i8k_init); module_exit(i8k_exit);而後按照只是看init和exit函數(其實主要是看init)
static int __init i8k_init(void) { struct proc_dir_entry *proc_i8k; int err; /* Are we running on an supported laptop? */ if (i8k_probe()) return -ENODEV; /* Register the proc entry */ proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops); if (!proc_i8k) return -ENOENT; err = i8k_init_hwmon(); if (err) goto exit_remove_proc; printk(KERN_INFO "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", I8K_VERSION); return 0; exit_remove_proc: remove_proc_entry("i8k", NULL); return err; } static void __exit i8k_exit(void) { i8k_exit_hwmon(); remove_proc_entry("i8k", NULL); }
/* * Probe for the presence of a supported laptop. */ static int __init i8k_probe(void) { char buff[4]; int version; /* * Get DMI information */ if (!dmi_check_system(i8k_dmi_table)) { if (!ignore_dmi && !force) return -ENODEV; printk(KERN_INFO "i8k: not running on a supported Dell system.\n"); printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n", i8k_get_dmi_data(DMI_SYS_VENDOR), i8k_get_dmi_data(DMI_PRODUCT_NAME), i8k_get_dmi_data(DMI_BIOS_VERSION)); } strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version)); /* * Get SMM Dell signature */ if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { printk(KERN_ERR "i8k: unable to get SMM Dell signature\n"); if (!force) return -ENODEV; } /* * Get SMM BIOS version. */ version = i8k_get_bios_version(); if (version <= 0) { //##########Here is the problem! printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n"); } else { buff[0] = (version >> 16) & 0xff; buff[1] = (version >> 8) & 0xff; buff[2] = (version) & 0xff; buff[3] = '\0'; /* * If DMI BIOS version is unknown use SMM BIOS version. */ if (!dmi_get_system_info(DMI_BIOS_VERSION)) strlcpy(bios_version, buff, sizeof(bios_version)); /* * Check if the two versions match. */ if (strncmp(buff, bios_version, sizeof(bios_version)) != 0) printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n", buff, bios_version); } return 0; }
找到了剛剛輸出的錯誤字符串,因而問題在於i8k_get_bios_version()的返回值<=0,讓咱們再來看看這個函數
ios
/* * Read the bios version. Return the version as an integer corresponding * to the ascii value, for example "A17" is returned as 0x00413137. */ static int i8k_get_bios_version(void) { struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, }; return i8k_smm(®s) ? : regs.eax; }這個函數裏面設置了一個映射寄存器的struct,其中eax爲 I8K_SMM_BIOS_VERSION常量,值爲0x00A6(文件頭上有),以後調用了i8k_smm(®s)函數,若是i8k_smm(®s)返回0則返回regs.eax。
/* * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. */ static int i8k_smm(struct smm_regs *regs) { int rc; int eax = regs->eax; #if defined(CONFIG_X86_64) asm volatile("pushq %%rax\n\t" "movl 0(%%rax),%%edx\n\t" "pushq %%rdx\n\t" "movl 4(%%rax),%%ebx\n\t" "movl 8(%%rax),%%ecx\n\t" "movl 12(%%rax),%%edx\n\t" "movl 16(%%rax),%%esi\n\t" "movl 20(%%rax),%%edi\n\t" "popq %%rax\n\t" "out %%al,$0xb2\n\t" "out %%al,$0x84\n\t" "xchgq %%rax,(%%rsp)\n\t" "movl %%ebx,4(%%rax)\n\t" "movl %%ecx,8(%%rax)\n\t" "movl %%edx,12(%%rax)\n\t" "movl %%esi,16(%%rax)\n\t" "movl %%edi,20(%%rax)\n\t" "popq %%rdx\n\t" "movl %%edx,0(%%rax)\n\t" "pushfq\n\t" "popq %%rax\n\t" "andl $1,%%eax\n" :"=a"(rc) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #else asm volatile("pushl %%eax\n\t" "movl 0(%%eax),%%edx\n\t" "push %%edx\n\t" "movl 4(%%eax),%%ebx\n\t" "movl 8(%%eax),%%ecx\n\t" "movl 12(%%eax),%%edx\n\t" "movl 16(%%eax),%%esi\n\t" "movl 20(%%eax),%%edi\n\t" "popl %%eax\n\t" "out %%al,$0xb2\n\t" "out %%al,$0x84\n\t" "xchgl %%eax,(%%esp)\n\t" "movl %%ebx,4(%%eax)\n\t" "movl %%ecx,8(%%eax)\n\t" "movl %%edx,12(%%eax)\n\t" "movl %%esi,16(%%eax)\n\t" "movl %%edi,20(%%eax)\n\t" "popl %%edx\n\t" "movl %%edx,0(%%eax)\n\t" "lahf\n\t" "shrl $8,%%eax\n\t" "andl $1,%%eax\n" :"=a"(rc) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #endif if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) return -EINVAL; return 0; }這一段嵌入了彙編。大概是按照64位仍是32位彙編有兩種版本,雖然我機器是64位,可是感受跟32位彙編比較眼熟,因此分下32位的代碼(兩種版本彙編作的事情確定是同樣的。。
asm ("movl %%eax, %%ebx\n\t" /* some other asm code */ : "a=" (c_var_for_output) //optional : "a" (c_var_for_input) //optional : "%eax", "%ebx" //registers involved );彙編裏面的寄存器要用兩個%,爲了跟輸入參數和輸出的佔位符分開(輸入和輸出用%0,%1,%2...等表示,具體請看上面的資料)。
c_var_for_input和c_var_for_output是asm與c的交互變量,這裏a的意思是值被放在%eax裏面/從%eax裏面讀出,能夠把c變量傳進去傳出來,最後那一行是涉及道的寄存器,若是涉及內存用"memory"表示。
這與那段彙編碼在幹嗎,我看來大概是這樣:
把regs傳進去,設置好各類寄存器以後,把其中的%eax(也就是0x00A6)用out指令給$0xb2,這個$0xb2貌似是SMM(System Management Mode)中用來查找smm_handler的,可是下面的$0x84就不知道是幹什麼的了,google了很久也沒結果,之好先跳過。
調用了兩個out以後,貌似還獲得了個返回值,是在%eax裏面,以後把這個返回值寫到了regs的.eax裏面。
彙編碼以後,檢查返回值rc,regs->eax是否爲0xffff,而且檢查其是否與傳進來的時候發生了變化,當且僅當rc!=0,regs->eax變了而且不是0xffff的時候才返回成功,不然說獲取失敗……
我這裏運行出來regs->eax和執行asm以前是同樣的,因此問題又回到了asm到底作了什麼……可是因爲找不到$0x84的做用,因此只能暫時到這裏了。
昨天晚上就研究到這裏,曾經改了幾回,電腦也崩潰了幾回- -
算了仍是去複習吧,明天又考試了……
(第一篇oschina上的blog,沒想到有這麼長(噓——我纔不說都是代碼撐的長度),昨天晚上躺在牀上忽然想起oschina這網站,有blog和git託管,幸福死了(*^__^*) ……
MinGKai
June 19th, 2013 git