mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] MCI: support boot partitions
@ 2013-05-23 13:56 Sascha Hauer
  2013-05-23 13:56 ` [PATCH 1/3] param: Add info function Sascha Hauer
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-05-23 13:56 UTC (permalink / raw)
  To: barebox

(newer?) eMMCs have some areas intended for bootloaders. This series
adds support for them. i.MX SoCs support booting from these areas which
is quite convenient since we no longer have to take care about the
regular partition table.

Also this series adds a new device parameter convenience helper for enums.

Sascha

----------------------------------------------------------------
Sascha Hauer (3):
      param: Add info function
      param: Add helpers to provide an enum parameter
      mci: Add support for MMC boot partitions

 drivers/base/driver.c  |   8 ++-
 drivers/mci/Kconfig    |   3 +
 drivers/mci/mci-core.c | 192 ++++++++++++++++++++++++++++++++++++++-----------
 include/mci.h          |  31 +++++++-
 include/param.h        |  15 ++++
 lib/parameter.c        | 104 +++++++++++++++++++++++++++
 6 files changed, 308 insertions(+), 45 deletions(-)

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] param: Add info function
  2013-05-23 13:56 [PATCH] MCI: support boot partitions Sascha Hauer
@ 2013-05-23 13:56 ` Sascha Hauer
  2013-05-23 13:56 ` [PATCH 2/3] param: Add helpers to provide an enum parameter Sascha Hauer
  2013-05-23 13:56 ` [PATCH 3/3] mci: Add support for MMC boot partitions Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-05-23 13:56 UTC (permalink / raw)
  To: barebox

Some parameters may wish to provide some information about their
meaning or possible values. Provide an info callback for parameters.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/base/driver.c | 8 ++++++--
 include/param.h       | 1 +
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index edd49b3..30a4bc5 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -495,8 +495,12 @@ static int do_devinfo(int argc, char *argv[])
 		printf("%s\n", list_empty(&dev->parameters) ?
 				"no parameters available" : "Parameters:");
 
