Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b2134f2d authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "ANDROID: AVB error handler to invalidate vbmeta partition."

parents 14f69426 b40bc405
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -532,6 +532,17 @@ config DM_LOG_WRITES

	  If unsure, say N.

config DM_VERITY_AVB
	tristate "Support AVB specific verity error behavior"
	depends on DM_VERITY
	---help---
	  Enables Android Verified Boot platform-specific error
	  behavior. In particular, it will modify the vbmeta partition
	  specified on the kernel command-line when non-transient error
	  occurs (followed by a panic).

	  If unsure, say N.

config DM_ANDROID_VERITY
	bool "Android verity target support"
	depends on DM_VERITY=y
+4 −0
Original line number Diff line number Diff line
@@ -74,3 +74,7 @@ endif
ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
dm-verity-objs			+= dm-android-verity.o
endif

ifeq ($(CONFIG_DM_VERITY_AVB),y)
dm-verity-objs			+= dm-verity-avb.o
endif
+217 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 Google.
 *
 * This file is released under the GPLv2.
 *
 * Based on drivers/md/dm-verity-chromeos.c
 */

#include <linux/device-mapper.h>
#include <linux/module.h>
#include <linux/mount.h>

#define DM_MSG_PREFIX "verity-avb"

/* Set via module parameter. */
static char avb_vbmeta_device[64];

static void invalidate_vbmeta_endio(struct bio *bio)
{
	complete(bio->bi_private);
}

static int invalidate_vbmeta_submit(struct bio *bio,
				    struct block_device *bdev,
				    int rw, int access_last_sector,
				    struct page *page)
{
	DECLARE_COMPLETION_ONSTACK(wait);

	bio->bi_private = &wait;
	bio->bi_end_io = invalidate_vbmeta_endio;
	bio->bi_bdev = bdev;

	bio->bi_iter.bi_sector = 0;
	if (access_last_sector) {
		sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1;
		bio->bi_iter.bi_sector = last_sector;
	}
	bio->bi_vcnt = 1;
	bio->bi_iter.bi_idx = 0;
	bio->bi_iter.bi_size = 512;
	bio->bi_iter.bi_bvec_done = 0;
	bio->bi_rw = rw;
	bio->bi_io_vec[0].bv_page = page;
	bio->bi_io_vec[0].bv_len = 512;
	bio->bi_io_vec[0].bv_offset = 0;

	submit_bio(rw, bio);
	/* Wait up to 2 seconds for completion or fail. */
	if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)))
		return -EIO;
	return 0;
}

