敬请关注微信公众号:“固件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字营”

=====================================================================