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


           

    敬请关注微信公众号:“固件C字营”

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

     

             最近遇到一个奇怪的问题,大概就是BIOS在POST过程读写EC,非常小概率会失效。经测试发现失效的概率跟读写频率,读写速度相关。

         于是乎,左手改EC代码,在相关的代码插桩加入log信息,右手分析祖传的BIOS代码,同样是打桩和加log,用终端来监视读/写过程中的异常,左右开弓忙的不亦乐乎。需要说明的是Cstyle为BIOS攻城狮,以下代码片段皆来自N多年以前的UEFI/BIOS旧项目。

        “祖传代码”之所以称之为祖传代码是因为大多数BIOS工程师都非常之忙,被追杀、被投诉的戏码屡屡上演如:被LD催,被PM用18米大刀追、被测试嫌弃Bug写的太多,被兄弟部门投诉解Bub速度太慢等等,岂止是苦逼、简直就是苦逼,因此本着快、准、狠,及不重复造轮子的最高指导原则,能C+V的绝不自己动手。

        言归正传,经过“坚持不懈”的填坑,终于.终于.终于,在数百万行代码里面(虽然99.9%都不是我们自己写的!!!)发现下面这段代码看着不太顺眼,仔细追查了一下之后发现这段代码的历史由来已久,应该是祖传无疑,此“码”远远看着就散发着一股子“渣男”气质,应该说是渣男中的渣男,必须除之而后快。

    1. //祖传代码片段//
    2. //
    3. // Read EC version from 62/66 port
    4. //
    5. EFI_STATUS  Status;
    6. UINT8       MaxVerByte;
    7. UINT8       VerByte;
    8. UINT8       *pChar;
    9. UINT8       VerValue[10];
    10. MaxVerByte = 10;
    11. pChar = VerValue;
    12. for (VerByte = 0; VerByte <=MaxVerByte; VerByte++)
    13. {
    14.     Status = WaitKbcIbe (EC_C_PORT);
    15.     if (Status == EFI_SUCCESS)
    16.     {
    17.         Status = WriteKbc (EC_C_PORT, 0x80);
    18.     }
    19.     Status = WaitKbcIbe (EC_C_PORT);
    20.     if (Status == EFI_SUCCESS)
    21.     {
    22.         Status = WriteKbc (EC_D_PORT, 0xA0 + VerByte);
    23.     }
    24.     Status = WaitKbcObf (EC_C_PORT); 
    25.     if (Status == EFI_SUCCESS)
    26.     {
    27.         ReadKbc (EC_D_PORT, pChar);
    28.     }
    29.     pChar++;
    30. }

            注:以上代码已经过脱敏处理。 

            前文介绍了“祖传代码”带来的噩梦,现介绍一个0.1版本的修正版本,供参考。此修正能解决部分问题,主要解决了数据读写失败后的异常处理。

    1. //V0.1修正版//
    2. //
    3. // Read EC version from 62/66 port
    4. //
    5. #define EcVersionAddr 0xA0
    6. #define EcVersionLen  10
    7. #define EcCommondRead 0x80
    8. #define EC_C_PORT     0x66
    9. #define EC_D_PORT     0x62
    10. EFI_STATUS  Status;
    11. UINT8       MaxVerByte=EcVersionLen;
    12. UINT8       VerByte;
    13. UINT8       VerValue[EcVersionLen];
    14. UINT8       *pChar=&VerValue[0];
    15. for (VerByte= 0; VerByte<MaxVerByte; VerByte++)
    16. {
    17. Status=WaitKbcIbe (EC_C_PORT);
    18. if (Status==EFI_SUCCESS)
    19. {
    20. Status=WriteKbc (EC_C_PORT, EcCommondRead);
    21. }
    22. else
    23. {
    24. ASSERT_EFI_ERROR (Status);
    25. return EFI_DEVICE_ERROR;
    26. }
    27. Status=WaitKbcIbe (EC_C_PORT);
    28. if (Status==EFI_SUCCESS)
    29. {
    30. Status=WriteKbc (EC_D_PORT, EcVersionAddr+VerByte);
    31. }
    32. else
    33. {
    34. ASSERT_EFI_ERROR (Status);
    35. return EFI_DEVICE_ERROR;
    36. }
    37. Status=WaitKbcObf (EC_C_PORT); 
    38.   if (Status==EFI_SUCCESS)
    39. {
    40.     ReadKbc (EC_D_PORT, pChar);
    41. }
    42. else
    43. {
    44.   ASSERT_EFI_ERROR (Status);
    45.   return EFI_DEVICE_ERROR;
    46. }
    47.    
    48. pChar++;
    49. }

            注:以上代码已经过脱敏处理。 

            以上理论上完成了既定目标,但是还有问题需要处理,比如:WaitKbcIbe ()的实现,仔细考究起来应该还有继续优化的空间,下面尝试做简单的重构。

            例1代码段的目标是查询IBF的状态,同时设置了超时时间为15*KBC_TIME_OUT微秒,但是实际上会有一个问题,假设IBF在第2微秒就满足了要求,那么CPU就会会无效的等待15-2=13微秒。相比较而言例2提供了一个另外的思路供探讨,这样做可能会在效率上有些许的改善。

    1. //祖传代码片段//
    2. //例1
    3. for (Index=0; Index<KBC_TIME_OUT; Index++)
    4. {
    5. KbdCmdState= IoRead8 (CommandState);
    6. if (!(KbdCmdState & KEY_IBF))
    7. {
    8. return EFI_SUCCESS;
    9. }
    10. else
    11. {
    12. Stall(15);
    13. }
    14. }
    1. //V0.1修正版本//
    2. //例2
    3. for (Index=0; Index<KBC_TIME_OUT*15; Index++)
    4. {
    5. KbdCmdState= IoRead8 (CommandState);
    6. if (!(KbdCmdState & KEY_IBF))
    7. {
    8. return EFI_SUCCESS;
    9. }
    10. else
    11. {
    12. Stall(1);
    13. }
    14. }

    注:以上代码已经过脱敏处理。

            综上,这一小段的“祖传代码”里面随便找一下就找出了好几个散发着“渣男”腐败气息的坑,稍不注意就可能深陷其中。当然这里只是抛砖引玉,在上面的代码段里面还有好几个陷阱,大家可以继续找找看,笔者这里就不指出来了;如:安全编码、编码规范、逻辑错误等好几个错误,可以继续挖坑看看。

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

    固件C字营·版权所有

    敬请关注微信公众号:“固件C字营”

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

     

  • 相关阅读:
    HCIA-Access V2.5华为认证接入网络工程师学习笔记第四章
    Ubuntu 20.04.5安装无线网卡RTL8821CE驱动
    停止员工拖延症!工时管理系统的作用之一
    SPPNet
    【滤波跟踪】基于不变扩展卡尔曼滤波器对装有惯性导航系统和全球定位系统IMU+GPS进行滤波跟踪附matlab代码
    Nacos+openfeign
    2022全新docker安装mysql镜像、tomcat镜像及打包IDEA项目为war包并部署到docker上成功运行
    PC商城开发
    软件测试界的三无简历,企业拿什么来招聘你,石沉大海的简历
    计算机毕业设计之java+javaweb的面向社区健康服务的医疗平台
  • 原文地址:https://blog.csdn.net/CStyle_0x007/article/details/125456606