Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Smitha Balasubramanyam <smitha.balasubramanyam@intel.com>
To: igt-dev@lists.freedesktop.org, zbigniew.kempczynski@intel.com
Subject: [PATCH v2 2/3] tests/intel/xe_ccs: Add fault-mode helper and VM_BIND decomp neg test
Date: Mon, 15 Jun 2026 18:01:08 +0530	[thread overview]
Message-ID: <20260615123109.2286386-3-smitha.balasubramanyam@intel.com> (raw)
In-Reply-To: <20260615123109.2286386-1-smitha.balasubramanyam@intel.com>

Add a helper to trigger page-fault writes along with a negative
testcase covering invalid UAPI handling in fault mode.

Signed-off-by: Smitha Balasubramanyam <smitha.balasubramanyam@intel.com>
---
 tests/intel/xe_ccs.c | 361 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 361 insertions(+)

diff --git a/tests/intel/xe_ccs.c b/tests/intel/xe_ccs.c
index e1dc80659..9a4fa36e1 100644
--- a/tests/intel/xe_ccs.c
+++ b/tests/intel/xe_ccs.c
@@ -71,6 +71,10 @@
  *
  * SUBTEST: vm-bind-decompress-uapi-bad-params
  * Description: UAPI negative test — attempt VM_BIND with a combination of invalid params
+ *
+ * SUBTEST: vm-bind-decompress-uapi-bad-params-fault-mode
+ * Description: UAPI negative test attempt VM_BIND with a combination of
+ *				invalid params - fault mode
  */
 
 IGT_TEST_DESCRIPTION("Exercise gen12 blitter with and without flatccs compression on Xe");
