summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2014-10-08 16:41:01 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-15 00:10:10 +0000
commit4184e626336fa8d794a21208387226f154d77d0f (patch)
treef5e03108edb836829498cc35a432f017bb1e707b
parent5dc75d16b6d5cb0ebc677e6572a2559c6157b8e4 (diff)
downloadvboot-4184e626336fa8d794a21208387226f154d77d0f.tar.gz
Use VbExStream APIs to read the kernel partition
This is necessary to support reading the kernel from raw NAND flash, where the driver may need to skip over bad sectors, and absolute sector addressing is thus not practical. The impact is relatively minor. Vboot only did two reads per kernel anyway, one for the first 64KB of the partition and a second for the rest of the kernel data. Firmware which uses vboot will need to implement the streaming APIs. Or, as a really easy workaround, just copy the implementation from firmware/stub/vboot_api_stub_stream.c, which translates from the new streaming API to the old sector-based disk API. BUG=chromium:403432 BRANCH=none TEST=make runtests; passes. CQ-DEPEND=CL:221992, CL:222885, CL:222945 Change-Id: I7437b489650c95c09ac68b67d4d86f9e15c2fa73 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/222410 Reviewed-by: Daniel Ehrenberg <dehrenberg@chromium.org>
-rw-r--r--firmware/lib/vboot_kernel.c95
-rw-r--r--tests/vboot_kernel_tests.c24
2 files changed, 83 insertions, 36 deletions
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index 8c985a97..a66b8861 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -214,6 +214,8 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
int rec_switch, dev_switch;
BootMode boot_mode;
uint32_t require_official_os = 0;
+ uint32_t body_toread;
+ uint8_t *body_readptr;
VbError_t retval = VBERROR_UNKNOWN;
int recovery = VBNV_RECOVERY_LK_UNSPECIFIED;
@@ -304,11 +306,10 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
VbKeyBlockHeader *key_block;
VbKernelPreambleHeader *preamble;
RSAPublicKey *data_key = NULL;
+ VbExStream_t stream = NULL;
uint64_t key_version;
uint32_t combined_version;
uint64_t body_offset;
- uint64_t body_offset_sectors;
- uint64_t body_sectors;
int key_block_valid = 1;
VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n",
@@ -334,15 +335,15 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
/* Found at least one kernel partition. */
found_partitions++;
- /* Read the first part of the kernel partition. */
- if (part_size < kbuf_sectors) {
- VBDEBUG(("Partition too small to hold kernel.\n"));
+ /* Set up the stream */
+ if (VbExStreamOpen(params->disk_handle,
+ part_start, part_size, &stream)) {
+ VBDEBUG(("Partition error getting stream.\n"));
shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL;
goto bad_kernel;
}
- if (0 != VbExDiskRead(params->disk_handle, part_start,
- kbuf_sectors, kbuf)) {
+ if (0 != VbExStreamRead(stream, KBUF_SIZE, kbuf)) {
VBDEBUG(("Unable to read start of partition.\n"));
shpart->check_result = VBSD_LKP_CHECK_READ_START;
goto bad_kernel;
@@ -487,54 +488,80 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
* one; we only needed to look at the versions to check for
* rollback. So skip to the next kernel preamble.
*/
- if (-1 != good_partition)
+ if (-1 != good_partition) {
+ VbExStreamClose(stream);
+ stream = NULL;
continue;
+ }
- /* Verify kernel body starts at multiple of sector size. */
body_offset = key_block->key_block_size +
preamble->preamble_size;
- if (0 != body_offset % blba) {
- VBDEBUG(("Kernel body not at multiple of "
- "sector size.\n"));
+
+ /*
+ * Make sure the kernel starts at or before what we already
+ * read into kbuf.
+ *
+ * We could deal with a larger offset by reading and discarding
+ * the data in between the vblock and the kernel data.
+ */
+ if (body_offset > KBUF_SIZE) {
shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET;
+ VBDEBUG(("Kernel body offset is %d > 64KB.\n",
+ (int)body_offset));
goto bad_kernel;
}
- body_offset_sectors = body_offset / blba;
- body_sectors =
- (preamble->body_signature.data_size + blba - 1) / blba;
if (!params->kernel_buffer) {
/* Get kernel load address and size from the header. */
params->kernel_buffer =
(void *)((long)preamble->body_load_address);
- params->kernel_buffer_size = body_sectors * blba;
- } else {
- /* Verify kernel body fits in the buffer */
- if (body_sectors * blba > params->kernel_buffer_size) {
- VBDEBUG(("Kernel body doesn't "
- "fit in memory.\n"));
- shpart->check_result =
- VBSD_LKP_CHECK_BODY_EXCEEDS_MEM;
- goto bad_kernel;
- }
+ params->kernel_buffer_size =
+ preamble->body_signature.data_size;
+ } else if (preamble->body_signature.data_size >
+ params->kernel_buffer_size) {
+ VBDEBUG(("Kernel body doesn't fit in memory.\n"));
+ shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_MEM;
+ goto bad_kernel;
}
- /* Verify kernel body fits in the partition */
- if (body_offset_sectors + body_sectors > part_size) {
- VBDEBUG(("Kernel body doesn't fit in partition.\n"));
- shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART;
- goto bad_kernel;
+ /*
+ * Body signature data size is 64 bit and toread is 32 bit so
+ * this could technically cause us to read less data. That's
+ * fine, because a 4 GB kernel is implausible, and if we did
+ * have one that big, we'd simply read too little data and fail
+ * to verify it.
+ */
+ body_toread = preamble->body_signature.data_size;
+ body_readptr = params->kernel_buffer;
+
+ /*
+ * If we've already read part of the kernel, copy that to the
+ * beginning of the kernel buffer.
+ */
+ if (body_offset < KBUF_SIZE) {
+ uint32_t body_copied = KBUF_SIZE - body_offset;
+
+ /* If the kernel is tiny, don't over-copy */
+ if (body_copied > body_toread)
+ body_copied = body_toread;
+
+ Memcpy(body_readptr, kbuf + body_offset, body_copied);
+ body_toread -= body_copied;
+ body_readptr += body_copied;
}
/* Read the kernel data */
- if (0 != VbExDiskRead(params->disk_handle,
- part_start + body_offset_sectors,
- body_sectors, params->kernel_buffer)) {
+ if (body_toread &&
+ 0 != VbExStreamRead(stream, body_toread, body_readptr)) {
VBDEBUG(("Unable to read kernel data.\n"));
shpart->check_result = VBSD_LKP_CHECK_READ_DATA;
goto bad_kernel;
}
+ /* Close the stream; we're done with it */
+ VbExStreamClose(stream);
+ stream = NULL;
+
/* Verify kernel data */
if (0 != VerifyData((const uint8_t *)params->kernel_buffer,
params->kernel_buffer_size,
@@ -603,6 +630,8 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
bad_kernel:
/* Handle errors parsing this kernel */
+ if (NULL != stream)
+ VbExStreamClose(stream);
if (NULL != data_key)
RSAPublicKeyFree(data_key);
diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c
index 3712d0d3..0c26d212 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -153,6 +153,7 @@ static void ResetMocks(void)
lkp.ending_lba = 1023;
lkp.kernel_buffer = kernel_buffer;
lkp.kernel_buffer_size = sizeof(kernel_buffer);
+ lkp.disk_handle = (VbExDiskHandle_t)1;
memset(&kbh, 0, sizeof(kbh));
kbh.data_key.key_version = 2;
@@ -162,7 +163,7 @@ static void ResetMocks(void)
memset(&kph, 0, sizeof(kph));
kph.kernel_version = 1;
kph.preamble_size = 4096 - kbh.key_block_size;
- kph.body_signature.data_size = 70000;
+ kph.body_signature.data_size = 70144;
kph.bootloader_address = 0xbeadd008;
kph.bootloader_size = 0x1234;
@@ -531,6 +532,12 @@ static void InvalidParamsTest(void)
gpt_init_fail = 1;
TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_NO_KERNEL_FOUND,
"Bad GPT");
+
+ /* This causes the stream open call to fail */
+ ResetMocks();
+ lkp.disk_handle = NULL;
+ TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
+ "Bad disk handle");
}
static void LoadKernelTest(void)
@@ -538,7 +545,9 @@ static void LoadKernelTest(void)
uint32_t u;
ResetMocks();
- TEST_EQ(LoadKernel(&lkp, &cparams), 0, "First kernel good");
+
+ u = LoadKernel(&lkp, &cparams);
+ TEST_EQ(u, 0, "First kernel good");
TEST_EQ(lkp.partition_number, 1, " part num");
TEST_EQ(lkp.bootloader_address, 0xbeadd008, " bootloader addr");
TEST_EQ(lkp.bootloader_size, 0x1234, " bootloader size");
@@ -689,6 +698,11 @@ static void LoadKernelTest(void)
TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
"Kernel body offset");
+ ResetMocks();
+ kph.preamble_size += 65536;
+ TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
+ "Kernel body offset huge");
+
/* Check getting kernel load address from header */
ResetMocks();
kph.body_load_address = (size_t)kernel_buffer;
@@ -709,7 +723,11 @@ static void LoadKernelTest(void)
"Kernel too big for partition");
ResetMocks();
- disk_read_to_fail = 108;
+ kph.body_signature.data_size = 8192;
+ TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Kernel tiny");
+
+ ResetMocks();
+ disk_read_to_fail = 228;
TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND,
"Fail reading kernel data");