《UEFI内核导读》祖传代码引发的血案(I)

本文讲述了在BIOS的POST过程中遇到的读写EC小概率失效问题,通过分析祖传代码,发现了问题根源并提出了修正方案。作者详细展示了从原始代码到优化后的代码,并讨论了WaitKbcIbe()实现的潜在优化,揭示了代码中多个可能的错误和改进点。
摘要由CSDN通过智能技术生成

       

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值