-		list_for_each_entry(param, &dev->parameters, list)
-			printf("%16s = %s\n", param->name, dev_get_param(dev, param->name));
+		list_for_each_entry(param, &dev->parameters, list) {
+			printf("%16s = %s", param->name, dev_get_param(dev, param->name));
+			if (param->info)
+				param->info(param);
+			printf("\n");
+		}
 #ifdef CONFIG_OFDEVICE
 		if (dev->device_node) {
 			printf("\ndevice node: %s\n", dev->device_node->full_name);
diff --git a/include/param.h b/include/param.h
index 4af2d09..54dea56 100644
--- a/include/param.h
+++ b/include/param.h
@@ -12,6 +12,7 @@ typedef unsigned long          IPaddr_t;
 struct param_d {
 	const char* (*get)(struct device_d *, struct param_d *param);
 	int (*set)(struct device_d *, struct param_d *param, const char *val);
+	void (*info)(struct param_d *param);
 	unsigned int flags;
 	char *name;
 	char *value;
-- 
1.8.2.rc2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] param: Add helpers to provide an enum parameter
  2013-05-23 13:56 [PATCH] MCI: support boot partitions Sascha Hauer
  2013-05-23 13:56 ` [PATCH 1/3] param: Add info function Sascha Hauer
@ 2013-05-23 13:56 ` Sascha Hauer
  2013-05-23 13:56 ` [PATCH 3/3] mci: Add support for MMC boot partitions Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-05-23 13:56 UTC (permalink / raw)
  To: barebox

We recently gained helper functions for different types of
device parameters. One thing missing was a helper for an
enum type parameter. This patch adds this.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/param.h |  14 ++++++++
 lib/parameter.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 118 insertions(+)

diff --git a/include/param.h b/include/param.h
index 54dea56..7830f6f 100644
--- a/include/param.h
+++ b/include/param.h
@@ -41,6 +41,11 @@ struct param_d *dev_add_param_bool(struct device_d *dev, const char *name,
 		int (*get)(struct param_d *p, void *priv),
 		int *value, void *priv);
 
+struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
+		int (*set)(struct param_d *p, void *priv),
+		int (*get)(struct param_d *p, void *priv),
+		int *value, const char **names, int max, void *priv);
+
 struct param_d *dev_add_param_int_ro(struct device_d *dev, const char *name,
 		int value, const char *format);
 
@@ -90,6 +95,15 @@ static inline struct param_d *dev_add_param_int(struct device_d *dev, const char
 	return NULL;
 }
 
+static inline struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
+		int (*set)(struct param_d *p, void *priv),
+		int (*get)(struct param_d *p, void *priv),
+		int *value, const char **names, int max, void *priv)
+
+{
+	return NULL;
+}
+
 static inline struct param_d *dev_add_param_bool(struct device_d *dev, const char *name,
 		int (*set)(struct param_d *p, void *priv),
 		int (*get)(struct param_d *p, void *priv),
diff --git a/lib/parameter.c b/lib/parameter.c
index e47e8b9..c5c6426 100644
--- a/lib/parameter.c
+++ b/lib/parameter.c
@@ -299,6 +299,110 @@ struct param_d *dev_add_param_int(struct device_d *dev, const char *name,
 	return &pi->param;
 }
 
+struct param_enum {
+	struct param_d param;
+	int *value;
+	const char **names;
+	int num_names;
+	int (*set)(struct param_d *p, void *priv);
+	int (*get)(struct param_d *p, void *priv);
+};
+
+static inline struct param_enum *to_param_enum(struct param_d *p)
+{
+	return container_of(p, struct param_enum, param);
+}
+
+static int param_enum_set(struct device_d *dev, struct param_d *p, const char *val)
+{
+	struct param_enum *pe = to_param_enum(p);
+	int value_save = *pe->value;
+	int i, ret;
+
+	if (!val)
+		return -EINVAL;
+
+	for (i = 0; i < pe->num_names; i++)
+		if (pe->names[i] && !strcmp(val, pe->names[i]))
+			break;
+
+	if (i == pe->num_names)
+		return -EINVAL;
+
+	*pe->value = i;
+
+	if (!pe->set)
+		return 0;
+
+	ret = pe->set(p, p->driver_priv);
+	if (ret)
+		*pe->value = value_save;
+
+	return ret;
+}
+
+static const char *param_enum_get(struct device_d *dev, struct param_d *p)
+{
+	struct param_enum *pe = to_param_enum(p);
+	int ret;
+
+	if (pe->get) {
+		ret = pe->get(p, p->driver_priv);
+		if (ret)
+			return NULL;
+	}
+
+	free(p->value);
+	p->value = strdup(pe->names[*pe->value]);
+
+	return p->value;
+}
+
+static void param_enum_info(struct param_d *p)
+{
+	struct param_enum *pe = to_param_enum(p);
+	int i;
+
+	printf(" (");
+
+	for (i = 0; i < pe->num_names; i++) {
+		if (!pe->names[i] || !*pe->names[i])
+			continue;
+		printf("\"%s\"%s", pe->names[i],
+				i ==  pe->num_names - 1 ? ")" : ", ");
+	}
+}
+
+struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
+		int (*set)(struct param_d *p, void *priv),
+		int (*get)(struct param_d *p, void *priv),
+		int *value, const char **names, int num_names, void *priv)
+{
+	struct param_enum *pe;
+	struct param_d *p;
+	int ret;
+
+	pe = xzalloc(sizeof(*pe));
+
+	pe->value = value;
+	pe->set = set;
+	pe->get = get;
+	pe->names = names;
+	pe->num_names = num_names;
+	p = &pe->param;
+	p->driver_priv = priv;
+
+	ret = __dev_add_param(p, dev, name, param_enum_set, param_enum_get, 0);
+	if (ret) {
+		free(pe);
+		return ERR_PTR(ret);
+	}
+
+	p->info = param_enum_info;
+
+	return &pe->param;
+}
+
 /**
  * dev_add_param_bool - add an boolean parameter to a device
  * @param dev	The device
-- 
1.8.2.rc2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] mci: Add support for MMC boot partitions
  2013-05-23 13:56 [PATCH] MCI: support boot partitions Sascha Hauer
  2013-05-23 13:56 ` [PATCH 1/3] param: Add info function Sascha Hauer
  2013-05-23 13:56 ` [PATCH 2/3] param: Add helpers to provide an enum parameter Sascha Hauer
@ 2013-05-23 13:56 ` Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-05-23 13:56 UTC (permalink / raw)
  To: barebox

Some MMC cards support boot partitions. These are special regions
on the MMC card intended to put a bootloader on.

This patch adds support for these partitions, they are accessible
as /dev/diskx.boot[0|1].

Additionally the partitions can be configured bootable using a
device parameter. This can be used to mark the user area or one
of the boot partitions as bootable.

Since this feature is mostly seen on eMMC cards it is made optional
to lower the size impact for boards which do not have eMMC.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mci/Kconfig    |   3 +
 drivers/mci/mci-core.c | 192 ++++++++++++++++++++++++++++++++++++++-----------
 include/mci.h          |  31 +++++++-
 3 files changed, 183 insertions(+), 43 deletions(-)

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 9558f28..7f28d13 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -29,6 +29,9 @@ config MCI_WRITE
 	default y
 	select DISK_WRITE
 
+config MCI_MMC_BOOT_PARTITIONS
+	bool "support MMC boot partitions"
+
 comment "--- MCI host drivers ---"
 
 config MCI_MXS
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 90ac2a3..5367b4f 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -388,6 +388,35 @@ static int mci_switch(struct mci *mci, unsigned set, unsigned index,
 	return mci_send_cmd(mci, &cmd, NULL);
 }
 
+static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
+{
+	unsigned ret = cap >> shift;
+
+	if (ret > 0x7fffffff) {
+		pr_warn("Limiting card size due to 31 bit contraints\n");
+		return 0x7fffffff;
+	}
+
+	return (int)ret;
+}
+
+static void mci_part_add(struct mci *mci, uint64_t size,
+                        unsigned int part_cfg, char *name, int idx, bool ro,
+                        int area_type)
+{
+	struct mci_part *part = &mci->part[mci->nr_parts];
+
+	part->mci = mci;
+	part->size = size;
+	part->blk.cdev.name = name;
+	part->blk.blockbits = SECTOR_SHIFT;
+	part->blk.num_blocks = mci_calc_blk_cnt(size, part->blk.blockbits);
+	part->area_type = area_type;
+	part->part_cfg = part_cfg;
+
+	mci->nr_parts++;
+}
+
 /**
  * Change transfer frequency for an MMC card
  * @param mci MCI instance
@@ -442,6 +471,25 @@ static int mmc_change_freq(struct mci *mci)
 	else
 		mci->card_caps |= MMC_MODE_HS;
 
+	if (mci->ext_csd[EXT_CSD_REV] >= 3 && mci->ext_csd[EXT_CSD_BOOT_MULT]) {
+		int idx;
+		unsigned int part_size;
+
+		for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
+			char *name;
+			part_size = mci->ext_csd[EXT_CSD_BOOT_MULT] << 17;
+
+			name = asprintf("%s.boot%d", mci->cdevname, idx);
+			mci_part_add(mci, part_size,
+					EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
+					name, idx, true,
+					MMC_BLK_DATA_AREA_BOOT);
+		}
+
+		mci->ext_csd_part_config = mci->ext_csd[EXT_CSD_PART_CONFIG];
+		mci->bootpart = (mci->ext_csd_part_config >> 3) & 0x7;
+	}
+
 	return 0;
 }
 
@@ -1063,6 +1111,10 @@ static int mci_startup(struct mci *mci)
 	/* we setup the blocklength only one times for all accesses to this media  */
 	err = mci_set_blocklen(mci, mci->read_bl_len);
 
+	mci_part_add(mci, mci->capacity, 0,
+			mci->cdevname, 0, true,
+			MMC_BLK_DATA_AREA_MAIN);
+
 	return err;
 }
 
