敬请关注微信公众号:“固件C字营”
=====================================================================
最近遇到一个奇怪的问题,大概就是BIOS在POST过程读写EC,非常小概率会失效。经测试发现失效的概率跟读写频率,读写速度相关。
于是乎,左手改EC代码,在相关的代码插桩加入log信息,右手分析祖传的BIOS代码,同样是打桩和加log,用终端来监视读/写过程中的异常,左右开弓忙的不亦乐乎。需要说明的是Cstyle为BIOS攻城狮,以下代码片段皆来自N多年以前的UEFI/BIOS旧项目。
“祖传代码”之所以称之为祖传代码是因为大多数BIOS工程师都非常之忙,被追杀、被投诉的戏码屡屡上演如:被LD催,被PM用18米大刀追、被测试嫌弃Bug写的太多,被兄弟部门投诉解Bub速度太慢等等,岂止是苦逼、简直就是苦逼,因此本着快、准、狠,及不重复造轮子的最高指导原则,能C+V的绝不自己动手。
言归正传,经过“坚持不懈”的填坑,终于.终于.终于,在数百万行代码里面(虽然99.9%都不是我们自己写的!!!)发现下面这段代码看着不太顺眼,仔细追查了一下之后发现这段代码的历史由来已久,应该是祖传无疑,此“码”远远看着就散发着一股子“渣男”气质,应该说是渣男中的渣男,必须除之而后快。
//祖传代码片段//
//
// Read EC version from 62/66 port
//
EFI_STATUS Status;
UINT8 MaxVerByte;
UINT8 VerByte;
UINT8 *pChar;
UINT8 VerValue[10];
MaxVerByte = 10;
pChar = VerValue;
for (VerByte = 0; VerByte <=MaxVerByte; VerByte++)
{
Status = WaitKbcIbe (EC_C_PORT);
if (Status == EFI_SUCCESS)
{
Status = WriteKbc (EC_C_PORT, 0x80);
}
Status = WaitKbcIbe (EC_C_PORT);
if (Status == EFI_SUCCESS)
{
Status = WriteKbc (EC_D_PORT, 0xA0 + VerByte);
}
Status = WaitKbcObf (EC_C_PORT);
if (Status == EFI_SUCCESS)
{
ReadKbc (EC_D_PORT, pChar);
}
pChar++;
}
注:以上代码已经过脱敏处理。
前文介绍了“祖传代码”带来的噩梦,现介绍一个0.1版本的修正版本,供参考。此修正能解决部分问题,主要解决了数据读写失败后的异常处理。
//V0.1修正版//
//
// Read EC version from 62/66 port
//
#define EcVersionAddr 0xA0
#define EcVersionLen 10
#define EcCommondRead 0x80
#define EC_C_PORT 0x66
#define EC_D_PORT 0x62
EFI_STATUS Status;
UINT8 MaxVerByte=EcVersionLen;
UINT8 VerByte;
UINT8 VerValue[EcVersionLen];
UINT8 *pChar=&VerValue[0];
for (VerByte= 0; VerByte<MaxVerByte; VerByte++)
{
Status=WaitKbcIbe (EC_C_PORT);
if (Status==EFI_SUCCESS)
{
Status=WriteKbc (EC_C_PORT, EcCommondRead);
}
else
{
ASSERT_EFI_ERROR (Status);
return EFI_DEVICE_ERROR;
}
Status=WaitKbcIbe (EC_C_PORT);
if (Status==EFI_SUCCESS)
{
Status=WriteKbc (EC_D_PORT, EcVersionAddr+VerByte);
}
else
{
ASSERT_EFI_ERROR (Status);
return EFI_DEVICE_ERROR;
}
Status=WaitKbcObf (EC_C_PORT);
if (Status==EFI_SUCCESS)
{
ReadKbc (EC_D_PORT, pChar);
}
else
{
ASSERT_EFI_ERROR (Status);
return EFI_DEVICE_ERROR;
}
pChar++;
}
注:以上代码已经过脱敏处理。
以上理论上完成了既定目标,但是还有问题需要处理,比如:WaitKbcIbe ()的实现,仔细考究起来应该还有继续优化的空间,下面尝试做简单的重构。
例1代码段的目标是查询IBF的状态,同时设置了超时时间为15*KBC_TIME_OUT微秒,但是实际上会有一个问题,假设IBF在第2微秒就满足了要求,那么CPU就会会无效的等待15-2=13微秒。相比较而言例2提供了一个另外的思路供探讨,这样做可能会在效率上有些许的改善。
//祖传代码片段//
//例1
for (Index=0; Index<KBC_TIME_OUT; Index++)
{
KbdCmdState= IoRead8 (CommandState);
if (!(KbdCmdState & KEY_IBF))
{
return EFI_SUCCESS;
}
else
{
Stall(15);
}
}
//V0.1修正版本//
//例2
for (Index=0; Index<KBC_TIME_OUT*15; Index++)
{
KbdCmdState= IoRead8 (CommandState);
if (!(KbdCmdState & KEY_IBF))
{
return EFI_SUCCESS;
}
else
{
Stall(1);
}
}
注:以上代码已经过脱敏处理。
综上,这一小段的“祖传代码”里面随便找一下就找出了好几个散发着“渣男”腐败气息的坑,稍不注意就可能深陷其中。当然这里只是抛砖引玉,在上面的代码段里面还有好几个陷阱,大家可以继续找找看,笔者这里就不指出来了;如:安全编码、编码规范、逻辑错误等好几个错误,可以继续挖坑看看。
=====================================================================
固件C字营·版权所有
敬请关注微信公众号:“固件C字营”
=====================================================================