@@ -108,6 +112,7 @@ struct test_config {
 	bool vm_bind_decompress;
 	bool vm_bind_fault_mode_decompress;
 	bool vm_bind_decompress_uapi_bad_params;
+	bool vm_bind_decompress_uapi_bad_params_fault_mode;
 	int width_increment;
 	int width_steps;
 	int overwrite_width;
@@ -833,6 +838,343 @@ static void vm_bind_decompress_uapi_bad_params(int xe,
 	vm_bind_decomp_test_cleanup(xe, ahnd, &allocated_resources);
 }
 
+/* Helper: submit a tiny GPU batch that writes an immediate dword to the
+ * provided VA. This forces the kernel to fault in / decompress pages
+ * for that VA. Uses the provided `ctx` and `ahnd` (caller should have
+ * created/validated these).
+ */
+static void trigger_page_fault_write(int xe, intel_ctx_t *ctx,
+				     uint64_t vm_map_addr,
+				     uint64_t map_size,
+				     uint32_t region)
+{
+	struct drm_xe_exec exec = {};
+	struct drm_xe_sync exec_sync = {};
+	const u64 vm_alignment = xe_get_default_alignment(xe);
+	const size_t ufence_bo_size = SZ_4K;
+	u64 cmd_vm_addr = 0;
+	u64 ufence_vm_addr = 0;
+	u64 *ufence_map = MAP_FAILED;
+	u32 cmd_bo = 0;
+	u32 ufence_bo = 0;
+	u32 *cmdp = MAP_FAILED;
+	int ret;
+
+	/* Create command and fence BOs, then map them directly into the target VM. */
+	cmd_bo = xe_bo_create(xe, 0, SZ_4K, region, 0);
+	igt_assert_f(cmd_bo, "failed to create cmd_bo\n");
+	ufence_bo = xe_bo_create_caching(xe, 0, ufence_bo_size,
+					 system_memory(xe), 0,
+					 DRM_XE_GEM_CPU_CACHING_WC);
+	igt_assert_f(ufence_bo, "failed to create ufence_bo\n");
+
+	/* Map the BO to write the batch */
+	cmdp = xe_bo_map(xe, cmd_bo, SZ_4K);
+	igt_assert_f(cmdp != MAP_FAILED, "xe_bo_map cmd_bo failed\n");
+	ufence_map = xe_bo_map(xe, ufence_bo, ufence_bo_size);
+	igt_assert_f(ufence_map != MAP_FAILED, "xe_bo_map ufence_bo failed\n");
+	memset(ufence_map, 0, ufence_bo_size);
+
+	cmd_vm_addr = ALIGN(vm_map_addr + map_size, vm_alignment);
+	ufence_vm_addr = ALIGN(cmd_vm_addr + SZ_4K, vm_alignment);
+
+	ret = __xe_vm_bind(xe, ctx->vm, 0, cmd_bo, 0, cmd_vm_addr, SZ_4K,
+			   DRM_XE_VM_BIND_OP_MAP, 0, NULL, 0, 0,
+			   DEFAULT_PAT_INDEX, 0);
+	igt_assert_eq(ret, 0);
+	ret = __xe_vm_bind(xe, ctx->vm, 0, ufence_bo, 0, ufence_vm_addr, ufence_bo_size,
+			   DRM_XE_VM_BIND_OP_MAP, 0, NULL, 0, 0,
+			   DEFAULT_PAT_INDEX, 0);
+	igt_assert_eq(ret, 0);
+
+	/* Build a tiny batch: MI_STORE_DWORD_IMM_GEN4 dst=vm_map_addr, value=0xDEADBEEF */
+	cmdp[0] = MI_STORE_DWORD_IMM_GEN4;
+	cmdp[1] = lower_32_bits(vm_map_addr);
+	cmdp[2] = upper_32_bits(vm_map_addr);
+	cmdp[3] = 0xDEADBEEF;
+	cmdp[4] = MI_BATCH_BUFFER_END;
+
+	/* Debug dump of the first few dwords in the fault-trigger batch. */
+	igt_debug("BB dump: cmd_bo=%u cmd_off=0x%llx region=%u vm=%u execq=%u",
+		  cmd_bo, (unsigned long long)cmd_vm_addr, region,
+		  ctx->vm, ctx->exec_queue);
+	for (int i = 0; i < 8; i++)
+		igt_debug(" bb[%02d]=0x%08x", i, cmdp[i]);
+	igt_debug("\n");
+
+	/* Dump the first few dwords of the BB (do this BEFORE munmap) */
+	igt_info("BB dump: cmd_bo=%u cmd_off=0x%llx region=%u vm=%u execq=%u",
+		 cmd_bo, (unsigned long long)cmd_vm_addr, region,
+		 ctx->vm, ctx->exec_queue);
+	for (int i = 0; i < 8; i++)
+		igt_info(" bb[%02d]=0x%08x", i, cmdp[i]);
+
+	/* Make CPU writes visible to GPU */
+	munmap(cmdp, SZ_4K);
+	cmdp = MAP_FAILED;
+
+	/* Submit the batch directly. */
+	memset(&exec, 0, sizeof(exec));
+	exec.exec_queue_id = ctx->exec_queue;
+	exec.address = cmd_vm_addr;
+	exec.num_batch_buffer = 1;
+
+	memset(&exec_sync, 0, sizeof(exec_sync));
+	exec_sync.type = DRM_XE_SYNC_TYPE_USER_FENCE;
+	exec_sync.flags = DRM_XE_SYNC_FLAG_SIGNAL;
+	exec_sync.addr = ufence_vm_addr;
+	exec_sync.timeline_value = 1ULL;
+	exec.num_syncs = 1;
+	exec.syncs = to_user_pointer(&exec_sync);
+
+	igt_info("Submitting faulting batch to write to VA 0x%llx\n",
+		 (unsigned long long)vm_map_addr);
+	igt_debug("Submitting faulting batch to write to VA 0x%llx\n",
+		  (unsigned long long)vm_map_addr);
+	ret = igt_ioctl(xe, DRM_IOCTL_XE_EXEC, &exec);
+	igt_assert_f(ret == 0, "EXEC ioctl failed: %d (%s)\n", ret, strerror(errno));
+
+	xe_wait_ufence(xe, ufence_map, 1ULL, ctx->exec_queue, NSEC_PER_SEC);
+	igt_assert_eq_u64(ufence_map[0], 1ULL);
+
+	/* Cleanup */
+	ret = __xe_vm_bind(xe, ctx->vm, 0, 0, 0, cmd_vm_addr, SZ_4K,
+			   DRM_XE_VM_BIND_OP_UNMAP, 0, NULL, 0, 0, 0, 0);
+	igt_assert_eq(ret, 0);
+	ret = __xe_vm_bind(xe, ctx->vm, 0, 0, 0, ufence_vm_addr, ufence_bo_size,
+			   DRM_XE_VM_BIND_OP_UNMAP, 0, NULL, 0, 0, 0, 0);
+	igt_assert_eq(ret, 0);
+
+	munmap(ufence_map, ufence_bo_size);
+	gem_close(xe, cmd_bo);
+	gem_close(xe, ufence_bo);
+
+	igt_info("Faulting write batch completed successfully\n");
+}
+
+/* Fault-mode variant of vm_bind_decompress_uapi_bad_params.
+ * It reuses your bad_cases but, for expected-success cases, triggers
+ * a GPU write to the mapped VA to force the kernel page-fault/decompress path.
+ */
+static void vm_bind_decompress_uapi_bad_params_fault_mode(int xe,
+							  intel_ctx_t *ctx,
+							  u64 ahnd,
+							  u32 region1,
+							  u32 region2,
+							  u32 width,
+							  u32 height,
+							  enum blt_tiling_type tiling,
+							  const struct test_config *config)
+{
+	struct vm_bind_decomp_setup_resources allocated_resources = {};
+	struct drm_xe_engine_class_instance inst = {
+		.engine_class = DRM_XE_ENGINE_CLASS_RENDER,
+	};
+	intel_ctx_t *fault_ctx = NULL;
+	u32 fault_exec_queue = 0;
+	int ret = 0;
+	bool pattern_matches;
+	void *mapped_data = MAP_FAILED;
+	u64 addr;
+	u32 handle;
+	u64 use_map_size;
+	const uint64_t default_alignment = xe_get_default_alignment(xe);
+	struct vm_bind_decomp_neg_test_case bad_cases[8] = {
+	/* PAT-based accepts (DECOMPRESS + Default PAT) */
+	{ "decompress-default-pat", DEFAULT_PAT_INDEX,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, false },
+
+	/* PAT-based accepts (DECOMPRESS + GPU WB PAT) */
+	{ "decompress-gpu-wb-pat", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, false },
+
+	/* PAT-based rejects (DECOMPRESS + Invalid PAT) */
+	{ "decompress-invalid-pat", INVALID_PAT_INDEX,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+
+	/* PAT-based rejects (DECOMPRESS + non-UC PAT) */
+	{ "decompress-gpu-wt-pat", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+
+	/* PAT-based rejects (DECOMPRESS + compressed PAT) */
+	{ "decompress-gpu-compressed-pat", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+
+	/* Range smaller than the uncompressed size (should be rejected for DECOMPRESS) */
+	{ "decompress-small-range", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+
+	/* Misaligned virtual address for DECOMPRESS update */
+	{ "decompress-misaligned-va", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+
+	/* Wrong BO handle supplied (invalid handle) */
+	{ "decompress-wrong-bo-handle", 0,
+	  DRM_XE_VM_BIND_FLAG_DECOMPRESS, true },
+	};
+	/* Setup (this asserts+cleans on failure) */
+	ret = vm_bind_decomp_test_setup(xe, ctx, ahnd, region1, region2, width,
+					height, tiling, config, false, true,
+					&allocated_resources);
+	igt_assert_eq(ret, 0);
+	igt_assert_f(allocated_resources.vm != 0, "VM not created\n");
+
+	/* Fault-triggering writes must execute in the same VM used by VM_BIND. */
+	fault_exec_queue = xe_exec_queue_create(xe, allocated_resources.vm, &inst, 0);
+	fault_ctx = intel_ctx_xe(xe, allocated_resources.vm, fault_exec_queue, 0, 0, 0);
+	igt_assert_f(fault_ctx, "failed to create fault-mode execution context\n");
+
+	addr = allocated_resources.vm_map_addr;
+	handle = allocated_resources.handle;
+	use_map_size = allocated_resources.map_size;
+
+	print_buffer_data(allocated_resources.src_obj->ptr,
+			  min_t(size_t, 64, allocated_resources.size),
+			  "ORIGINAL", 4);
+	print_buffer_data(allocated_resources.comp_obj->ptr,
+			  min_t(size_t, 64, allocated_resources.size),
+			  "COMPRESSED", 4);
+
+	/* Fill PAT values before iterating through each case*/
+	bad_cases[1].pat = intel_get_pat_idx_wb(xe);
+	bad_cases[3].pat = intel_get_pat_idx_wt(xe);
+	bad_cases[4].pat = intel_get_pat_idx_uc_comp(xe);
+	bad_cases[5].pat = allocated_resources.uncompressed_pat;
+	bad_cases[6].pat = allocated_resources.uncompressed_pat;
+	bad_cases[7].pat = allocated_resources.uncompressed_pat;
+
+	/* Iterate cases */
+	for (unsigned int i = 0; i < ARRAY_SIZE(bad_cases); i++) {
+		const struct vm_bind_decomp_neg_test_case *test_variant = &bad_cases[i];
+
+		/* Reset per-case parameters */
+		u32 use_pat = test_variant->pat;
+		u32 flags = test_variant->flags;
+
+		addr = allocated_resources.vm_map_addr;
+		handle = allocated_resources.handle;
+		use_map_size = allocated_resources.map_size;
+
+		igt_info("\nVM_BIND variant: %s\n", test_variant->test_name);
+
+		/* Corrupt inputs or pass wrong params for a few special cases. */
+		if (!strcmp(test_variant->test_name, "decompress-small-range")) {
+			if (allocated_resources.map_size > default_alignment)
+				use_map_size = allocated_resources.map_size - 1;
+			igt_info("Using reduced map size: %llu\n",
+				 (unsigned long long)use_map_size);
+		} else if (!strcmp(test_variant->test_name, "decompress-misaligned-va")) {
+			addr = allocated_resources.vm_map_addr + 1;
+		} else if (!strcmp(test_variant->test_name, "decompress-wrong-bo-handle")) {
+			handle = allocated_resources.handle ^ 0xdeadbeef;
+		}
+
+		errno = 0;
+		ret =
+		__xe_vm_bind(xe, allocated_resources.vm, 0, handle, 0, addr,
+			     use_map_size, DRM_XE_VM_BIND_OP_MAP, flags,
+			     NULL, 0, 0, use_pat, 0);
+		if (test_variant->expect_fail) {
+			if (ret == 0) {
+				/* Unexpected success — unmap and abort */
+				igt_warn("VM_BIND unexpectedly SUCCEEDED for %s — cleaning up\n",
+					 test_variant->test_name);
+				__xe_vm_bind(xe, allocated_resources.vm, 0, 0, 0,
+					     addr, use_map_size, DRM_XE_VM_BIND_OP_UNMAP, 0,
+					     NULL, 0, 0, 0, 0);
+				vm_bind_decomp_test_cleanup(xe, ahnd, &allocated_resources);
+				igt_assert_f(false,
+					     "VM_BIND expected failure but succeeded for %s",
+					     test_variant->test_name);
+			} else {
+				igt_info("VM_BIND rejected as expected for %s "
+				"(ret=%d errno=%d %s)\n",
+				test_variant->test_name, ret, errno, strerror(errno));
+				igt_info("Success : VM_Bind rejected\n");
+			}
+		} else {
+			/* Expected path: VM_BIND must succeed, then data integrity is verified. */
+			if (ret != 0) {
+				igt_warn("VM_BIND unexpectedly FAILED for %s: %d (%s)\n",
+					 test_variant->test_name, ret, strerror(errno));
+				vm_bind_decomp_test_cleanup(xe, ahnd, &allocated_resources);
+				igt_assert_f(false, "VM_BIND unexpected failure for %s",
+					     test_variant->test_name);
+			}
+
+			igt_info("VM_BIND succeeded for %s (as expected)\n",
+				 test_variant->test_name);
+			/* Trigger page-fault via GPU write to the mapped VA */
+			trigger_page_fault_write(xe, fault_ctx, addr,
+						 allocated_resources.map_size,
+						 region1);
+
+			/* Compare destination buffer against source and print short dumps. */
+			mapped_data = xe_bo_map(xe, allocated_resources.handle,
+						allocated_resources.size);
+			igt_assert_f(mapped_data != MAP_FAILED,
+				     "Failed to map destination BO for %s\n",
+				     test_variant->test_name);
+
+			print_buffer_data(allocated_resources.src_obj->ptr,
+					  min_t(size_t, 64, allocated_resources.size),
+					  "ORIGINAL", 4);
+			print_buffer_data(mapped_data,
+					  min_t(size_t, 64, allocated_resources.size),
+					  "DECOMPRESSED", 4);
+
+			pattern_matches =
+				verify_test_pattern(mapped_data,
+						    allocated_resources.size,
+						    "DECOMPRESSED");
+			if (!pattern_matches)
+				igt_info("Decompression pattern verification FAILED for %s\n",
+					 test_variant->test_name);
+
+			if (memcmp(mapped_data,
+				   allocated_resources.src_obj->ptr,
+				   allocated_resources.size) != 0) {
+				igt_info("Decompressed data does not match original for %s\n",
+					 test_variant->test_name);
+				print_buffer_data(mapped_data,
+						  min_t(size_t, 256, allocated_resources.size),
+						  "CURRENT_STATE", 4);
+				print_buffer_data(allocated_resources.src_obj->ptr,
+						  min_t(size_t, 256, allocated_resources.size),
+						  "EXPECTED", 4);
+
+				munmap(mapped_data, allocated_resources.size);
+				mapped_data = MAP_FAILED;
+				vm_bind_decomp_test_cleanup(xe, ahnd, &allocated_resources);
+				igt_assert_f(false,
+					     "Decompressed BO contents differ from original for %s",
+					     test_variant->test_name);
+			}
+
+			igt_info("Decompression content matches original for %s\n",
+				 test_variant->test_name);
+			munmap(mapped_data, allocated_resources.size);
+			mapped_data = MAP_FAILED;
+
+			/* Unmap to keep state clean for next iteration */
+			ret =
+				__xe_vm_bind(xe, allocated_resources.vm, 0, 0, 0, addr,
+					     use_map_size, DRM_XE_VM_BIND_OP_UNMAP,
+					     0, NULL, 0, 0, 0, 0);
+			if (ret != 0)
+				igt_warn("Unmap after VM_BIND (fault-mode) failed: %d (%s)\n", ret,
+					 strerror(errno));
+		}
+	}
+
+	if (fault_exec_queue)
+		xe_exec_queue_destroy(xe, fault_exec_queue);
+	free(fault_ctx);
+
+	/* Final cleanup */
+	vm_bind_decomp_test_cleanup(xe, ahnd, &allocated_resources);
+}
+
 struct blt_copy3_data {
 	int xe;
 	struct blt_copy_object src;
@@ -1741,6 +2083,12 @@ static void single_copy(int xe, const struct test_config *config,
 		vm_bind_decompress_uapi_bad_params(xe, ctx, ahnd, region1, region2,
 						   width, height, tiling, config);
 		put_ahnd(ahnd);
+	} else if (config->vm_bind_decompress_uapi_bad_params_fault_mode) {
+		ahnd = intel_allocator_open(xe, vm, INTEL_ALLOCATOR_RELOC);
+		vm_bind_decompress_uapi_bad_params_fault_mode(xe,
+							      ctx, ahnd, region1, region2,
+							      width, height, tiling, config);
+		put_ahnd(ahnd);
 	} else {
 		copyfns[copy_function].copyfn(xe, ctx,
 					      region1, region2,
@@ -2058,6 +2406,19 @@ int igt_main_args("bf:pst:W:H:", NULL, help_str, opt_handler, NULL)
 		single_copy(xe, &config, region1, region2, width, height, tiling, BLOCK_COPY);
 	}
 
+	igt_describe("Validate uAPI of VM_BIND with DECOMPRESS flag with bad params");
+	igt_subtest("vm-bind-decompress-uapi-bad-params-fault-mode") {
+		struct test_config config = { .compression = true,
+			.vm_bind_decompress_uapi_bad_params_fault_mode = true };
+		u32 region1 = system_memory(xe);
+		u32 region2 = vram_if_possible(xe, 0);
+		int tiling = T_LINEAR;
+		int width = param.width;
+		int height = param.height;
+
+		single_copy(xe, &config, region1, region2, width, height, tiling, BLOCK_COPY);
+	}
+
 	igt_fixture() {
 		xe_device_put(xe);
 		close(xe);
-- 
2.43.0


  parent reply	other threads:[~2026-06-15 12:26 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-27  5:42 [PATCH 0/3] Negative tests for VM_BIND decompression Smitha Balasubramanyam
2026-05-27  5:42 ` [PATCH 1/3] tests/intel/xe_ccs : Add helpers and Negative test for VM_Bind Decomp Smitha Balasubramanyam
2026-05-27  5:42 ` [PATCH 2/3] tests/intel/xe_ccs: Add fault-mode helper and VM_BIND decomp neg test Smitha Balasubramanyam
2026-05-27  5:42 ` [PATCH 3/3] tests/intel/xe_ccs : Add CCS corruption neg test for VM_BIND decomp Smitha Balasubramanyam
2026-05-27  6:40 ` ✓ Xe.CI.BAT: success for Negative tests for VM_BIND decompression Patchwork
2026-05-27  6:42 ` ✓ i915.CI.BAT: " Patchwork
2026-05-27  8:39 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-05-27 13:21 ` ✓ i915.CI.Full: success " Patchwork
2026-06-15 12:31 ` [PATCH v2 0/3] " Smitha Balasubramanyam
2026-06-15 12:31   ` [PATCH v2 1/3] tests/intel/xe_ccs : Add helpers and Negative test for VM_Bind Decomp Smitha Balasubramanyam
2026-06-15 17:02     ` Matthew Auld
2026-06-15 17:06       ` Matthew Auld
2026-06-22  5:24         ` Balasubramanyam, Smitha
2026-06-22 14:50           ` Matthew Auld
2026-06-15 12:31   ` Smitha Balasubramanyam [this message]
2026-06-15 12:31   ` [PATCH v2 3/3] tests/intel/xe_ccs : Add CCS corruption neg test for VM_BIND decomp Smitha Balasubramanyam
2026-06-15 17:05     ` Matthew Auld
2026-06-22  6:08       ` Balasubramanyam, Smitha
2026-06-22 15:21         ` Matthew Auld
  -- strict thread matches above, loose matches on Subject: below --
2026-04-20  4:57 [PATCH 0/2] Negative tests for VM_BIND decompression Smitha Balasubramanyam
2026-05-25 11:40 ` [PATCH v2 0/3] " Smitha Balasubramanyam
2026-05-25 11:40   ` [PATCH v2 2/3] tests/intel/xe_ccs: Add fault-mode helper and VM_BIND decomp neg test Smitha Balasubramanyam
2026-05-25 12:15 ` [PATCH v2 0/3] Negative tests for VM_BIND decompression Smitha Balasubramanyam
2026-05-25 12:15   ` [PATCH v2 2/3] tests/intel/xe_ccs: Add fault-mode helper and VM_BIND decomp neg test Smitha Balasubramanyam

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260615123109.2286386-3-smitha.balasubramanyam@intel.com \
    --to=smitha.balasubramanyam@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=zbigniew.kempczynski@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox