Re: [PATCH] bcb: Android bootloader control block driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 29 Jun 2012 12:36:30 -0700 Andrew Boie <andrew.p.boie@xxxxxxxxx>
wrote:

> Android userspace tells the kernel that it wants to boot into recovery
> or some other non-default OS environment by passing a string argument
> to reboot(). It is left to the OEM to hook this up to their specific
> bootloader.
> 
> This driver uses the bootloader control block (BCB) in the 'misc'
> partition, which is the AOSP mechanism used to communicate with
> the bootloader. Writing 'bootonce-NNN' to the command field
> will cause the bootloader to do a oneshot boot into an alternate
> boot label NNN if it exists. The device and partition number are
> passed in via kernel command line.
> 
> It is the bootloader's job to guard against this area being uninitialzed
> or containing an invalid boot label, and just boot normally if that
> is the case. The bootloader will always populate a magic signature in
> the BCB; the driver will refuse to update it if not present.
> 
> Signed-off-by: Andrew Boie <andrew.p.boie@xxxxxxxxx>

Maybe I'm missing something obvious, but why does this need to be implemented
in the kernel?  Can't some user-space process just write to that partition
using open/read/seek/write/close before calling 'reboot'.

Thanks,
NeilBrown



> ---
>  drivers/staging/android/Kconfig  |   11 ++
>  drivers/staging/android/Makefile |    1 +
>  drivers/staging/android/bcb.c    |  232 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 244 insertions(+)
>  create mode 100644 drivers/staging/android/bcb.c
> 
> diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
> index 9a99238..c30fd20 100644
> --- a/drivers/staging/android/Kconfig
> +++ b/drivers/staging/android/Kconfig
> @@ -78,6 +78,17 @@ config ANDROID_INTF_ALARM_DEV
>  	  elapsed realtime, and a non-wakeup alarm on the monotonic clock.
>  	  Also exports the alarm interface to user-space.
>  
> +config BOOTLOADER_CONTROL
> +	tristate "Bootloader Control Block module"
> +	default n
> +	help
> +	  This driver installs a reboot hook, such that if reboot() is invoked
> +	  with a string argument NNN, "bootonce-NNN" is copied to the command
> +	  field in the Bootloader Control Block on the /misc partition, to be
> +	  read by the bootloader. If the string matches one of the boot labels
> +	  defined in its configuration, it will boot once into that label. The
> +	  device and partition number are specified on the kernel command line.
> +
>  endif # if ANDROID
>  
>  endmenu
> diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
> index 8e18d4e..a27707f 100644
> --- a/drivers/staging/android/Makefile
> +++ b/drivers/staging/android/Makefile
> @@ -9,5 +9,6 @@ obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)	+= lowmemorykiller.o
>  obj-$(CONFIG_ANDROID_SWITCH)		+= switch/
>  obj-$(CONFIG_ANDROID_INTF_ALARM_DEV)	+= alarm-dev.o
>  obj-$(CONFIG_PERSISTENT_TRACER)		+= trace_persistent.o
> +obj-$(CONFIG_BOOTLOADER_CONTROL)	+= bcb.o
>  
>  CFLAGS_REMOVE_trace_persistent.o = -pg
> diff --git a/drivers/staging/android/bcb.c b/drivers/staging/android/bcb.c
> new file mode 100644
> index 0000000..af75257
> --- /dev/null
> +++ b/drivers/staging/android/bcb.c
> @@ -0,0 +1,232 @@
> +/*
> + * bcb.c: Reboot hook to write bootloader commands to
> + *        the Android bootloader control block
> + *
> + * (C) Copyright 2012 Intel Corporation
> + * Author: Andrew Boie <andrew.p.boie@xxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/blkdev.h>
> +#include <linux/reboot.h>
> +
> +#define BCB_MAGIC	0xFEEDFACE
> +
> +/* Persistent area written by Android recovery console and Linux bcb driver
> + * reboot hook for communication with the bootloader. Bootloader must
> + * gracefully handle this area being unitinitailzed */
> +struct bootloader_message {
> +	/* Directive to the bootloader on what it needs to do next.
> +	 * Possible values:
> +	 *   boot-NNN - Automatically boot into label NNN
> +	 *   bootonce-NNN - Automatically boot into label NNN, clearing this
> +	 *     field afterwards
> +	 *   anything else / garbage - Boot default label */
> +	char command[32];
> +
> +	/* Storage area for error codes when using the BCB to boot into special
> +	 * boot targets, e.g. for baseband update. Not used here. */
> +	char status[32];
> +
> +	/* Area for recovery console to stash its command line arguments
> +	 * in case it is reset and the cache command file is erased.
> +	 * Not used here. */
> +	char recovery[1024];
> +
> +	/* Magic sentinel value written by the bootloader; don't update this
> +	 * if not equalto to BCB_MAGIC */
> +	uint32_t magic;
> +};
> +
> +/* TODO: device names/partition numbers are unstable. Add support for looking
> + * by GPT partition UUIDs */
> +static char *bootdev = "sda";
> +module_param(bootdev, charp, S_IRUGO);
> +MODULE_PARM_DESC(bootdev, "Block device for bootloader communication");
> +
> +static int partno;
> +module_param(partno, int, S_IRUGO);
> +MODULE_PARM_DESC(partno, "Partition number for bootloader communication");
> +
> +static int device_match(struct device *dev, void *data)
> +{
> +	if (strcmp(dev_name(dev), bootdev) == 0)
> +		return 1;
> +	return 0;
> +}
> +
> +static struct block_device *get_emmc_bdev(void)
> +{
> +	struct block_device *bdev;
> +	struct device *disk_device;
> +
> +	disk_device = class_find_device(&block_class, NULL, NULL, device_match);
> +	if (!disk_device) {
> +		pr_err("bcb: device %s not found!\n", bootdev);
> +		return NULL;
> +	}
> +	bdev = bdget_disk(dev_to_disk(disk_device), partno);
> +	if (!bdev) {
> +		dev_err(disk_device, "bcb: unable to get disk (%s,%d)\n",
> +				bootdev, partno);
> +		return NULL;
> +	}
> +	/* Note: this bdev ref will be freed after first
> +	   bdev_get/bdev_put cycle */
> +	return bdev;
> +}
> +
> +static u64 last_lba(struct block_device *bdev)
> +{
> +	if (!bdev || !bdev->bd_inode)
> +		return 0;
> +	return div_u64(bdev->bd_inode->i_size,
> +		       bdev_logical_block_size(bdev)) - 1ULL;
> +}
> +
> +static size_t read_lba(struct block_device *bdev,
> +		       u64 lba, u8 *buffer, size_t count)
> +{
> +	size_t totalreadcount = 0;
> +	sector_t n = lba * (bdev_logical_block_size(bdev) / 512);
> +
> +	if (!buffer || lba > last_lba(bdev))
> +		return 0;
> +
> +	while (count) {
> +		int copied = 512;
> +		Sector sect;
> +		unsigned char *data = read_dev_sector(bdev, n++, &sect);
> +		if (!data)
> +			break;
> +		if (copied > count)
> +			copied = count;
> +		memcpy(buffer, data, copied);
> +		put_dev_sector(sect);
> +		buffer += copied;
> +		totalreadcount += copied;
> +		count -= copied;
> +	}
> +	return totalreadcount;
> +}
> +
> +static size_t write_lba(struct block_device *bdev,
> +		       u64 lba, u8 *buffer, size_t count)
> +{
> +	size_t totalwritecount = 0;
> +	sector_t n = lba * (bdev_logical_block_size(bdev) / 512);
> +
> +	if (!buffer || lba > last_lba(bdev))
> +		return 0;
> +
> +	while (count) {
> +		int copied = 512;
> +		Sector sect;
> +		unsigned char *data = read_dev_sector(bdev, n++, &sect);
> +		if (!data)
> +			break;
> +		if (copied > count)
> +			copied = count;
> +		memcpy(data, buffer, copied);
> +		set_page_dirty(sect.v);
> +		unlock_page(sect.v);
> +		put_dev_sector(sect);
> +		buffer += copied;
> +		totalwritecount += copied;
> +		count -= copied;
> +	}
> +	sync_blockdev(bdev);
> +	return totalwritecount;
> +}
> +
> +static int bcb_reboot_notifier_call(
> +		struct notifier_block *notifier,
> +		unsigned long what, void *data)
> +{
> +	int ret = NOTIFY_DONE;
> +	char *cmd = (char *)data;
> +	struct block_device *bdev = NULL;
> +	struct bootloader_message *bcb = NULL;
> +
> +	if (what != SYS_RESTART || !data)
> +		goto out;
> +
> +	bdev = get_emmc_bdev();
> +	if (!bdev)
> +		goto out;
> +
> +	/* make sure the block device is open rw */
> +	if (blkdev_get(bdev, FMODE_READ | FMODE_WRITE, NULL) < 0) {
> +		pr_err("bcb: blk_dev_get failed!\n");
> +		goto out;
> +	}
> +
> +	bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
> +	if (!bcb) {
> +		pr_err("bcb: out of memory\n");
> +		goto out;
> +	}
> +
> +	if (read_lba(bdev, 0, (u8 *)bcb, sizeof(*bcb)) != sizeof(*bcb)) {
> +		pr_err("bcb: couldn't read bootloader control block\n");
> +		goto out;
> +	}
> +
> +	if (bcb->magic != BCB_MAGIC) {
> +		pr_err("bcb: bootloader control block corrupted, not writing\n");
> +		goto out;
> +	}
> +
> +	/* When the bootloader reads this area, it will null-terminate it
> +	* and check if it matches any existing boot labels */
> +	snprintf(bcb->command, sizeof(bcb->command), "bootonce-%s", cmd);
> +
> +	if (write_lba(bdev, 0, (u8 *)bcb, sizeof(*bcb)) != sizeof(*bcb)) {
> +		pr_err("bcb: couldn't write bootloader control block\n");
> +		goto out;
> +	}
> +
> +	ret = NOTIFY_OK;
> +out:
> +	kfree(bcb);
> +	if (bdev)
> +		blkdev_put(bdev, FMODE_READ | FMODE_WRITE);
> +
> +	return ret;
> +}
> +
> +static struct notifier_block bcb_reboot_notifier = {
> +	.notifier_call = bcb_reboot_notifier_call,
> +};
> +
> +static int __init bcb_init(void)
> +{
> +	if (partno < 1) {
> +		pr_err("bcb: partition number not specified\n");
> +		return -1;
> +	}
> +	if (register_reboot_notifier(&bcb_reboot_notifier)) {
> +		pr_err("bcb: unable to register reboot notifier\n");
> +		return -1;
> +	}
> +	pr_info("bcb: writing commands to (%s,%d)\n",
> +			bootdev, partno);
> +	return 0;
> +}
> +module_init(bcb_init);
> +
> +static void __exit bcb_exit(void)
> +{
> +	unregister_reboot_notifier(&bcb_reboot_notifier);
> +}
> +module_exit(bcb_exit);
> +
> +MODULE_AUTHOR("Andrew Boie <andrew.p.boie@xxxxxxxxx>");
> +MODULE_DESCRIPTION("bootloader communication module");
> +MODULE_LICENSE("GPL v2");

Attachment: signature.asc
Description: PGP signature

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel

[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux