Rowhammer Attacks

最近看资料了解到的一种内存的攻击方式,效果是能够翻转被攻击的内存区域 Bits,如果这个 Bits 正好是用于检查权限之类的,用这种方法就能获得更高的权限。

原理:RAM的制造精度越来越高,部件在物理层面上越来越小。既能在一块芯片上集成更大的内存容量,又能让各个内存单元之间不发生电磁干扰是非常难以做到的。这一情况使得对内存的单个区域进行的读写有可能干扰到邻近的区域,导致电流流入或流出邻近的内存单元。如果反复进行大量读写,有可能改变邻近内存单元的内容,使得0变成1,或者1变成0。这种现象被称为比特翻转(bitflipping),可被利用获取更高的权限。【参考1】

快速读取上图中的黄色 Row ,可能会导致紫色的Row 出现 bits 反转。【参考2】

当然,从原理上来看触发Rowhammer漏洞非常简单,但成功地利用该漏洞有点难,因为内存中大多数的位是与攻击目标无关的,而这些无关位的翻转可能会导致内存破坏。想要成功地利用Rowhammer,攻击者必须能够诱使系统加载目标内存页到DRAM中与攻击者所有的物理内存行邻接的行中。【参考3】

在 MemTest86 的测试中,如果有遇到写入和读取有1Bit差别的failure,可以尝试在Setep 中打开 Enable RH Prevention 这个功能。

参考:

1.http://xilinx.eetrend.com/d6-xilinx/news/2016-10/10624.html 【DDR未解之谜】Row Hammer这么严重,知道如何解决吗?

2.https://www.zhihu.com/question/28666294 Row Hammer漏洞会有多大实质性影响?

3.https://blog.csdn.net/u013806583/article/details/53103714

开源的 Windows ACPI Table 查看工具

最近发现一个开源的 Windows 下查看 ACPI table 的工具 firmwaretables 在下面的链接可以下载到:

https://github.com/vurdalakov/firmwaretables

Lists, extracts and decodes system firmware tables. Usage: firmwaretables <-list | -all | -save | -decode <table type> <table id> [filename]> [-silent] Commands: -l – list available system firmware tables -a – save all system firmware tables to files -s – save specific system firmware table to file -d – decode specific system firmware table Options: -silent – no error messages are shown; check exit code Exit codes: 0 – operation succeeded 1 – operation failed -1 – invalid command line syntax

TinkerNode 制作斐波那契时钟

斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…….. 这个数列从第3项开始,每一项都等于前两项之和: f(n)=f(n-1)+f(n-2)  , n>2, f(1)=1,f(2)=1。

这样的数列非常简单,但是有着很多有趣的特性。比如:而且当n趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618)。

可以看到越到后面,的比值越接近黄金比。

人类和禽兽的一个很大的区别在于对颜色的识别。比如,相对于人类来说,狗狗可以称为色弱。宠物眼科医师已经得出结论,狗所看到的颜色与红绿色盲的人有点相似。狗的眼睛有蓝色和绿色的光感受体,但没有红色的光感受体。因此,狗无法轻易区分黄色,绿色和红色,但是他们可以轻松识别不同深浅的蓝色、紫色和灰色。颜色仅仅是狗感知环境的因素之一。亮度,对比度,尤其是物体运动,都是非常重要的因素,并且这些因素更能帮助狗狗来感知它们周围的环境。【参考1】

狗狗看到的颜色:

人类看到的颜色:

据说人眼对于颜色的敏感是千万年进化而来的结果。比如,准确通过识别书上红色的成熟果实,能够大大提升采集工作的效率…….沿着这种理论,女性应该能分辨出更多的红色,而显示确实如此。譬如,在我眼中都是红色的口红,在老婆眼中可能是朱红 (vermeil),粉红 (pink),梅红(plum),玫瑰红(rose),桃红(peach blossom),樱桃红 ( cherry),桔红(reddish orange),石榴红(garnet),枣红(purplish red),莲红(lotus red),浅莲红(fuchsia pink),豉豆红(bean red)…….

将斐波那契数列和颜色结合在一起,就有了斐波那契时钟,这是国外众筹网站上的一个项目:

想读出这个时钟的时间需要一点技巧:钟表面有5个区域,每个区域代表着不同的权值,比如最小的方块表示1 最大的表示5:

