文档章节

关于lm-sensors中i8k.c的研究

mingkaidox
 mingkaidox
发布于 2013/06/19 10:17
字数 1425
阅读 425
收藏 0

由于使用Dell Vostro,在Arch下看不到风扇的转速(当然更没法控制风扇)。
看了一下,发现是i8k的问题,于是拿来代码研究一下(虽然我是小白……
i8k的代码在:http://khali.linux-fr.org/devel/lm-sensors/drivers/i8k/
包括一个Makefile和一个i8k.c。

$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,在最下面

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,让我们再来看看这个函数

/*
 * 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(&regs) ? : regs.eax;
}
这个函数里面设置了一个映射寄存器的struct,其中eax为 I8K_SMM_BIOS_VERSION常量,值为0x00A6(文件头上有),之后调用了i8k_smm(&regs)函数,如果i8k_smm(&regs)返回0则返回regs.eax。
继续看i8k_smm(&regs)
/*
 * 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(/* asm code */)的用法,具体可以看这里
大概就是下面这样:
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

© 著作权归作者所有

共有 人打赏支持
mingkaidox
粉丝 12
博文 26
码字总数 8887
作品 0
闵行
程序员
私信 提问
linux下监控cpu温度

最近电脑的风扇总是偷停,所以需要随时监控cpu温度。要在linux监控cpu需要用lm_sensors,在KDE中推荐使用ksensors。 首先emerge ksensors会依依赖关系一起安装lm_sensors 要确保使用2.4以上内...

红薯
2009/03/01
1K
0
LM_Sensors 3.2.0 发布

LM_Sensors 3.2.0发布 好久没有有关LM_Sensors项目的报道了。LM_Sensors是一项自由软件项目,致力于提供户空间组件和多种硬件传感器的Linux下的内核驱动。LM_Sensors让监视CUP/系统温度,风扇...

xyxzfj
2010/10/11
346
0
CentOS安装lm_sensors温度监控

CentOS安装lm_sensors温度监控 注意,本文为64位操作系统下的实例,可根据操作系统的版本下载相应的版本文件。 CentOS 安装lm_sensors 温度监控 首先查看是否安装,系统默认安装了 #rpm -qa...

IT_小翼
2015/01/19
0
0
CentOS 安装lm_sensors 温度监控

CentOS 安装lmsensors 温度监控 首先查看是否安装,系统默认安装了 #rpm -qa|grep sensors lmsensors-devel-3.1.1-17.el6.x8664 lmsensors-libs-3.1.1-17.el6.x8664 lmsensors-3.1.1-17.el6.......

sosg
2013/10/16
0
0
谷歌开源大规模语言建模库,探索RNN极限

近日,谷歌宣布开源大规模语言建模模型库,这项名为“探索RNN极限”的研究今年2月发表时就引发激论,如今姗姗来迟的开源更加引人瞩目。研究测试取得了极好的成绩,另外开源的数据库含有大约1...

王练
2016/09/17
2.8K
3

没有更多内容

加载失败,请刷新页面

加载更多

80后阿里P10,“关老板”如何带着MaxCompute一路升级?

我是个幸运的人。虽然幸运不能被复制,但是眼光和努力可以。 关涛/关老板,80后的阿里P10,阿里巴巴通用计算平台负责人,阿里巴巴计算平台研究员。12年职场人生,微软和阿里的选择。 关涛的花...

阿里云官方博客
18分钟前
0
0
开源软件和开源模式面临的生存危机

开源模式可能正面临一场危机。越来越多的开源软件和平台被大型云计算服务商融入自家的云服务体系,并以此获利颇丰,但并不支付费用,也没有对开源社区做出相应的回馈。而实际上,大部分开源软...

Linux就该这么学
18分钟前
0
0
统一服务消息返回错误:{"errcode":40165,"errmsg":"invalid weapp pagepath hint: [bsAWua0201ge30]"}

{"errcode":40165,"errmsg":"invalid weapp pagepath hint: [bsAWua0201ge30]"} 原因:pagepath参数为所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar), 以前配置的是:m...

tianma3798
20分钟前
0
0
ElasticSearch实战:Linux日志对接Kibana

本文由云+社区发表 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTFul web接口。ElasticSearch是用Java开发的,并作为Apache许可条款下...

腾讯云加社区
23分钟前
0
0
FeignClient超时配置

1前沿 使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间 1.1ribbon配置 ribbon: OkToRetryOnAllOperations: f...

lovelan1314
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部