首页 > 代码库 > Cstyle的UEFI导读:第18.0篇 NVRAM的工作原理(上)

Cstyle的UEFI导读:第18.0篇 NVRAM的工作原理(上)

    虽有句话说的好,有用的东西记在脑子里,没有的记在笔记本上。但是现在的信息量越来越大,而且随着时间的推移记忆力会越来越不可靠,所以只好把最近工作之余看的一些东西记录下来,避免被迅速忘记。这里就记录一下一些NVRAM相关的东西。
    NVRAM的定义就不必罗嗦了,非易失性存储器,当然这种定义很宽泛,我们且不一样一个去说明,这里只说UEFI 里面最常用的狭义的NVRAM(SPI ROM里面的一块区域)。一般而论UEFI当中会用到两块区域作为NVRAM分别为NVRAM,NVRAM_BackUp,至于为什么要这么做其实原因很简单,那就是备份和安全保护,应为NVRAM在DXE阶段是可以读写的,既然能写就表示可能会出错,比如突然断电,软件或者硬件错误等等,而NVRAM作为UEFI当中非常重要的一个可供用来提供系统灵活配置的机制如果没有一套完美的容错机制就会发生很严重的问题,比如某个block由于某些原因出错,我们就需要保证这种错误不会导致系统的崩溃。
    再来,NVRAM其实就是在整个SPI rom的区域划分出两个FV,这个FV的属性是可以读写的,在PEI阶段提供readonly variable 的ppi来仅提供读取的功能,在DXE阶段我们就能在RT service里面提供getvariable和setvariable的服务来通过GUID和variableName来搜索我们需要的变量,如setup的设定值。在X86当中NVRAM所存储的区域是刚好被南桥映射到CPU的内存空间,这个区域可以使用MMIO的方式直接读取和写入
就类似于x86系统在reset的时候从FFFFFFF0位置通过MMIO方式读取第一条指令一样。这个区域的大小是可以通过南桥的相关寄存器来设定的,一般会在sec阶段或者早期的pei阶段去设置它,保证该区域能被正确的映射。当然我们也可以使用南桥的SPI控制器来直接使用NOR Flash的读写命令来直接读写这段区域,就类似于在dos下用软件来刷新bios一样。这种一般会在RT或者是SMM模式下提供一些服务提供给其他的工具来调用,这些超出了NVRAM要将的范畴,先不管。
    先讲下NVRAM在SPI Nor Flahs当中是如何存储的。其实它大概分成3部分:每一家的BIOS vendor的实现都不太一样,但是基本的东西差不多,就我看到的AMI A4的和UDK2014里面的实现就不一样,为了不违法NDA这里就只介绍UDK里面的实现方式。
    第一部分:这里就只有一个EFI_FIRMWARE_VOLUME_HEADER的头,主要是将整个NVRAM当作一个FV来看待,这里FV的位置有是在buid的时候定义好的不可更改,在AMI的方案当中一般用token来控制,在UDK当中可以使用FDF里面来定义。我们可以从实际的build logo里面找到它对应的位置,然后使用UE来打开2进制文件来查看。这里先上一个UDK里面的图,这里定义NVRAM存在0x280000位置,长度为0xc000.
 
   
   第二部分:这里就是FFS的区域,不同的实现方式可能不太一样,可以使用FFS2或者是FFS的格式。

     
    第三部分:这里就是真正的FFS file区域了,这里的数据按照Variable store(VARIABLE_STORE_HEADER)的格式来存储,然后使用variable来顺序的存储每一个数据包括他们的名字,长度,GUID,属性(VARIABLE_HEADER)
     
     
    下面是一个实际的SPI里面的数据存放格式,可以对照上面的数据逐个对比,楼主也对比过AMI的A4的实现,虽然他们的Variable store和variable的数据结构和实现方式不太一样,但是实现起来逻辑上是一模一样的,差别在于少量的算法实现不太一样,有兴趣可以自己对比看看:
      
    下面先来说NVRAM的读取,为了简化,这里暂时不考虑FTW(FaultTolerantWrite)的部分,先简单介绍如何从NVRAM读取所需数据。这里只需要2个数据结构就可以完成对NVRAM变量的索引,一个是变量的存储仓库variable store,一个是实际的变量variable他们都有对应的文件头来表示,从刚刚上面降到的FV和FFS开始依次往下查找就可以了,同时需要注意的是pack的大小,是否需要填充字节。考虑到Flash的物理特性有可能会有位反转现象所以在读之前,需要先校验以下的两个文件头是否合法,如果出错,就需要使用到FTW以及NVRAM_BackUp的机制来处理,这个留着下次再说。还有就是以下是基于UDK的实现,其他的实现类似A4的实现跟这个不太一样,不过基本差不多也是有GUID和Name来作为变量的头,然后把变量存储在紧接着“头”的后面。具体的算法很简单可以参考Variable.c这个文件的实现,只要稍微有一点点的基础就能读懂,不需要赘述。


    好了今天就先记录到这里,明天继续。

转载请注明出处
Cstyle.z.zhou@outlook.com  //  http://blog.csdn.net/CStyle_0x007

Cstyle的UEFI导读:第18.0篇 NVRAM的工作原理(上)