表面上有4种颜色:红色,黄色,蓝色和白色。其中白色表示0. 其他的取值是由其所在区域的权值决定的。具体时间的计算如下

小时数 = 红色数值 + 蓝色数值

分钟数 = (绿色数值 + 蓝色数值) x 5

表示范围就是从 00:00到12:55. 同样的时间可能由不同的方式进行表示。比如:12:55 可以由下面2种方式来表示

红色=1  蓝色=1+2+3+5=11 绿色=0

因此,时间计算方法如下:

小时数 = 1+11=12

分钟数 = (0 + 11) x 5=55

同样的,下面这样也表示 12:55

再来一个复杂的,比如表面如下:

红色=1 绿色=2 蓝色=3+5=8

小时数 = 1+8=9

分钟数 = (2 + 8) x 5=50

所以上面表示的是 9:50 (另外,这个时间总共有6种表示方法)

从表示方法上也能看出来时间是以5分钟为单位的。无法表示注诸如  12:01 这样的时间,只有 12:00或者 12:05 这样。

00:00到 12:55一共有 152个时间,有些时间有很多个表示方法总共有992个方案。

上面介绍了最原始的斐波那契时钟, 小的表面并不能满足我的要求,因此,我设计了一个使用 TinkerNode(ESP32)发出 VGA信号的方案,这样可以将带有 VGA 的显示器变成时钟。电路上非常简单,最主要的是 VGA 接口上的 R,G,B 3个Pin和 Vsync 和 Hsync 2个Pin。

PCB 设计如下:

和之前的设计一样,硬件只是整个作品的一小部分,绝大多数工作是软件来完成的。

需要解决的第一个问题是时间的表示,为此,我编写了一个C语言代码将所有的可能时间拆分表示, 最终结果在 clockdata.h 中。比如,当前时间是  12:55 。首先要在 TimeToIndex [] 中查找(12*12+55/5=155), 其中  TimeToIndex [155] 定义如下:

//155  12:55  990

{2,990}

意思是: 12:55 有2种表示方法,在 TimeToSectionColor [] 这个表格中990 开始处:

//155  12:55  990

{SECRED  ,SECBLUE ,SECBLUE ,SECBLUE ,SECBLUE },

{SECBLUE ,SECRED  ,SECBLUE ,SECBLUE ,SECBLUE }

第一种方法, Section 0-4 颜色分别为 RED,BLUE,BLUE,BLUE,BLUE。 第二种方法,Section 0-4 颜色分别为 BLUE,RED,BLUE,BLUE,BLUE。根据不同区域,将对应区域设定设定为指定的颜色即可。

接下来是时间的获取, TinkerNode 支持物联网,可以轻松的获得,在 DFRobot_NBIOT\examples\LocalTime\CalLocalTime_NB 有这样的例子(此外,对于 TinkerNode开发板还可以通过 GPS/ WIFI 来实现自动获得)。完整代码如下:

// TinkerNode Quectel BC20 模块支持
#include "DFRobot_BC20.h"

// VGA 支持库
#include <ESP32Lib.h>
#include <Ressources/Font8x8.h>

// 计算后的的时间对颜色表示方法
#include "clockdata.h"

DFRobot_BC20 myBC20;

// VGA 显示引脚
const int redPin = 22;
const int greenPin = 21;
const int bluePin = 19;
const int vsyncPin = 18;
const int hsyncPin = 23;

const int TIMEX= 560;
const int TIMEY= 450;

// 3 Bit VGA 支持
VGA3BitI vga;

