共计 17032 个字符,预计需要花费 43 分钟才能阅读完成。
SeaBIOS调用 vTPM启动过程
要了解可信启动的过程,不能单单在“边启动,边度量”,都说CRTM是信任源头,但是它又是BIOS的一部分,那么他们之间是怎么度量的呢?度量哪儿呢?度量日志放在哪儿呢?
首先是POST阶段的mianinit函数->platform_hardware_setup->tpm_setup()
tpm_setup
对于tpm_setup,用于初始化可信计算模块。
void
tpm_setup(void)
{
if (!CONFIG_TCGBIOS)
return;
//探测tpm2设备
int ret = tpm_tpm2_probe();
if (ret) {
//探测tpm1.2设备
ret = tpm_tcpa_probe();
if (ret)
return;
}
//获取TPM版本 u8 TPM_version 并且调用相应驱动的init方法,tis_init或者crb_init
TPM_version = tpmhw_probe();
if (TPM_version == TPM_VERSION_NONE)
return;
dprintf(DEBUG_tcg,
"TCGBIOS: Detected a TPM %s.\n",
(TPM_version == TPM_VERSION_1_2) ? "1.2" : "2");
TPM_working = 1;
if (runningOnXen())
return;
//初始化TPM
ret = tpm_startup();
if (ret)
return;
//度量smbios
tpm_smbios_measure();
//度量EV_ACTION事件,扩展进入PCR 2寄存器
tpm_add_action(2, "Start Option ROM Scan");
}
可以看到,他先获取了平台上的TPM版本,然后在进行TPM初始化,接着度量smbios添加度量日志。我们看一下细节,它是怎样获取到TPM版本的?以TPM2为例。
static int
tpm_tpm2_probe(void)
{
//从acpi表中获取标识符
struct tpm2_descriptor_rev2 *tpm2 = find_acpi_table(TPM2_SIGNATURE);
if (!tpm2)
return -1;
if (tpm2->length < 76)
return -1;
dprintf(DEBUG_tcg, "TCGBIOS: TPM2: LASA = %p, LAML = %u\n",
(u8 *)(long)tpm2->log_area_start_address,
tpm2->log_area_minimum_length);
//设置日志区域
return tpm_set_log_area((u8*)(long)tpm2->log_area_start_address,
tpm2->log_area_minimum_length);
}
可以看到,从acpi表中获取TPM2_SIGNATURE标识的TPM2描述符,找到之后就判断长度是否符合要求,然后就调用tpm_set_log_area
函数,该函数将设置TPM的日志区域并返回0以表示成功。
tpmhw_probe
该函数获取TPM版本 TPM_version 并且调用相应驱动的init方法,tis_init或者crb_init。
TPMVersion
tpmhw_probe(void)
{
unsigned int i;
for (i = 0; i < TPM_NUM_DRIVERS; i++) {
struct tpm_driver *td = &tpm_drivers[i];
//调用相应的probe方法
if (td->probe() != 0) {
//调用相应的tis_init或者crb_init方法
td->init();
TPMHW_driver_to_use = i;
return td->get_tpm_version();
}
}
return TPM_VERSION_NONE;
}
在qemu-tpm-2.12中,所有的与driver相关的代码,在seaBIOS中的src/hw/tpm_drivers.c
之中。给出tpm_drivers的定义,可以看到tpm驱动分为tis和crb,crb只支持2.0。
struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = {
[TIS_DRIVER_IDX] =
{
.timeouts = NULL,
.durations = NULL,
.set_timeouts = set_timeouts,
.probe = tis_probe,
.get_tpm_version = tis_get_tpm_version,
.init = tis_init,
.activate = tis_activate,
.ready = tis_ready,
.senddata = tis_senddata,
.readresp = tis_readresp,
.waitdatavalid = tis_waitdatavalid,
.waitrespready = tis_waitrespready,
},
[CRB_DRIVER_IDX] =
{
.timeouts = NULL,
.durations = NULL,
.set_timeouts = set_timeouts,
.probe = crb_probe,
.get_tpm_version = crb_get_tpm_version,
.init = crb_init,
.activate = crb_activate,
.ready = crb_ready,
.senddata = crb_senddata,
.readresp = crb_readresp,
.waitdatavalid = crb_waitdatavalid,
.waitrespready = crb_waitrespready,
},
};
tpm_startup
static int
tpm_startup(void)
{
switch (TPM_version) {
case TPM_VERSION_1_2:
return tpm12_startup();
case TPM_VERSION_2:
return tpm20_startup();
}
return -1;
}
我们都以TPM2.0为例,在确认TPM版本之后,初始化TPM。
static int
tpm20_startup(void)
{
//设置超时时间
tpm20_set_timeouts();
//发送TPM2_CC_Startup命令,以清除TPM设备并返回一个结果,该结果存储在ret变量中
int ret = tpm_simple_cmd(0, TPM2_CC_Startup,
2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n",
ret);
//在coreboot已经启用的情况下,但是我们没有使用这里不用看
if (CONFIG_COREBOOT && ret == TPM2_RC_INITIALIZE)
/* with other firmware on the system the TPM may already have been
* initialized
*/
ret = 0;
if (ret)
goto err_exit;
//发送TPM自检命令
ret = tpm_simple_cmd(0, TPM2_CC_SelfTest,
1, TPM2_YES, TPM_DURATION_TYPE_LONG);
dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_SelfTest = 0x%08x\n",
ret);
if (ret)
goto err_exit;
//获取相应的pcrbanks
ret = tpm20_get_pcrbanks();
if (ret)
goto err_exit;
//写入相应的efi事件结构
ret = tpm20_write_EfiSpecIdEventStruct();
if (ret)
goto err_exit;
return 0;
//失败处理
err_exit:
dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
tpm_set_failure();
return -1;
}
tpm_simple_cmd
发送简单的TPM命令,如果使用swtpm作为TPM那么会通过qemu后端驱动发送出去。
static int
tpm_simple_cmd(u8 locty, u32 ordinal
, int param_size, u16 param, enum tpmDurationType to_t)
{
struct {
struct tpm_req_header trqh;
u16 param;
} PACKED req = {
.trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size),
.trqh.ordinal = cpu_to_be32(ordinal),
.param = param_size == 2 ? cpu_to_be16(param) : param,
};
switch (TPM_version) {
case TPM_VERSION_1_2:
req.trqh.tag = cpu_to_be16(TPM_TAG_RQU_CMD);
break;
case TPM_VERSION_2:
req.trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
break;
}
u8 obuffer[64];
struct tpm_rsp_header *trsh = (void*)obuffer;
u32 obuffer_len = sizeof(obuffer);
memset(obuffer, 0x0, sizeof(obuffer));
//组装好请求后,发送请求。
int ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t);
ret = ret ? -1 : be32_to_cpu(trsh->errcode);
dprintf(DEBUG_tcg, "Return from tpm_simple_cmd(%x, %x) = %x\n",
ordinal, param, ret);
return ret;
}
可看到tpmhw_transmit方法才是发送命令的关键。
tpmhw_transmit
int
tpmhw_transmit(u8 locty, struct tpm_req_header *req,
void *respbuffer, u32 *respbufferlen,
enum tpmDurationType to_t)
{
if (TPMHW_driver_to_use == TPM_INVALID_DRIVER)
return -1;
struct tpm_driver *td = &tpm_drivers[TPMHW_driver_to_use];
//函数用于激活TPM设备的TIS接口,请求并等待访问权限,如果访问权限已被获取,则该函数返回0,否则返回非零结果表示激活失败。
u32 irc = td->activate(locty);
if (irc != 0) {
/* tpm could not be activated */
return -1;
}
//发送数据
irc = td->senddata((void*)req, be32_to_cpu(req->totlen));
if (irc != 0)
return -1;
//在超时时间内等待状态寄存器值被设置,等待返回数据
irc = td->waitdatavalid();
if (irc != 0)
return -1;
//在超时时间内等待数据寄存器值被设置,可以读取数据
irc = td->waitrespready(to_t);
if (irc != 0)
return -1;
//读取返回响应数据
irc = td->readresp(respbuffer, respbufferlen);
if (irc != 0 ||
*respbufferlen < sizeof(struct tpm_rsp_header))
return -1;
td->ready();
return 0;
}
可以看到发送命令分为6个主要函数,activate、senddata、waitdatavalid、waitrespready、readresp以及ready。
tis_activate
TPM(Trusted Platform Module)是一个专门的安全硬件模块,它用于在计算设备中提供硬件级别的安全功能,例如密钥生成、加密、解密和数字签名。TPM通常用于确保计算平台的完整性和认证。
TPM的Localities是一种访问控制机制,用于在TPM内部区分不同安全级别的访问权限。Localities的概念是为了支持多种安全执行环境(例如BIOS、操作系统、虚拟化平台等)共享TPM资源,同时确保这些环境之间的隔离和安全性。
TPM中有五个不同的Localities,编号为0到4。它们的安全级别是递增的,即Localities 0具有最低的安全级别,而Localities 4具有最高的安全级别。这些不同的Localities可以用于控制对TPM资源的访问。例如,某些敏感操作可能只允许在具有较高Localities级别的环境中执行。
总结一下,TPM的Localities是一种区分不同安全级别访问权限的机制,用于支持多种安全执行环境共享TPM资源,同时保证环境之间的隔离和安全性。
主要作用就是激活对应的locality
//u8类型的locty,表示要激活的locality编号(0到4之间的整数)
static u32 tis_activate(u8 locty)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u8 acc;
int l;
u32 timeout_a = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A];
//函数检查当前locality(locty)是否已被激活。如果没有被激活,它将释放所有正在使用的locality(从4到0),以便将当前locality设置为活动状态。
if (!(readb(TIS_REG(locty, TIS_REG_ACCESS)) &
TIS_ACCESS_ACTIVE_LOCALITY)) {
/* release locality in use top-downwards */
for (l = 4; l >= 0; l--)
writeb(TIS_REG(l, TIS_REG_ACCESS),
TIS_ACCESS_ACTIVE_LOCALITY);
}
/* request access to locality */
//请求访问指定的locality(locty),通过向TIS_REG_ACCESS寄存器写入TIS_ACCESS_REQUEST_USE值
writeb(TIS_REG(locty, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
//函数读取TIS_REG_ACCESS寄存器的值,检查已请求的locality是否已激活。
//如果成功激活,函数将TIS_REG_STS寄存器设置为TIS_STS_COMMAND_READY,并调用tis_wait_sts函数等待状态寄存器的状态变为TIS_STS_COMMAND_READY。
acc = readb(TIS_REG(locty, TIS_REG_ACCESS));
if ((acc & TIS_ACCESS_ACTIVE_LOCALITY)) {
writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
rc = tis_wait_sts(locty, timeout_a,
TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
}
return rc;
}
tis-senddata
tis驱动的主要发送函数,我们看一下是如何实现的?
static u32 tis_senddata(const u8 *const data, u32 len)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u32 offset = 0;
u32 end_loop = 0;
u16 burst = 0;
u8 locty = tis_find_active_locality();
u32 timeout_d = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_D];
u32 end = timer_calc_usec(timeout_d);
do {
while (burst == 0) {
//循环从TIS_REG_STS读取要发送的字节数
burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8;
if (burst == 0) {
if (timer_check(end)) {
warn_timeout();
break;
}
yield();
}
}
if (burst == 0) {
rc = TCG_RESPONSE_TIMEOUT;
break;
}
while (1) {
//循环向TIS_REG_DATA_FIFO中写入数据相应的数据
writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]);
burst--;
if (burst == 0 || offset == len)
break;
}
if (offset == len)
end_loop = 1;
} while (end_loop == 0);
return rc;
}
总结:就是从TIS_REG_STS中获取要发送的字节数,然后将数据写入TIS_REG_DATA_FIFO之中。
tis_waitdatavalid
这个函数在超时时间内,等待返回数据,如果状态为没有被重新设置就会返回错误。
static u32 tis_waitdatavalid(void)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u8 locty = tis_find_active_locality();
u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C];
//tis_wait_sts() 函数等待 TPM 状态寄存器中指定的状态位(在本例中为 TIS_STS_VALID)被设置为可用,在指定的超时期限内设置状态位时返回 0。
if (tis_wait_sts(locty, timeout_c, TIS_STS_VALID, TIS_STS_VALID) != 0)
rc = 1;
return rc;
}
tis_waitrespready
在TIS_REG_STS被设置可用之后,将其设置为TIS_STS_TPM_GO,在超时期限内,tis_wait_sts()
函数等待 TPM 状态寄存器中指定的状态位(在本例中为 TIS_STS_DATA_AVAILABLE
)被设置,在指定的超时期限内设置状态位时返回 0。如果在超时期限内状态位未被设置,则 tis_wait_sts()
返回非零的错误代码。
static u32 tis_waitrespready(enum tpmDurationType to_t)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u8 locty = tis_find_active_locality();
u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t];
writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO);
//超时期限内等待TIS_STS_DATA_AVAILABLE为数据可用
if (tis_wait_sts(locty, timeout,
TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0)
rc = 1;
return rc;
}
tis_readresp
就是从TIS_REG_DATA_FIFO中读取数据。
static u32 tis_readresp(u8 *buffer, u32 *len)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u32 offset = 0;
u32 sts;
u8 locty = tis_find_active_locality();
while (offset < *len) {
//读取数据TIS_REG_DATA_FIFO
buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO));
offset++;
sts = readb(TIS_REG(locty, TIS_REG_STS));
/* data left ? */
if ((sts & TIS_STS_DATA_AVAILABLE) == 0)
break;
}
*len = offset;
return rc;
}
tis_ready
将locty设置为activate之后的状态。这样就不用下次继续activate了。
static u32 tis_ready(void)
{
if (!CONFIG_TCGBIOS)
return 0;
u32 rc = 0;
u8 locty = tis_find_active_locality();
u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B];
//恢复TIS_REG_STS寄存器值为TIS_STS_COMMAND_READY,代表命令准备好了
writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
rc = tis_wait_sts(locty, timeout_b,
TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
return rc;
}
tpm20_get_pcrbanks
该函数定义了一个名为buffer
的u8
类型数组,长度为128,用于存储从TPM获取到的数据。其次调用tpm20_getcapability获取PCR信息,TCG官方解释如下:
TPM_CAP_PCRS
是TPM(Trusted Platform Module,可信平台模块)的一种Capability(能力),用于返回当前分配给PCR(Platform Configuration Registers)的信息。该命令的参数property
应为零。当TPM收到此命令时,它将始终返回完整的PCR分配,并将moreData
设置为NO
。返回的信息是一个
TPML_PCR_SELECTION
结构体,其中包含了每个PCR bank中已分配的PCR的信息。对于每个已实现的PCR bank,该结果中必须包含一个TPMS_PCR_SELECTION
结构体。对于每个已实现的哈希算法,该结果中也可以包含一个TPMS_PCR_SELECTION
结构体。通过使用
TPM_CAP_PCRS
命令,可以查询当前的PCR分配情况,以便在系统中进行更有效的安全策略实施。
struct tpm2_res_getcapability {
struct tpm_rsp_header hdr;
u8 moreData;
u32 capability;
u8 data[0]; /* capability dependent data */
} PACKED;
struct tpms_pcr_selection {
u16 hashAlg;
u8 sizeOfSelect;
u8 pcrSelect[0];
} PACKED;
struct tpml_pcr_selection {
u32 count;
struct tpms_pcr_selection selections[0];
} PACKED;
static int
tpm20_get_pcrbanks(void)
{
//用于存储从TPM获取到的数据
u8 buffer[128];
struct tpm2_res_getcapability *trg =
(struct tpm2_res_getcapability *)&buffer;
//获取PCR,参数property应为零,TPM收到此命令时,它将始终返回完整的PCR分配,并将moreData设置为NO
int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr,
sizeof(buffer));
if (ret)
return ret;
/* defend against (broken) TPM sending packets that are too short */
u32 resplen = be32_to_cpu(trg->hdr.totlen);
if (resplen <= offsetof(struct tpm2_res_getcapability, data))
return -1;
u32 size = resplen - offsetof(struct tpm2_res_getcapability, data);
/* we need a valid tpml_pcr_selection up to and including sizeOfSelect */
if (size < offsetof(struct tpml_pcr_selection, selections) +
offsetof(struct tpms_pcr_selection, pcrSelect))
return -1;
//全局变量,存放PCR bank
tpm20_pcr_selection = malloc_high(size);
if (tpm20_pcr_selection) {
//返回TPML_PCR_SELECTION结构体
memcpy(tpm20_pcr_selection, &trg->data, size);
tpm20_pcr_selection_size = size;
} else {
warn_noalloc();
ret = -1;
}
return ret;
}
tpm20_getcapability
向TPM发送TPM2_CC_GetCapability请求,返回当前TPM的能力描述信息。上面调用的就是返回对应的PCR属性以及对应的数量。下图时TCG规范中的可以使用TPM2_CC_GetCapability获取信息的命令。
static int
tpm20_getcapability(u32 capability, u32 property, u32 count,
struct tpm_rsp_header *rsp, u32 rsize)
{
struct tpm2_req_getcapability trg = {
.hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
.hdr.totlen = cpu_to_be32(sizeof(trg)),
.hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability),
.capability = cpu_to_be32(capability),
.property = cpu_to_be32(property),
.propertycount = cpu_to_be32(count),
};
u32 resp_size = rsize;
int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size,
TPM_DURATION_TYPE_SHORT);
ret = (ret ||
rsize < be32_to_cpu(rsp->totlen)) ? -1 : be32_to_cpu(rsp->errcode);
dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n",
ret);
return ret;
}
tpm20_write_EfiSpecIdEventStruct
该函数用于添加在描述摘要格式的日志开头添加一条,该日志添加到了ACPI日志中,这个事件包含了TPM所支持的所有hash算法的列表,以及每个hash算法对应的hash大小。
- 检查tpm20_pcr_selection,这是TPM返回的PCR选择结构,包含了所选PCR及对应的hash算法信息。如果这个结构不存在或格式错误,函数返回失败。
- 填充EfiSpecIdEventStruct事件结构。这个结构包含hash算法列表和大小信息。
- 计算EfiSpecIdEventStruct事件结构的总大小,包括大小字段本身和 DigestSizes数组。
- 创建一个tpm_log_event结构,设置事件类型为EV_NO_ACTION。
- 调用tpm_log_event函数,将EfiSpecIdEventStruct事件记录到ACPI日志中。
static int
tpm20_write_EfiSpecIdEventStruct(void)
{
if (!tpm20_pcr_selection)
return -1;
struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
u32 count;
for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) {
u8 sizeOfSelect = sel->sizeOfSelect;
nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
if (nsel > end)
break;
int hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
if (hsize < 0) {
dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n",
be16_to_cpu(sel->hashAlg));
return -1;
}
event.hdr.digestSizes[count].algorithmId = be16_to_cpu(sel->hashAlg);
event.hdr.digestSizes[count].digestSize = hsize;
sel = nsel;
}
if (sel != end) {
dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n");
return -1;
}
event.hdr.numberOfAlgorithms = count;
int event_size = offsetof(struct TCG_EfiSpecIdEventStruct
, digestSizes[count]);
u32 *vendorInfoSize = (void*)&event + event_size;
*vendorInfoSize = 0;
event_size += sizeof(*vendorInfoSize);
struct tpm_log_entry le = {
.hdr.eventtype = EV_NO_ACTION,
};
return tpm_log_event(&le.hdr, SHA1_BUFSIZE, &event, event_size);
}
tpm_smbios_measure
这个函数的目的是度量SMBIOS表,并记录相应的日志信息。
static void
tpm_smbios_measure(void)
{
//使用SHA1作为默认Hash算法
struct pcctes pcctes = {
.eventid = 1,
.eventdatasize = SHA1_BUFSIZE,
};
//SMBiosAddr地址在maininit过程中platform_hardware_setup中qemu_platform_setup已经被赋值了
struct smbios_entry_point *sep = SMBiosAddr;
dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep);
if (!sep)
return;
sha1((const u8 *)sep->structure_table_address,
sep->structure_table_length, pcctes.digest);
//将SMBIOS表的度量值扩展进入对应PCR 1寄存器中,并记录日志。
tpm_add_measurement_to_log(1,
EV_EVENT_TAG,
(const char *)&pcctes, sizeof(pcctes),
(u8 *)&pcctes, sizeof(pcctes));
}
tpm_add_measurement_to_log
在函数中,完成对相应PCR的扩展,并记录日志到ACPI日志中。
/*
* Add a measurement to the log; the data at data_seg:data/length are
* appended to the TCG_PCClientPCREventStruct
*
* Input parameters:
* pcrindex : which PCR to extend
* event_type : type of event; specs section on 'Event Types'
* event : pointer to info (e.g., string) to be added to log as-is
* event_length: length of the event
* hashdata : pointer to the data to be hashed
* hashdata_length: length of the data to be hashed
*/
static void
tpm_add_measurement_to_log(u32 pcrindex, u32 event_type,
const char *event, u32 event_length,
const u8 *hashdata, u32 hashdata_length)
{
if (!tpm_is_working())
return;
u8 hash[SHA1_BUFSIZE];
sha1(hashdata, hashdata_length, hash);
struct tpm_log_entry le = {
.hdr.pcrindex = pcrindex,
.hdr.eventtype = event_type,
};
int digest_len = tpm_build_digest(&le, hash, 1);
if (digest_len < 0)
return;
//进行TPM扩展
int ret = tpm_extend(&le, digest_len);
if (ret) {
tpm_set_failure();
return;
}
tpm_build_digest(&le, hash, 0);
//记录日志
tpm_log_event(&le.hdr, digest_len, event, event_length);
}
tpm_extend
就是调用tpmhw_transmit进行TPM扩展命令的发送,进行TPM扩展。
tpm_add_action
调用放:tpm_add_action(2, "Start Option ROM Scan");
我们看一下该函数:添加了对EV_ACTION事件的度量。并将它扩展到PCR 2寄存器。其实底层也是调用tpm_add_measurement_to_log方法。
// Add an EV_ACTION measurement to the list of measurements
static void
tpm_add_action(u32 pcrIndex, const char *string)
{
u32 len = strlen(string);
tpm_add_measurement_to_log(pcrIndex, EV_ACTION,
string, len, (u8 *)string, len);
}
tpm_menu
接着TPM相关的操作是,在post阶段->maininit->interactiva_bootmenu,显示TPM配置菜单,包括两个操作,clear TPM和改变PCR banks。
tpm_prepboot
这个函数是接着要执行的,post阶段->prepareboot()->tpm_prepboot(),准备要进入boot阶段了执行的,获取授权认证密钥。
void
tpm_prepboot(void)
{
if (!CONFIG_TCGBIOS)
return;
switch (TPM_version) {
case TPM_VERSION_1_2:
if (TPM_has_physical_presence)
tpm_simple_cmd(0, TPM_ORD_PhysicalPresence,
2, TPM_PP_NOT_PRESENT_LOCK, TPM_DURATION_TYPE_SHORT);
break;
case TPM_VERSION_2:
tpm20_prepboot();
break;
}
tpm_add_action(4, "Calling INT 19h");
tpm_add_event_separators();
}
tpm20_prepboot如下:先获取随机数,然后获取授权密钥。
static void
tpm20_prepboot(void)
{
int ret = tpm20_stirrandom();
if (ret)
goto err_exit;
u8 auth[20];
//获取随机数
ret = tpm20_getrandom(&auth[0], sizeof(auth));
if (ret)
goto err_exit;
//获取授权密钥
ret = tpm20_hierarchychangeauth(auth);
if (ret)
goto err_exit;
return;
err_exit:
dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
tpm_set_failure();
}
tpm_add_event_separators
向PCRS0-7中添加事件分隔符,以便记录系统启动过程中的事件。
/*
* Add event separators for PCRs 0 to 7; specs on 'Measuring Boot Events'
*/
static void
tpm_add_event_separators(void)
{
static const u8 evt_separator[] = {0xff,0xff,0xff,0xff};
u32 pcrIndex;
for (pcrIndex = 0; pcrIndex <= 7; pcrIndex++)
//tpm_add_measurement_to_log函数会计算事件的哈希值,并将其添加到PCR寄存器中。
tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR,
NULL, 0,
evt_separator,
sizeof(evt_separator));
tpm_add_bcv
在boot阶段,调用13号中断将磁盘数据读入内存后,调用TPM设备对MBR进行度量。调用流程:boot阶段,do_boot->boot_disk->tpm_add_bcv;
//MBR结构
struct mbr_s {
u8 code[440];
// 0x01b8
u32 diskseg;
// 0x01bc
u16 null;
// 0x01be
struct partition_s partitions[4];
// 0x01fe
u16 signature;
} PACKED;
tpm_add_bcv(bootdrv, MAKE_FLATPTR(bootseg, 0), 512);
看看度量代码:
void
tpm_add_bcv(u32 bootdrv, const u8 *addr, u32 length)
{
if (!tpm_is_working())
return;
//如果小于512字节,直接返回MBR一般都是第一个扇区512字节,前440字节一般是代码
if (length < 0x200)
return;
const char *string = "Booting BCV device 00h (Floppy)";
if (bootdrv == 0x80)
string = "Booting BCV device 80h (HDD)";
//添加事件度量,往PCR 4中扩展
tpm_add_action(4, string);
/* specs: see section 'Hard Disk Device or Hard Disk-Like Devices' */
/* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */
string = "MBR";
// MBR放在了PCR 寄存器4中
tpm_add_measurement_to_log(4, EV_IPL,
string, strlen(string),
addr, 0x1b8);
/* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */
//512字节的后面72字节分区表,放在了PCR 5寄存器中
string = "MBR PARTITION_TABLE";
tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA,
string, strlen(string),
addr + 0x1b8, 0x48);
}
测试一下评论