@@ -1104,6 +1156,36 @@ static int sd_send_if_cond(struct mci *mci)
 	return 0;
 }
 
+static int mci_blk_part_switch(struct mci_part *part)
+{
+	struct mci *mci = part->mci;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS))
+		return 0;
+
+	if (mci->part_curr == part)
+		return 0;
+
+	if (!IS_SD(mci)) {
+		u8 part_config = mci->ext_csd_part_config;
+
+		part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+		part_config |= part->part_cfg;
+
+		ret = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
+				EXT_CSD_PART_CONFIG, part_config);
+		if (ret)
+			return ret;
+
+		mci->ext_csd_part_config = part_config;
+	}
+
+	mci->part_curr = part;
+
+	return 0;
+}
+
 /* ------------------ attach to the blocklayer --------------------------- */
 
 /**
@@ -1119,10 +1201,13 @@ static int sd_send_if_cond(struct mci *mci)
 static int __maybe_unused mci_sd_write(struct block_device *blk,
 				const void *buffer, int block, int num_blocks)
 {
-	struct mci *mci = container_of(blk, struct mci, blk);
+	struct mci_part *part = container_of(blk, struct mci_part, blk);
+	struct mci *mci = part->mci;
 	struct mci_host *host = mci->host;
 	int rc;
 
+	mci_blk_part_switch(part);
+
 	if (host->card_write_protected && host->card_write_protected(host)) {
 		dev_err(mci->mci_dev, "card write protected\n");
 		return -EPERM;
@@ -1165,9 +1250,12 @@ static int __maybe_unused mci_sd_write(struct block_device *blk,
 static int mci_sd_read(struct block_device *blk, void *buffer, int block,
 				int num_blocks)
 {
-	struct mci *mci = container_of(blk, struct mci, blk);
+	struct mci_part *part = container_of(blk, struct mci_part, blk);
+	struct mci *mci = part->mci;
 	int rc;
 
+	mci_blk_part_switch(part);
+
 	dev_dbg(mci->mci_dev, "%s: Read %d block(s), starting at %d\n",
 		__func__, num_blocks, block);
 
@@ -1338,18 +1426,6 @@ static int mci_check_if_already_initialized(struct mci *mci)
 	return 0;
 }
 
-static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
-{
-	unsigned ret = cap >> shift;
-
-	if (ret > 0x7fffffff) {
-		pr_warn("Limiting card size due to 31 bit contraints\n");
-		return 0x7fffffff;
-	}
-
-	return (int)ret;
-}
-
 static struct block_device_ops mci_ops = {
 	.read = mci_sd_read,
 #ifdef CONFIG_BLOCK_WRITE
@@ -1357,6 +1433,28 @@ static struct block_device_ops mci_ops = {
 #endif
 };
 
+static int mci_set_boot(struct param_d *param, void *priv)
+{
+	struct mci *mci = priv;
+
+	mci->ext_csd_part_config &= ~(7 << 3);
+	mci->ext_csd_part_config |= mci->bootpart << 3;
+
+	return mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
+			EXT_CSD_PART_CONFIG, mci->ext_csd_part_config);
+}
+
+static const char *mci_boot_names[] = {
+	"disabled",
+	"boot0",
+	"boot1",
+	NULL, /* reserved */
+	NULL, /* reserved */
+	NULL, /* reserved */
+	NULL, /* reserved */
+	"user",
+};
+
 /**
  * Probe an MCI card at the given host interface
  * @param mci MCI device instance
@@ -1365,7 +1463,7 @@ static struct block_device_ops mci_ops = {
 static int mci_card_probe(struct mci *mci)
 {
 	struct mci_host *host = mci->host;
-	int rc, disknum;
+	int i, rc, disknum;
 
 	if (host->card_present && !host->card_present(host)) {
 		dev_err(mci->mci_dev, "no card inserted\n");
@@ -1402,6 +1500,13 @@ static int mci_card_probe(struct mci *mci)
 	if (rc)
 		goto on_error;
 
+	if (host->devname) {
+		mci->cdevname = strdup(host->devname);
+	} else {
+		disknum = cdev_find_free_index("disk");
+		mci->cdevname = asprintf("disk%d", disknum);
+	}
+
 	rc = mci_startup(mci);
 	if (rc) {
 		dev_dbg(mci->mci_dev, "Card's startup fails with %d\n", rc);
@@ -1411,36 +1516,39 @@ static int mci_card_probe(struct mci *mci)
 	dev_dbg(mci->mci_dev, "Card is up and running now, registering as a disk\n");
 	mci->ready_for_use = 1;	/* TODO now or later? */
 
