diff --git a/hw/lpc.c b/hw/lpc.c
index bf3ab1fae..caaacc461 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -667,6 +667,80 @@ int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr,
 	return __lpc_read_sanity(addr_type, addr, data, sz, true);
 }
 
+int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len)
+{
+	int rc;
+
+	prlog(PR_TRACE, "Reading 0x%08x bytes at FW offset 0x%08x\n",
+	      len, off);
+
+	while (len) {
+		uint32_t chunk;
+		uint32_t dat;
+
+		/* XXX: make this read until it's aligned */
+		if (len > 3 && !(off & 3)) {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
+			if (!rc) {
+				/*
+				 * lpc_read swaps to CPU endian but it's not
+				 * really a 32-bit value, so convert back.
+				 */
+				*(__be32 *)buf = cpu_to_be32(dat);
+			}
+			chunk = 4;
+		} else {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
+			if (!rc)
+				*(uint8_t *)buf = dat;
+			chunk = 1;
+		}
+		if (rc) {
+			prlog(PR_ERR, "lpc_read failure %d to FW 0x%08x\n", rc, off);
+			return rc;
+		}
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
+int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len)
+{
+	int rc;
+
+	prlog(PR_TRACE, "Writing 0x%08x bytes at FW offset 0x%08x\n",
+	      len, off);
+
+	while (len) {
+		uint32_t chunk;
+
+		if (len > 3 && !(off & 3)) {
+			/* endian swap: see lpc_window_write */
+			uint32_t dat = be32_to_cpu(*(__be32 *)buf);
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
+			chunk = 4;
+		} else {
+			uint8_t dat = *(uint8_t *)buf;
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
+			chunk = 1;
+		}
+		if (rc) {
+			prlog(PR_ERR, "lpc_write failure %d to FW 0x%08x\n", rc, off);
+			return rc;
+		}
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
 /*
  * The "OPAL" variant add the emulation of 2 and 4 byte accesses using
  * byte accesses for IO and MEM space in order to be compatible with
diff --git a/include/lpc.h b/include/lpc.h
index b641aa4e6..ce9c33dc7 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -102,6 +102,12 @@ extern int64_t lpc_probe_write(enum OpalLPCAddressType addr_type, uint32_t addr,
 extern int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr,
 			      uint32_t *data, uint32_t sz);
 
+/*
+ * helpers for doing a bulk io to firmware space.
+ */
+extern int64_t lpc_fw_read(uint32_t addr, void *buf, uint32_t sz);
+extern int64_t lpc_fw_write(uint32_t addr, const void *buf, uint32_t sz);
+
 /* Mark LPC bus as used by console */
 extern void lpc_used_by_console(void);
 
diff --git a/libflash/ipmi-hiomap.c b/libflash/ipmi-hiomap.c
index 29355d666..93aaef455 100644
--- a/libflash/ipmi-hiomap.c
+++ b/libflash/ipmi-hiomap.c
@@ -555,45 +555,11 @@ static int lpc_window_read(struct ipmi_hiomap *ctx, uint32_t pos,
 			   void *buf, uint32_t len)
 {
 	uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos);
-	int rc;
 
 	if ((ctx->current.lpc_addr + ctx->current.size) < (off + len))
 		return FLASH_ERR_PARM_ERROR;
 
-	prlog(PR_TRACE, "Reading at 0x%08x for 0x%08x offset: 0x%08x\n",
-	      pos, len, off);
-
-	while(len) {
-		uint32_t chunk;
-		uint32_t dat;
-
-		/* XXX: make this read until it's aligned */
-		if (len > 3 && !(off & 3)) {
-			rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
-			if (!rc) {
-				/*
-				 * lpc_read swaps to CPU endian but it's not
-				 * really a 32-bit value, so convert back.
-				 */
-				*(__be32 *)buf = cpu_to_be32(dat);
-			}
-			chunk = 4;
-		} else {
-			rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
-			if (!rc)
-				*(uint8_t *)buf = dat;
-			chunk = 1;
-		}
-		if (rc) {
-			prlog(PR_ERR, "lpc_read failure %d to FW 0x%08x\n", rc, off);
-			return rc;
-		}
-		len -= chunk;
-		off += chunk;
-		buf += chunk;
-	}
-
-	return 0;
+	return lpc_fw_read(off, buf, len);
 }
 
 static int lpc_window_write(struct ipmi_hiomap *ctx, uint32_t pos,
@@ -601,7 +567,6 @@ static int lpc_window_write(struct ipmi_hiomap *ctx, uint32_t pos,
 {
 	uint32_t off = ctx->current.lpc_addr + (pos - ctx->current.cur_pos);
 	enum lpc_window_state state;
-	int rc;
 
 	lock(&ctx->lock);
 	state = ctx->window_state;
@@ -613,34 +578,7 @@ static int lpc_window_write(struct ipmi_hiomap *ctx, uint32_t pos,
 	if ((ctx->current.lpc_addr + ctx->current.size) < (off + len))
 		return FLASH_ERR_PARM_ERROR;
 
-	prlog(PR_TRACE, "Writing at 0x%08x for 0x%08x offset: 0x%08x\n",
-	      pos, len, off);
-
-	while(len) {
-		uint32_t chunk;
-
-		if (len > 3 && !(off & 3)) {
-			/* endian swap: see lpc_window_read */
-			uint32_t dat = be32_to_cpu(*(__be32 *)buf);
-
-			rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
-			chunk = 4;
-		} else {
-			uint8_t dat = *(uint8_t *)buf;
-
-			rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
-			chunk = 1;
-		}
-		if (rc) {
-			prlog(PR_ERR, "lpc_write failure %d to FW 0x%08x\n", rc, off);
-			return rc;
-		}
-		len -= chunk;
-		off += chunk;
-		buf += chunk;
-	}
-
-	return 0;
+	return lpc_fw_write(off, buf, len);
 }
 
 /* Best-effort asynchronous event handling by blocklevel callbacks */
diff --git a/libflash/mbox-flash.c b/libflash/mbox-flash.c
index 4c20f15f2..9e686c97d 100644
--- a/libflash/mbox-flash.c
+++ b/libflash/mbox-flash.c
@@ -147,79 +147,19 @@ static int lpc_window_read(struct mbox_flash_data *mbox_flash, uint32_t pos,
 			   void *buf, uint32_t len)
 {
 	uint32_t off = mbox_flash->read.lpc_addr + (pos - mbox_flash->read.cur_pos);
-	int rc;
 
 	prlog(PR_TRACE, "Reading at 0x%08x for 0x%08x offset: 0x%08x\n",
 			pos, len, off);
 
-	while(len) {
-		uint32_t chunk;
-		uint32_t dat;
-
-		/* XXX: make this read until it's aligned */
-		if (len > 3 && !(off & 3)) {
-			rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
-			if (!rc) {
-				/*
-				 * lpc_read swaps to CPU endian but it's not
-				 * really a 32-bit value, so convert back.
-				 */
-				*(__be32 *)buf = cpu_to_be32(dat);
-			}
-			chunk = 4;
-		} else {
-			rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
-			if (!rc)
-				*(uint8_t *)buf = dat;
-			chunk = 1;
-		}
-		if (rc) {
-			prlog(PR_ERR, "lpc_read failure %d to FW 0x%08x\n", rc, off);
-			return rc;
-		}
-		len -= chunk;
-		off += chunk;
-		buf += chunk;
-	}
-
-	return 0;
+	return lpc_fw_read(off, buf, len);
 }
 
 static int lpc_window_write(struct mbox_flash_data *mbox_flash, uint32_t pos,
 			    const void *buf, uint32_t len)
 {
 	uint32_t off = mbox_flash->write.lpc_addr + (pos - mbox_flash->write.cur_pos);
-	int rc;
-
-
-	prlog(PR_TRACE, "Writing at 0x%08x for 0x%08x offset: 0x%08x\n",
-			pos, len, off);
-
-	while(len) {
-		uint32_t chunk;
-
-		if (len > 3 && !(off & 3)) {
-			/* endian swap: see lpc_window_read */
-			uint32_t dat = be32_to_cpu(*(__be32 *)buf);
-
-			rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
-			chunk = 4;
-		} else {
-			uint8_t dat = *(uint8_t *)buf;
-
-			rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
-			chunk = 1;
-		}
-		if (rc) {
-			prlog(PR_ERR, "lpc_write failure %d to FW 0x%08x\n", rc, off);
-			return rc;
-		}
-		len -= chunk;
-		off += chunk;
-		buf += chunk;
-	}
 
-	return 0;
+	return lpc_fw_write(off, buf, len);
 }
 
 static uint64_t mbox_flash_mask(struct mbox_flash_data *mbox_flash)
diff --git a/libflash/test/mbox-server.c b/libflash/test/mbox-server.c
index 8a68cfff6..053be9894 100644
--- a/libflash/test/mbox-server.c
+++ b/libflash/test/mbox-server.c
@@ -142,6 +142,74 @@ int64_t lpc_write(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
 	return 0;
 }
 
+int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len);
+int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len)
+{
+	int rc;
+
+	while (len) {
+		uint32_t chunk;
+		uint32_t dat;
+
+		/* XXX: make this read until it's aligned */
+		if (len > 3 && !(off & 3)) {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
+			if (!rc) {
+				/*
+				 * lpc_read swaps to CPU endian but it's not
+				 * really a 32-bit value, so convert back.
+				 */
+				*(__be32 *)buf = cpu_to_be32(dat);
+			}
+			chunk = 4;
+		} else {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
+			if (!rc)
+				*(uint8_t *)buf = dat;
+			chunk = 1;
+		}
+		if (rc)
+			return rc;
+
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
+int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len);
+int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len)
+{
+	int rc;
+
+	while (len) {
+		uint32_t chunk;
+
+		if (len > 3 && !(off & 3)) {
+			/* endian swap: see lpc_window_write */
+			uint32_t dat = be32_to_cpu(*(__be32 *)buf);
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
+			chunk = 4;
+		} else {
+			uint8_t dat = *(uint8_t *)buf;
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
+			chunk = 1;
+		}
+		if (rc)
+			return rc;
+
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
 int bmc_mbox_register_attn(mbox_attn_cb handler, void *drv_data)
 {
 	mbox_data.attn = handler;
diff --git a/libflash/test/test-ipmi-hiomap.c b/libflash/test/test-ipmi-hiomap.c
index 6117e9dd4..b740a6f2b 100644
--- a/libflash/test/test-ipmi-hiomap.c
+++ b/libflash/test/test-ipmi-hiomap.c
@@ -223,6 +223,72 @@ int64_t lpc_read(enum OpalLPCAddressType addr_type __attribute__((unused)),
 	return 0;
 }
 
+int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len)
+{
+	int rc;
+
+	while (len) {
+		uint32_t chunk;
+		uint32_t dat;
+
+		/* XXX: make this read until it's aligned */
+		if (len > 3 && !(off & 3)) {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
+			if (!rc) {
+				/*
+				 * lpc_read swaps to CPU endian but it's not
+				 * really a 32-bit value, so convert back.
+				 */
+				*(__be32 *)buf = cpu_to_be32(dat);
+			}
+			chunk = 4;
+		} else {
+			rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
+			if (!rc)
+				*(uint8_t *)buf = dat;
+			chunk = 1;
+		}
+		if (rc)
+			return rc;
+
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
+int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len)
+{
+	int rc;
+
+	while (len) {
+		uint32_t chunk;
+
+		if (len > 3 && !(off & 3)) {
+			/* endian swap: see lpc_window_write */
+			uint32_t dat = be32_to_cpu(*(__be32 *)buf);
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
+			chunk = 4;
+		} else {
+			uint8_t dat = *(uint8_t *)buf;
+
+			rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
+			chunk = 1;
+		}
+		if (rc)
+			return rc;
+
+		len -= chunk;
+		off += chunk;
+		buf += chunk;
+	}
+
+	return 0;
+}
+
 static bool lpc_read_success(const uint8_t *buf, size_t len)
 {
 	if (len < 64) {