void setup()
{
  Serial.begin(115200);
  while(!myBC20.powerOn()){
    delay(1000);
    Serial.print(".");
  }
  Serial.println("BC20 started!");
  while(!myBC20.checkNBCard()){
    Serial.println("Please insert the NB SIM card !");
    delay(1000);
  }
  Serial.println("Waitting for access ...");
  while(myBC20.getGATT() == 0){
    Serial.print(".");
    delay(1000);
  }
  Serial.println("Waiting for NB time...");
  // 通过 BC20 获得当前时间
  while(myBC20.getCLK()){
    if(sCLK.Year > 2000){
      break;
    }
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
  Serial.println("Configure local time");
  // 将获得的时间设置给ESP32
  configLocalTime(sCLK.Year,sCLK.Month,sCLK.Day,sCLK.Hour,sCLK.Minute,sCLK.Second);
Serial.printf("sdata %d-%02d-02d %02d:%02d:%02d\n",sCLK.Year,sCLK.Month,sCLK.Day,sCLK.Hour,sCLK.Minute,sCLK.Second);
  // 显示模式为  640x480 (主要是内存限制)
	vga.init(vga.MODE640x480, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
	// 使用 8x8 字库
	vga.setFont(Font8x8);
	// 背景为白色
	vga.clear(vga.RGB(0xffffff));
  /*
	// 这里可以再屏幕上显示一下当前内存剩余情况
	// 设置文字颜色
	vga.setTextColor(vga.RGB(0));
  // 设置屏幕输出位置
  vga.setCursor(0, 20);
  //show the remaining memory
  vga.print("free memory: ");
  vga.print((int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
  */
}

// 颜色索引到颜色值转换函数
int SectionColor(int ColorIndex) {
  if (ColorIndex==SECRED) {return vga.RGB(0xFF);}
  if (ColorIndex==SECGREEN) {return vga.RGB(0xFF00);}
  if (ColorIndex==SECBLUE) {return vga.RGB(0xFF0000);}  
  return vga.RGB(0xFFFFFF);
}

// 记录上一个秒数
int LastSecond=0xFF;

// 记录上一个颜色
int LastTimeIndex=0xFFFF;

void loop()
{
  int TimeIndex;

  // 取得当前时间
  tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  AnalysisTime(&timeinfo);
  Serial.printf("%02d:%02d:%02d\n",timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
  // 如果秒发生变化
  if (timeinfo.tm_sec!=LastSecond) {
      // 串口输出当前时间
      //Serial.printf("%02d:%02d:%02d\n",timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
      // 擦除之前屏幕上绘制的时间
      vga.fillRect(TIMEX,TIMEY,64,64,vga.RGB(0xffffff));
      // 设置文字颜色
      vga.setTextColor(vga.RGB(0));
      vga.setCursor(TIMEX,TIMEY);
      // 如果小时只有1位,那么前面用 0 填充      
      if (timeinfo.tm_hour<10) {vga.print("0");}
      vga.print(timeinfo.tm_hour);
      vga.print(":");
      // 如果分钟只有1位,那么前面用 0 填充
      if (timeinfo.tm_min<10) {vga.print("0");}
      vga.print(timeinfo.tm_min);
      vga.print(":");
      // 如果秒只有1位,那么前面用 0 填充
      if (timeinfo.tm_sec<10) {vga.print("0");}
      vga.print(timeinfo.tm_sec);
      LastSecond=timeinfo.tm_sec;
      
      // 时间表示范围是从 0:0 到 12:55, 所以对于12点特别处理
      if (timeinfo.tm_hour!=12) {TimeIndex=(timeinfo.tm_hour % 12)*12+(timeinfo.tm_min/5);}
      
      // 只有当需要改变屏幕显示的时候才重新绘制
      if (LastTimeIndex!=TimeIndex) {
        // 
        Serial.print("New time index:");
        Serial.printf("%d  [%d %d]\n",TimeIndex,TimeToIndex[TimeIndex].start,TimeToIndex[TimeIndex].number);
        
        // 生成下一个时间的颜色,对于有多个可能性的事件,随机选择一个
        int PoolIndex=TimeToIndex[TimeIndex].start+random(TimeToIndex[TimeIndex].number);
        Serial.print("Pool index:");
        Serial.println(PoolIndex);
        Serial.printf("Pool [%d %d %d %d %d]\n",
                TimeToSectionColor[PoolIndex].timepool[0],
                TimeToSectionColor[PoolIndex].timepool[1],
                TimeToSectionColor[PoolIndex].timepool[2],
                TimeToSectionColor[PoolIndex].timepool[3],
                TimeToSectionColor[PoolIndex].timepool[4]
        );
        
        // 使用圆形表示时间
        if (TimeToSectionColor[PoolIndex].timepool[0]==SECNONE) {
              vga.fillCircle(80,80,80,vga.RGB(0xFFFF));
              vga.fillCircle(80,80,75,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
          }else{  
              vga.fillCircle(80,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
        } 
        if (TimeToSectionColor[PoolIndex].timepool[1]==SECNONE) {
              vga.fillCircle(200,40,40,vga.RGB(0xFFFF));
              vga.fillCircle(200,40,35,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        }else {
              vga.fillCircle(200,40,40,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[2]==SECNONE) {
              vga.fillCircle(200,120,40,vga.RGB(0xFFFF));
              vga.fillCircle(200,120,35,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        }else {
              vga.fillCircle(200,120,40,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[3]==SECNONE) {
              vga.fillCircle(120,280,120,vga.RGB(0xFFFF));
              vga.fillCircle(120,280,115,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        }else {
              vga.fillCircle(120,280,120,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[4]==SECNONE) {
              vga.fillCircle(440,200,200,vga.RGB(0xFFFF));
              vga.fillCircle(440,200,195,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        }else {
              vga.fillCircle(440,200,200,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        }
        

        /*
        // 使用正方形表示时间
        vga.fillRect(0,0,160,160,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
        vga.fillRect(160,0,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        vga.fillRect(160,80,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        vga.fillRect(0,160,240,240,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        vga.fillRect(240,0,400,400,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        */
        
        /*
        // 这里可以预览一下8种颜色
        vga.fillRect(0,0,80,80,  vga.RGB(0x000000));
        vga.fillRect(80,0,80,80, vga.RGB(0x0000FF));
        vga.fillRect(160,0,80,80,vga.RGB(0x00FF00));
        vga.fillRect(240,0,80,80,vga.RGB(0xFF0000));
        vga.fillRect(320,0,80,80,vga.RGB(0xFF00FF));
        vga.fillRect(400,0,80,80,vga.RGB(0xFFFFFF));
        vga.fillRect(480,0,80,80,vga.RGB(0x00FFFF));
        vga.fillRect(560,0,80,80,vga.RGB(0xFFFF00));
        */

        LastTimeIndex=TimeIndex;
      }
   } 
   delay(900);  
}

完整代码下载:

最终,提到颜色,不由得让人想起乐嘉老师的“色彩心理学”,他的学说完全可以作为检验一个人常识和逻辑水平的试金石。

本文首发 https://mc.dfrobot.com.cn/thread-307842-1-1.html

参考:

1. 作者:汪小喵 https://www.zhihu.com/question/22005284/answer/64313689

工作视频在B站可以看到

https://www.bilibili.com/video/BV1df4y1Y7rR

对应的电路图和 PCB

AMD BIOS 工作机会

THE ROLE:
Provide consultation to internal and external customers regarding AMD features and programming requirements.
AMD’s environment is fast paced, results oriented and built upon a legion of forward-thinking people with a passion for winning technology.
This is an extraordinary opportunity to work in an outstanding company like AMD – Join us!

THE PERSON:
Has knowledge with ACPI, USB, PCIE, SATA and other PC industry standard. Has good communication skills and be able to work both independently and in a team.

KEY RESPONSIBILITIES:
Design, develop, and debug BIOS (System Software) for internal/external systems and platforms that use AMD APU/CPUParticipant in day-to-day BIOS development work using PC assembly and C languages; will need to interact with internal organizations, BIOS vendors, and customers.Comfortable working with PC hardware and platform issuesMust have strong system debugging skills.The following are typical tasks that the engineer will be responsible for:Design BIOS features required by AMD APU/CPU;Develop BIOS features for AMD new platforms crafted in house or externally; sustain existing BIOS; debug BIOS and system related issues;Assist APU/CPU validation, platform validation, and debug engineers to develop/debug system and silicon issues; 


PREFERRED EXPERIENCE:
Strong Knowledge about ACPI, USB, PCIE, SATA and other PC industry standardGood at X86 assembly and C languageFamiliar with at least one BIOS code base (AMI, Insyde or Phoenix BIOS).Strong communication skillsExperience in BIOS related tools development is a plus.Fluent in both written and spoken English.Fixes code for firmware (IC embedded code) application.Involves collaboration on or assuming the consultative or leadership responsibilities for a specific project or for product development initiatives.Is required to deal with internal groups on behalf of the group or project.Has accountability for results in a particular area of work.May have limited accountability for a small number of engineers related to projects (2-5), (e.g., be involved in any of the following activities: interview and selection, day-to-day technical supervision or mentoring). 


ACADEMIC CREDENTIALS:
Bachelor or Master, major in EE, CS or related area 
LOCATION:
 上海浦东环科路669


有兴趣的朋友可以联系 Eddy.Gu@amd.com

WinDBG 读取 RTC 的 Script

WinDBG 支持Script,这样用户可以通过编程来实现一些自动控制。本文介绍的就是一个读取 RTC (CMOS)的 Script。

先介绍一下如何使用,将 Script 文件放在 c:\asl\ 下面,文件名是 cmos.txt。在 WinDBG 停下来目标机之后输入 $><c:\asl\cmos.txt 即可运行【参考1】。 WinDBG端结果如下:

kd> $><c:\asl\cmos.txt

    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
===================================================
 0| 8  0 47  0 16  0  2  1 12 20 26  2  0 80  0  0 
 1| 0  0 F0  0  E 80  2 FF FF 2F  0  0  0  0  0  4 
 2|FF FF FF FF FF 3F  0  0  0  0  0  0  0  0  8 EB 
 3|FF FF 20  0  0 7F  0 20 20  0  0  0  0 31  0  0 
 4| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 5| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 6| 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 7| 0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0 
 8|38  0 48  0 16  0  2  1 12 20 26  2  0 80  0  0 
 9| 0  0 F0  0  E 80  2 FF FF 2F  0  0  0  0  0  4 
 A|FF FF FF FF FF 3F  0  0  0  0  0  0  0  0  8 EB 
 B|FF FF 20  0  0 7F  0 20 20  0  0  0  0 31  0  0 
 C| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 D| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 E| 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 F| 0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0

在虚拟机中使用 RW_Everything 可以看到同样的结果:

RW_EveryThing 读取 CMOS

具体的代码如下:

$$********************************************
$$* WinDBG Script from www.lab-z.com         *
$$*                                          *
$$*   Read CMOS                              *
$$*                           2020 DEC 1st   *
$$********************************************

.printf "   "
.for (r $t0=0; @$t0 &lt; 10;r $t0=$t0+1) {.printf "%2X ",$t0}
.printf "\n"
.for (r $t0=0; @$t0 &lt; 11;r $t0=$t0+1) {.printf "==="}
.printf "\n"

.for (r $t0=0; $t0 &lt; 10;r $t0=$t0+1) {
  .printf "%2X|",$t0
  .for (r $t1=0; $t1 &lt; 10;r $t1=$t1+1) {
     r $t2=$t0*10+$t1
     ob 70 $t2
     .foreach /pS 1 (vv {!ib 71}) {r $t3=${vv}}
     .printf "%2X ",$t3
   }
  .printf "\n"
}

简单介绍一下代码意思:

“$$” 表示注释,所以最开始几行只是用于标记

下面几行是用于输出一个表格头

 “.for (r $t0=0; $t0 < 10;r $t0=$t0+1) ” 这是一个 for 循环,其中使用到了 $t0 这是 WinDBG 内置的虚拟寄存器,在 WinDBG 中有 t0-t19个

计算当前的 Index r $t2=$t0*10+$t1

代码中所有的数值都是十六进制,所以看到 10 意思是0x10, 表示 16(十进制)

 ob 70 $t2 这是 outport 命令,将 $t2 值送到 0x70 Port 中

 前面对 70 Port输出 Index, 接下来需要从 71 Port上读取。端口读取命令是 ib ,但是无法直接取得读到的值。因此这里使用  .foreach 来解析命令返回值。在这里,ib返回值是 00000070:  XXXXXX 这种形式。因为空格的存在,解析结果会分为两次出现在后面的大括号中。/pS 1 的意思是忽略返回值中的第一个,使用这个参数之后我们只能收到 XXXXXX 这个结果,我们将其放入 $t3 中

 .foreach /pS 1 (vv {!ib 71}) {r $t3=${vv}}

8.文件文件使用ASCII 格式,因此文件中最好不要出现中文

上面只是实现了CMOS 的读取,配合 WinDBG 的断电能够实现更多的功能,另外最新的 WinDBG Script 支持 JavaScript 有兴趣的朋友可以更深入的研究。

本文提到的 Script 文件下载:

参考:

  1. https://www.cnblogs.com/softfair/p/windbg-command-ii.html Windbg  脚本命令简介 二, Windbg  script command
  2. https://blog.csdn.net/hgy413/article/details/9073157  50.windbg-.foreach循环输入(windbg script)
  3. https://www.cnblogs.com/awpatp/archive/2010/06/16/1758929.html 在WinDBG中使用foreach
  4. https://www.jianshu.com/p/56ff0bc43d3d Windbg调试笔记

Ampere Computing 招聘(202101)

这是一家做ARM 服务器的公司,网站 :

https://amperecomputing.com/#

DescriptionThe Role:

Our PAE will work with ODM to perform technical supports in software area, specifically in UEFI and BMC related.

What the ODM Management Team wants you to know:

ODM Management is the team to deliver the product from NPI to mass production. A critical function team to make sure the design/manufacture tasks at ODM is completed to deliver the platforms to customers.

What you’ll do:

As a member of the ODM management team, you will support design activities of various ODM projects. And work with sales, AE, and engineering teams worldwide to provide software related support.

What you’ll bring:

Experience of UEFI and BMC porting/testing/debugging either AMI code base or opensourceCoding capability of related software of C+, JAVA, HTML, or othersRequired knowledge of CPU and server system architectureSupport ODM experience is required to demonstrate the skills for design supporting, technical service, and problem-solvingCapable of working independently as well as cross different functional teams’ cooperation within company and customers sideHands on experience in the setup and configuration of server systems

Education & Experience:

Bachelor’s degree in computer science, electrical engineering with minimum 9 years of experienceOur CompanyAmpere is designing the future of hyperscale cloud and edge computing with the world’s first cloud native processor. Built for the cloud with a modern 64-bit Arm server-based architecture, Ampere gives customers the freedom to accelerate the delivery of all cloud computing applications. With industry-leading cloud performance, power efficiency and scalability, Ampere processors are tailored for the continued growth of cloud and edge computing.

有兴趣的朋友请联系 jun.chen@amperecomputing.com

QEMU 下 SecureBoot 和 EFI 签名的测试

本文将介绍如何在 QEMU 上实验 Secure Boot 的功能,以及如何对一个 EFI Application 进行签名。

首先,需要编写一个能在QEMU 上运行的,打开 Secure Boot 功能的 BIOS。编译命令如下:

build -a X64 -p OvmfPkg\OvmfPkgx64.dsc -D SECURE_BOOT_ENABLE=TRUE

即可(在 EDK202008 的版本上测试,打开这个功能后需要用到 OpenSSL 提供的函数,因此需要在 EDK2中加入 OpenSSL 的代码)。接下来,根据【参考1】,我们需要生成下面这些文件用于签名。

生成这些文件必须安装Windows SDK,这里使用的是 Windows 10 的 SDK ,工具在C:\Program Files (x86)\Windows Kits\10\bin\x64 下面:

  1. 生成 TestRoot.pvk 和 TestRoot.cer

命令:makecert -n “CN=TestRoot ” -r -sv TestRoot.pvk TestRoot.cer

运行之后会要求你设置一个密码,这里我们使用  labz 作为密码。

之后会再要求你输入一次 labz

2.生成TestSub.pvk 和 TestSub.cer

命令:makecert -n “CN=TestRoot ” -iv TestRoot.pvk -ic TestRoot.cer -sv TestSub.pvk TestSub.cer

这里会要求你设置另外一个密码,这里我们使用 labzpri

输入你设置的 labzpri

再次弹出的对话框输入之前设置的 labz 这个密码

现在我们就有了前面提到的4个文件。

然后准备一个用于测试的EFI 文件,我们使用 Hell.EFI ,它只是在屏幕上输出字符串。同样的,使用 SDK 中的工具对其进行签名,命令如下:

C:\Program Files (x86)\Windows Kits\10\bin\x64>signtool sign /ac TestSub.cer /f TestSub.pfx /p labzpri /fd sha256 Hello.Efi

运行结果:

签名后的文件名称是 Hello-signed.efi 。到现在我们有4个签名相关的文件和2个 EFI 文件,将他们全部copy到U盘上的QEMU目录中(特别注意 U盘必须FAT32格式)。之后使用下面这个 command 启动 QEMU:

qemu-system-x86_64.exe -pflash ovmf_secureboot.fd -hda fat:rw:j:\QEMU -net none

启动后,默认情况下 Secure Boot 是 Disabled,至少需要从 Setup 加入 PK 和 DB 两组 Key【参考1】才能 Enable这个功能:

a.Device Mnager->Secure Boot Configuration切换为 Custom Mode

b.加载 PK 的操作在  Custom Secure Boot Options 中选择 PK Option

c进入之后选择 Enroll PK 通过文件加载 PK

选择盘符

选择 TestRoot.cer 文件

返回后可以看到这个文件已经加载生效了

d.继续加载 DB文件, 操作如下

一个系统中只能有一个 PK,但是可以有多个 DB 文件。这里需要给加载的DB 指定一个 GUID,随便写编一个即可

e.完成设置后返回即可看到 Secure Boot 已经 Enable了。

在 Shell 下测试,因为 Hello.efi 没有签名,所以无法执行,Hello-signed.efi 正确签名了,所以可以运行。

上面就是在 QEMU 下测试 Secure Boot 功能的过程,有兴趣的朋友可以动手试试。

参考:

  1. http://gschwinds.net/cache/SigningUEFIImages-1.31.pdf
  2. https://www.jianshu.com/p/e892733c6f50
  3. 来源同【1】DB和 PK 对于 Secure Boot  Enabling 是必须的

【参考1】的文档,备份一下

ESP32 作为蓝牙音频接收端

具体方法:

1. https://github.com/tierneytim/btAudio 下载并且安装

2.使用UDA1334A I2S 立体声 解码器 I2S Stereo Decoder, 注意:这个是解码器,并不能直接推动喇叭,所以如果在没有音响的情况下只能使用耳机,

如果你想做一个直接带喇叭的播放器,那么还需要一个功放。

原作使用的是“Adafruit I2S Stereo Decoder – UDA1334A” https://learn.adafruit.com/adafr … er-uda1334a/pinouts

我这个是从长江智动买的,一般来说,如果你看到 Adafriut 的板子都可以尝试在这家搜索是否有相同的设计:

CJMCU-1334 UDA334A I2C Decoder

3.将对应的线连接起来, 这里提到的是 D26/D27/D25 这样的Pin:

int bck = 26;
int ws = 27;
int dout = 25;

ESP32 做蓝牙音频接收,之后从 I2S 接口输出给 Codec

4.运行库里面 minimalAudio

5.手机可以搜索到这个设备将 Audio 发送到 ESP32 上

测试视频可以在下面看到:

https://www.bilibili.com/video/BV1B64y1c7H3

sizeof(int)=?

很长一段时间以来,我理所当然的认为 sizeof(int) 是根据编译目标变化的,比如: x86 下应该为 4 Bytes (32-bits),x64 模式下应该为 8 Bytes(64 bits)。但是近日有朋友遇到了问题,我随手实验,用 printf 输出 sizeof(int) 和 sizeof(int *) 结果令我大吃一惊:

x86 下 sizeof(int) ==4 , sizeof(int *)==4

x64 下 sizeof(int) ==4 , sizeof(int *)==8

就是说, x64 下内存指针变成了8Bytes, 但是 int 仍然是 4bytes。无他,规定如此耳。

所以,在编写 UEFI 代码的时候,尽量不要使用 int 类型,因为你在代码中随手写下的赋值可能会因为编译目标不同而导致令人疑惑的错误。作为 Firmware 工程师,需要时刻了解数据的大小。

参考:

1.https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-160

2.https://www.geeksforgeeks.org/difference-between-sizeofint-and-sizeofint-in-c-c/

3.https://docs.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-160

Sizes of built-in types

Most built-in types have implementation-defined sizes. The following table lists the amount of storage required for built-in types in Microsoft C++. In particular, long is 4 bytes even on 64-bit operating systems.

TypeSize
boolcharchar8_tunsigned charsigned char__int81 byte
char16_t__int16shortunsigned shortwchar_t__wchar_t2 bytes
char32_tfloat__int32intunsigned intlongunsigned long4 bytes
double__int64long doublelong longunsigned long long8 bytes