-	/*
-	 * An MMC/SD card acts like an ordinary disk.
-	 * So, re-use the disk driver to gain access to this media
-	 */
-	mci->blk.dev = mci->mci_dev;
-	mci->blk.ops = &mci_ops;
-
-	if (host->devname) {
-		mci->blk.cdev.name = strdup(host->devname);
-	} else {
-		disknum = cdev_find_free_index("disk");
-		mci->blk.cdev.name = asprintf("disk%d", disknum);
-	}
+	for (i = 0; i < mci->nr_parts; i++) {
+		struct mci_part *part = &mci->part[i];
 
-	mci->blk.blockbits = SECTOR_SHIFT;
-	mci->blk.num_blocks = mci_calc_blk_cnt(mci->capacity, mci->blk.blockbits);
-
-	rc = blockdevice_register(&mci->blk);
-	if (rc != 0) {
-		dev_err(mci->mci_dev, "Failed to register MCI/SD blockdevice\n");
-		goto on_error;
-	}
+		/*
+		 * An MMC/SD card acts like an ordinary disk.
+		 * So, re-use the disk driver to gain access to this media
+		 */
+		part->blk.dev = mci->mci_dev;
+		part->blk.ops = &mci_ops;
 
-	dev_info(mci->mci_dev, "registered %s\n", mci->blk.cdev.name);
+		rc = blockdevice_register(&part->blk);
+		if (rc != 0) {
+			dev_err(mci->mci_dev, "Failed to register MCI/SD blockdevice\n");
+			goto on_error;
+		}
+		dev_info(mci->mci_dev, "registered %s\n", part->blk.cdev.name);
+
+		/* create partitions on demand */
+		if (part->area_type == MMC_BLK_DATA_AREA_MAIN) {
+			rc = parse_partition_table(&part->blk);
+			if (rc != 0) {
+				dev_warn(mci->mci_dev, "No partition table found\n");
+				rc = 0; /* it's not a failure */
+			}
+		}
 
-	/* create partitions on demand */
-	rc = parse_partition_table(&mci->blk);
-	if (rc != 0) {
-		dev_warn(mci->mci_dev, "No partition table found\n");
-		rc = 0; /* it's not a failure */
+		if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
+				part->area_type == MMC_BLK_DATA_AREA_BOOT &&
+				!mci->param_boot) {
+			mci->param_boot = dev_add_param_enum(mci->mci_dev, "boot",
+					mci_set_boot, NULL, &mci->bootpart,
+					mci_boot_names, ARRAY_SIZE(mci_boot_names), mci);
+		}
 	}
 
 	dev_dbg(mci->mci_dev, "SD Card successfully added\n");
