Home | History | Annotate | Download | only in futility
      1 /* Copyright 2012 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  *
      5  * Exports the kernel commandline from a given partition/image.
      6  */
      7 
      8 #include <stdio.h>
      9 #include <string.h>
     10 #include <sys/mman.h>
     11 #include <sys/fcntl.h>
     12 #include <sys/stat.h>
     13 #include <sys/types.h>
     14 #include <unistd.h>
     15 
     16 #include "host_common.h"
     17 #include "kernel_blob.h"
     18 #include "vboot_api.h"
     19 #include "vboot_host.h"
     20 
     21 #ifdef USE_MTD
     22 #include <linux/major.h>
     23 #include <mtd/mtd-user.h>
     24 #include <mtdutils.h>
     25 #endif
     26 
     27 typedef ssize_t (*ReadFullyFn)(void *ctx, void *buf, size_t count);
     28 
     29 static ssize_t ReadFullyWithRead(void *ctx, void *buf, size_t count)
     30 {
     31 	ssize_t nr_read = 0;
     32 	int fd = *((int*)ctx);
     33 	while (nr_read < count) {
     34 		ssize_t to_read = count - nr_read;
     35 		ssize_t chunk = read(fd, buf + nr_read, to_read);
     36 		if (chunk < 0) {
     37 			return -1;
     38 		} else if (chunk == 0) {
     39 			break;
     40 		}
     41 		nr_read += chunk;
     42 	}
     43 	return nr_read;
     44 }
     45 
     46 #ifdef USE_MTD
     47 static ssize_t ReadFullyWithMtdRead(void *ctx, void *buf, size_t count)
     48 {
     49 	MtdReadContext *mtd_ctx = (MtdReadContext*)ctx;
     50 	return mtd_read_data(mtd_ctx, buf, count);
     51 }
     52 #endif
     53 
     54 /* Skip the stream by calling |read_fn| many times. Return 0 on success. */
     55 static int SkipWithRead(void *ctx, ReadFullyFn read_fn, size_t count)
     56 {
     57 	char buf[1024];
     58 	ssize_t nr_skipped = 0;
     59 	while (nr_skipped < count) {
     60 		ssize_t to_read = count - nr_skipped;
     61 		if (to_read > sizeof(buf)) {
     62 			to_read = sizeof(buf);
     63 		}
     64 		if (read_fn(ctx, buf, to_read) != to_read) {
     65 			return -1;
     66 		}
     67 		nr_skipped += to_read;
     68 	}
     69 	return 0;
     70 }
     71 
     72 static char *FindKernelConfigFromStream(void *ctx, ReadFullyFn read_fn,
     73 					uint64_t kernel_body_load_address)
     74 {
     75 	VbKeyBlockHeader key_block;
     76 	VbKernelPreambleHeader preamble;
     77 	uint32_t now = 0;
     78 	uint32_t offset = 0;
     79 
     80 	/* Skip the key block */
     81 	if (read_fn(ctx, &key_block, sizeof(key_block)) != sizeof(key_block)) {
     82 		VbExError("not enough data to fill key block header\n");
     83 		return NULL;
     84 	}
     85 	ssize_t to_skip = key_block.key_block_size - sizeof(key_block);
     86 	if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
     87 		VbExError("key_block_size advances past the end of the blob\n");
     88 		return NULL;
     89 	}
     90 	now += key_block.key_block_size;
     91 
     92 	/* Open up the preamble */
     93 	if (read_fn(ctx, &preamble, sizeof(preamble)) != sizeof(preamble)) {
     94 		VbExError("not enough data to fill preamble\n");
     95 		return NULL;
     96 	}
     97 	to_skip = preamble.preamble_size - sizeof(preamble);
     98 	if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
     99 		VbExError("preamble_size advances past the end of the blob\n");
    100 		return NULL;
    101 	}
    102 	now += preamble.preamble_size;
    103 
    104 	/* Read body_load_address from preamble if no
    105 	 * kernel_body_load_address */
    106 	if (kernel_body_load_address == USE_PREAMBLE_LOAD_ADDR)
    107 		kernel_body_load_address = preamble.body_load_address;
    108 
    109 	/* The x86 kernels have a pointer to the kernel commandline in the
    110 	 * zeropage table, but that's irrelevant for ARM. Both types keep the
    111 	 * config blob in the same place, so just go find it. */
    112 	offset = preamble.bootloader_address -
    113 	    (kernel_body_load_address + CROS_PARAMS_SIZE +
    114 	     CROS_CONFIG_SIZE) + now;
    115 	to_skip = offset - now;
    116 	if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
    117 		VbExError("params are outside of the memory blob: %x\n",
    118 			  offset);
    119 		return NULL;
    120 	}
    121 	char *ret = malloc(CROS_CONFIG_SIZE);
    122 	if (!ret) {
    123 		VbExError("No memory\n");
    124 		return NULL;
    125 	}
    126 	if (read_fn(ctx, ret, CROS_CONFIG_SIZE) != CROS_CONFIG_SIZE) {
    127 		VbExError("Cannot read kernel config\n");
    128 		free(ret);
    129 		ret = NULL;
    130 	}
    131 	return ret;
    132 }
    133 
    134 char *FindKernelConfig(const char *infile, uint64_t kernel_body_load_address)
    135 {
    136 	char *newstr = NULL;
    137 
    138 	int fd = open(infile, O_RDONLY | O_CLOEXEC | O_LARGEFILE);
    139 	if (fd < 0) {
    140 		VbExError("Cannot open %s\n", infile);
    141 		return NULL;
    142 	}
    143 
    144 	void *ctx = &fd;
    145 	ReadFullyFn read_fn = ReadFullyWithRead;
    146 
    147 #ifdef USE_MTD
    148 	struct stat stat_buf;
    149 	if (fstat(fd, &stat_buf)) {
    150 		VbExError("Cannot stat %s\n", infile);
    151 		return NULL;
    152 	}
    153 
    154 	int is_mtd = (major(stat_buf.st_rdev) == MTD_CHAR_MAJOR);
    155 	if (is_mtd) {
    156 		ctx = mtd_read_descriptor(fd, infile);
    157 		if (!ctx) {
    158 			VbExError("Cannot read from MTD device %s\n", infile);
    159 			return NULL;
    160 		}
    161 		read_fn = ReadFullyWithMtdRead;
    162 	}
    163 #endif
    164 
    165 	newstr = FindKernelConfigFromStream(ctx, read_fn,
    166 					    kernel_body_load_address);
    167 
    168 #ifdef USE_MTD
    169 	if (is_mtd) {
    170 		mtd_read_close(ctx);
    171 	}
    172 #endif
    173 	close(fd);
    174 
    175 	return newstr;
    176 }
    177