static int invalidate_vbmeta(dev_t vbmeta_devt)
{
	int ret = 0;
	struct block_device *bdev;
	struct bio *bio;
	struct page *page;
	fmode_t dev_mode;
	/* Ensure we do synchronous unblocked I/O. We may also need
	 * sync_bdev() on completion, but it really shouldn't.
	 */
	int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
	int access_last_sector = 0;

	/* First we open the device for reading. */
	dev_mode = FMODE_READ | FMODE_EXCL;
	bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
				 invalidate_vbmeta);
	if (IS_ERR(bdev)) {
		DMERR("invalidate_kernel: could not open device for reading");
		dev_mode = 0;
		ret = -ENOENT;
		goto failed_to_read;
	}

	bio = bio_alloc(GFP_NOIO, 1);
	if (!bio) {
		ret = -ENOMEM;
		goto failed_bio_alloc;
	}

	page = alloc_page(GFP_NOIO);
	if (!page) {
		ret = -ENOMEM;
		goto failed_to_alloc_page;
	}

	access_last_sector = 0;
	ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
	if (ret) {
		DMERR("invalidate_vbmeta: error reading");
		goto failed_to_submit_read;
	}

	/* We have a page. Let's make sure it looks right. */
	if (memcmp("AVB0", page_address(page), 4) == 0) {
		/* Stamp it. */
		memcpy(page_address(page), "AVE0", 4);
		DMINFO("invalidate_vbmeta: found vbmeta partition");
	} else {
		/* Could be this is on a AVB footer, check. Also, since the
		 * AVB footer is in the last 64 bytes, adjust for the fact that
		 * we're dealing with 512-byte sectors.
		 */
		size_t offset = (1<<SECTOR_SHIFT) - 64;

		access_last_sector = 1;
		ret = invalidate_vbmeta_submit(bio, bdev, rw,
					       access_last_sector, page);
		if (ret) {
			DMERR("invalidate_vbmeta: error reading");
			goto failed_to_submit_read;
		}
		if (memcmp("AVBf", page_address(page) + offset, 4) != 0) {
			DMERR("invalidate_vbmeta called on non-vbmeta partition");
			ret = -EINVAL;
			goto invalid_header;
		}
		/* Stamp it. */
		memcpy(page_address(page) + offset, "AVE0", 4);
		DMINFO("invalidate_vbmeta: found vbmeta footer partition");
	}

	/* Now rewrite the changed page - the block dev was being
	 * changed on read. Let's reopen here.
	 */
	blkdev_put(bdev, dev_mode);
	dev_mode = FMODE_WRITE | FMODE_EXCL;
	bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
				 invalidate_vbmeta);
	if (IS_ERR(bdev)) {
		DMERR("invalidate_vbmeta: could not open device for writing");
		dev_mode = 0;
		ret = -ENOENT;
		goto failed_to_write;
	}

	/* We re-use the same bio to do the write after the read. Need to reset
	 * it to initialize bio->bi_remaining.
	 */
	bio_reset(bio);

	rw |= REQ_WRITE;
	ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
	if (ret) {
		DMERR("invalidate_vbmeta: error writing");
		goto failed_to_submit_write;
	}

	DMERR("invalidate_vbmeta: completed.");
	ret = 0;
failed_to_submit_write:
failed_to_write:
invalid_header:
	__free_page(page);
failed_to_submit_read:
	/* Technically, we'll leak a page with the pending bio, but
	 * we're about to reboot anyway.
	 */
failed_to_alloc_page:
	bio_put(bio);
failed_bio_alloc:
	if (dev_mode)
		blkdev_put(bdev, dev_mode);
failed_to_read:
	return ret;
}

void dm_verity_avb_error_handler(void)
{
	dev_t dev;

	DMINFO("AVB error handler called for %s", avb_vbmeta_device);

	if (avb_vbmeta_device[0] == '\0') {
		DMERR("avb_vbmeta_device parameter not set");
		goto fail_no_dev;
	}

	dev = name_to_dev_t(avb_vbmeta_device);
	if (!dev) {
		DMERR("No matching partition for device: %s",
		      avb_vbmeta_device);
		goto fail_no_dev;
	}

	invalidate_vbmeta(dev);

fail_no_dev:
	;
}

static int __init dm_verity_avb_init(void)
{
	DMINFO("AVB error handler initialized with vbmeta device: %s",
	       avb_vbmeta_device);
	return 0;
}

static void __exit dm_verity_avb_exit(void)
{
}

module_init(dm_verity_avb_init);
module_exit(dm_verity_avb_exit);

MODULE_AUTHOR("David Zeuthen <zeuthen@google.com>");
MODULE_DESCRIPTION("AVB-specific error handler for dm-verity");
MODULE_LICENSE("GPL");

/* Declare parameter with no module prefix */
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX	"androidboot.vbmeta."
module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0);
+5 −1
Original line number Diff line number Diff line
@@ -233,8 +233,12 @@ out:
	if (v->mode == DM_VERITY_MODE_LOGGING)
		return 0;

	if (v->mode == DM_VERITY_MODE_RESTART)
	if (v->mode == DM_VERITY_MODE_RESTART) {
#ifdef CONFIG_DM_VERITY_AVB
		dm_verity_avb_error_handler();
#endif
		kernel_restart("dm-verity device corrupted");
	}

	return 1;
}
+1 −0
Original line number Diff line number Diff line
@@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
extern void verity_dtr(struct dm_target *ti);
extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
extern int verity_map(struct dm_target *ti, struct bio *bio);
extern void dm_verity_avb_error_handler(void);
#endif /* DM_VERITY_H */
Loading