diff --git a/include/mci.h b/include/mci.h
index eca48a5..1735986 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -185,6 +185,8 @@
 /*
  * EXT_CSD field definitions
  */
+#define EXT_CSD_PART_CONFIG_ACC_MASK	(0x7)
+#define EXT_CSD_PART_CONFIG_ACC_BOOT0	(0x1)
 
 #define EXT_CSD_CMD_SET_NORMAL		(1<<0)
 #define EXT_CSD_CMD_SET_SECURE		(1<<1)
@@ -306,10 +308,28 @@ struct mci_host {
 	int (*card_write_protected)(struct mci_host *);
 };
 
+struct mci;
+
+#define MMC_NUM_BOOT_PARTITION	2
+#define MMC_NUM_GP_PARTITION	4
+#define MMC_NUM_PHY_PARTITION	6
+
+struct mci_part {
+	struct block_device	blk;		/**< the blockdevice for the card */
+	struct mci		*mci;
+	uint64_t		size;		/* partition size (in bytes) */
+	unsigned int		part_cfg;	/* partition type */
+	char			*name;
+	unsigned int		area_type;
+#define MMC_BLK_DATA_AREA_MAIN	(1<<0)
+#define MMC_BLK_DATA_AREA_BOOT	(1<<1)
+#define MMC_BLK_DATA_AREA_GP	(1<<2)
+#define MMC_BLK_DATA_AREA_RPMB	(1<<3)
+};
+
 /** MMC/SD and interface instance information */
 struct mci {
 	struct mci_host *host;		/**< the host for this card */
-	struct block_device blk;	/**< the blockdevice for the card */
 	struct device_d *mci_dev;	/**< the device for our disk (mcix) */
 	unsigned version;
 	/** != 0 when a high capacity card is connected (OCR -> OCR_HCS) */
@@ -330,6 +350,15 @@ struct mci {
 	char *ext_csd;
 	int probe;
 	struct param_d *param_probe;
+	struct param_d *param_boot;
+	int bootpart;
+
+	struct mci_part part[MMC_NUM_PHY_PARTITION];
+	int nr_parts;
+	char *cdevname;
+
+	struct mci_part *part_curr;
+	u8 ext_csd_part_config;
 };
 
 int mci_register(struct mci_host*);
-- 
1.8.2.rc2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-05-23 13:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-23 13:56 [PATCH] MCI: support boot partitions Sascha Hauer
2013-05-23 13:56 ` [PATCH 1/3] param: Add info function Sascha Hauer
2013-05-23 13:56 ` [PATCH 2/3] param: Add helpers to provide an enum parameter Sascha Hauer
2013-05-23 13:56 ` [PATCH 3/3] mci: Add support for MMC boot partitions Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox