diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index 0e635b3a6..797ec18ca 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -1972,9 +1972,14 @@ static int deassert_reset(struct target *target)
 		return wait_for_state(target, TARGET_RUNNING);
 }
 
-static int read_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+static int read_memory(struct target *target, const riscv_mem_access_info_t args)
 {
+	target_addr_t address = args.address;
+	uint32_t size = args.size;
+	uint32_t count = args.count;
+	uint8_t *buffer = args.buffer.read;
+	uint32_t increment = args.increment;
+
 	if (increment != size) {
 		LOG_ERROR("read_memory with custom increment not implemented");
 		return ERROR_NOT_IMPLEMENTED;
@@ -2142,9 +2147,18 @@ static int setup_write_memory(struct target *target, uint32_t size)
 	return ERROR_OK;
 }
 
-static int write_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer)
+static int write_memory(struct target *target, const riscv_mem_access_info_t args)
 {
+	if (args.increment != args.size) {
+		LOG_TARGET_ERROR(target, "Write increment size has to be equal to element size");
+		return ERROR_NOT_IMPLEMENTED;
+	}
+
+	target_addr_t address = args.address;
+	uint32_t size = args.size;
+	uint32_t count = args.count;
+	const uint8_t *buffer = args.buffer.write;
+
 	riscv011_info_t *info = get_info(target);
 	jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
 
@@ -2371,7 +2385,9 @@ static int init_target(struct command_context *cmd_ctx,
 {
 	LOG_DEBUG("init");
 	RISCV_INFO(generic_info);
+	/* TODO: replace read and write with single access function*/
 	generic_info->read_memory = read_memory;
+	generic_info->write_memory = write_memory;
 	generic_info->authdata_read = &riscv011_authdata_read;
 	generic_info->authdata_write = &riscv011_authdata_write;
 	generic_info->print_info = &riscv011_print_info;
@@ -2406,7 +2422,5 @@ struct target_type riscv011_target = {
 	.assert_reset = assert_reset,
 	.deassert_reset = deassert_reset,
 
-	.write_memory = write_memory,
-
 	.arch_state = arch_state,
 };
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 4ab3357dc..2a65fc794 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -64,10 +64,8 @@ static int register_read_direct(struct target *target, riscv_reg_t *value,
 		enum gdb_regno number);
 static int register_write_direct(struct target *target, enum gdb_regno number,
 		riscv_reg_t value);
-static int read_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
-static int write_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer);
+static int read_memory(struct target *target, const riscv_mem_access_info_t args);
+static int write_memory(struct target *target, const riscv_mem_access_info_t args);
 static bool riscv013_get_impebreak(const struct target *target);
 static unsigned int riscv013_get_progbufsize(const struct target *target);
 
@@ -1201,7 +1199,16 @@ static int scratch_read64(struct target *target, scratch_mem_t *scratch,
 		case SPACE_DMI_RAM:
 			{
 				uint8_t buffer[8] = {0};
-				if (read_memory(target, scratch->debug_address, 4, 2, buffer, 4) != ERROR_OK)
+				const riscv_mem_access_info_t args = {
+					.address = scratch->debug_address,
+					.buffer.read = buffer,
+					.size = 4,
+					.count = 2,
+					.increment = 4,
+					.is_read = true,
+					.is_virtual = false,
+				};
+				if (read_memory(target, args) != ERROR_OK)
 					return ERROR_FAIL;
 				*value = buffer[0] |
 					(((uint64_t) buffer[1]) << 8) |
@@ -1242,7 +1249,16 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
 					value >> 48,
 					value >> 56
 				};
-				if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK)
+				const riscv_mem_access_info_t args = {
+					.address = scratch->debug_address,
+					.buffer.write = buffer,
+					.size = 4,
+					.increment = 4,
+					.count = 2,
+					.is_read = false,
+					.is_virtual = false
+				};
+				if (write_memory(target, args) != ERROR_OK)
 					return ERROR_FAIL;
 			}
 			break;
@@ -2763,7 +2779,9 @@ static int init_target(struct command_context *cmd_ctx,
 	generic_info->dmi_read = &dmi_read;
 	generic_info->dmi_write = &dmi_write;
 	generic_info->get_dmi_address = &riscv013_get_dmi_address;
+	/* TODO: replace read and write with single access function*/
 	generic_info->read_memory = read_memory;
+	generic_info->write_memory = write_memory;
 	generic_info->data_bits = &riscv013_data_bits;
 	generic_info->print_info = &riscv013_print_info;
 	generic_info->get_impebreak = &riscv013_get_impebreak;
@@ -3124,19 +3142,18 @@ static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *
 	return ERROR_OK;
 }
 
-static int read_memory_bus_v0(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+static int read_memory_bus_v0(struct target *target, const riscv_mem_access_info_t args)
 {
-	if (size != increment) {
+	if (args.size != args.increment) {
 		LOG_TARGET_ERROR(target, "sba v0 reads only support size==increment");
 		return ERROR_NOT_IMPLEMENTED;
 	}
 
 	LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
-			TARGET_PRIxADDR, size, count, address);
-	uint8_t *t_buffer = buffer;
-	riscv_addr_t cur_addr = address;
-	riscv_addr_t fin_addr = address + (count * size);
+			TARGET_PRIxADDR, args.size, args.count, args.address);
+	uint8_t *t_buffer = args.buffer.read;
+	riscv_addr_t cur_addr = args.address;
+	riscv_addr_t fin_addr = args.address + (args.count * args.size);
 	uint32_t access = 0;
 
 	const int DM_SBCS_SBSINGLEREAD_OFFSET = 20;
@@ -3146,13 +3163,13 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
 	const uint32_t DM_SBCS_SBAUTOREAD = (0x1U << DM_SBCS_SBAUTOREAD_OFFSET);
 
 	/* ww favorise one off reading if there is an issue */
-	if (count == 1) {
-		for (uint32_t i = 0; i < count; i++) {
+	if (args.count == 1) {
+		for (uint32_t i = 0; i < args.count; i++) {
 			if (dm_read(target, &access, DM_SBCS) != ERROR_OK)
 				return ERROR_FAIL;
 			dm_write(target, DM_SBADDRESS0, cur_addr);
-			/* size/2 matching the bit access of the spec 0.13 */
-			access = set_field(access, DM_SBCS_SBACCESS, size/2);
+			/* size/2 matching the bit sbaccess of the spec 0.13 */
+			access = set_field(access, DM_SBCS_SBACCESS, args.size / 2);
 			access = set_field(access, DM_SBCS_SBSINGLEREAD, 1);
 			LOG_TARGET_DEBUG(target, "read_memory: sab: access:  0x%08x", access);
 			dm_write(target, DM_SBCS, access);
@@ -3161,9 +3178,9 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
 			if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
 				return ERROR_FAIL;
 			LOG_TARGET_DEBUG(target, "read_memory: sab: value:  0x%08x", value);
-			buf_set_u32(t_buffer, 0, 8 * size, value);
-			t_buffer += size;
-			cur_addr += size;
+			buf_set_u32(t_buffer, 0, 8 * args.size, value);
+			t_buffer += args.size;
+			cur_addr += args.size;
 		}
 		return ERROR_OK;
 	}
@@ -3176,7 +3193,7 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
 	dm_write(target, DM_SBADDRESS0, cur_addr);
 	/* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement
 	 * size/2 matching the bit access of the spec 0.13 */
-	access = set_field(access, DM_SBCS_SBACCESS, size/2);
+	access = set_field(access, DM_SBCS_SBACCESS, args.size / 2);
 	access = set_field(access, DM_SBCS_SBAUTOREAD, 1);
 	access = set_field(access, DM_SBCS_SBSINGLEREAD, 1);
 	access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1);
@@ -3185,21 +3202,21 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
 
 	while (cur_addr < fin_addr) {
 		LOG_TARGET_DEBUG(target, "sab:autoincrement:\r\n\tsize: %d\tcount:%d\taddress: 0x%08"
-				PRIx64, size, count, cur_addr);
+				PRIx64, args.size, args.count, cur_addr);
 		/* read */
 		uint32_t value;
 		if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
 			return ERROR_FAIL;
-		buf_set_u32(t_buffer, 0, 8 * size, value);
-		cur_addr += size;
-		t_buffer += size;
+		buf_set_u32(t_buffer, 0, 8 * args.size, value);
+		cur_addr += args.size;
+		t_buffer += args.size;
 
 		/* if we are reaching last address, we must clear autoread */
-		if (cur_addr == fin_addr && count != 1) {
+		if (cur_addr == fin_addr && args.count != 1) {
 			dm_write(target, DM_SBCS, 0);
 			if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
 				return ERROR_FAIL;
-			buf_set_u32(t_buffer, 0, 8 * size, value);
+			buf_set_u32(t_buffer, 0, 8 * args.size, value);
 		}
 	}
 
@@ -3213,35 +3230,34 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
 /**
  * Read the requested memory using the system bus interface.
  */
-static int read_memory_bus_v1(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+static int read_memory_bus_v1(struct target *target, const riscv_mem_access_info_t args)
 {
-	if (increment != size && increment != 0) {
+	if (args.increment != args.size && args.increment != 0) {
 		LOG_TARGET_ERROR(target, "sba v1 reads only support increment of size or 0");
 		return ERROR_NOT_IMPLEMENTED;
 	}
 
-	assert(size <= 16);
-	assert(IS_PWR_OF_2(size));
+	assert(args.size <= 16);
+	assert(IS_PWR_OF_2(args.size));
 
 	dm013_info_t *dm = get_dm(target);
 	if (!dm)
 		return ERROR_FAIL;
 
 	RISCV013_INFO(info);
-	target_addr_t next_address = address;
-	target_addr_t end_address = address + (increment ? count : 1) * size;
+	target_addr_t next_address = args.address;
+	target_addr_t end_address = args.address + (args.increment ? args.count : 1) * args.size;
 
 	/* TODO: Reading all the elements in a single batch will boost the
 	 * performance.
 	 */
 	while (next_address < end_address) {
 		uint32_t sbcs_write = set_field(0, DM_SBCS_SBREADONADDR, 1);
-		sbcs_write |= sb_sbaccess(size);
-		if (increment == size)
+		sbcs_write |= sb_sbaccess(args.size);
+		if (args.increment == args.size)
 			sbcs_write = set_field(sbcs_write, DM_SBCS_SBAUTOINCREMENT, 1);
-		if (count > 1)
-			sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, count > 1);
+		if (args.count > 1)
+			sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, args.count > 1);
 		if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK)
 			return ERROR_FAIL;
 
@@ -3259,8 +3275,8 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 		 * be unnecessary.
 		 */
 		uint32_t sbvalue[4] = {0};
-		for (uint32_t i = (next_address - address) / size; i < count - 1; i++) {
-			const uint32_t size_in_words = DIV_ROUND_UP(size, 4);
+		for (uint32_t i = (next_address - args.address) / args.size; i < args.count - 1; i++) {
+			const uint32_t size_in_words = DIV_ROUND_UP(args.size, 4);
 			struct riscv_batch *batch = riscv_batch_alloc(target, size_in_words);
 			/* Read of sbdata0 must be performed as last because it
 			 * starts the new bus data transfer
@@ -3280,16 +3296,17 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 			const size_t last_key = batch->read_keys_used - 1;
 			for (size_t k = 0; k <= last_key; ++k) {
 				sbvalue[k] = riscv_batch_get_dmi_read_data(batch, last_key - k);
-				buf_set_u32(buffer + i * size + k * 4, 0, MIN(32, 8 * size), sbvalue[k]);
+				buf_set_u32(args.buffer.read + i * args.size + k * 4,
+						0, MIN(32, 8 * args.size), sbvalue[k]);
 			}
 
 			riscv_batch_free(batch);
-			const target_addr_t read_addr = address + i * increment;
-			log_memory_access(read_addr, sbvalue, size, true);
+			const target_addr_t read_addr = args.address + i * args.increment;
+			log_memory_access(read_addr, sbvalue, args.size, true);
 		}
 
 		uint32_t sbcs_read = 0;
-		if (count > 1) {
+		if (args.count > 1) {
 			/* "Writes to sbcs while sbbusy is high result in undefined behavior.
 			 * A debugger must not write to sbcs until it reads sbbusy as 0." */
 			if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK)
@@ -3303,8 +3320,9 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 		/* Read the last word, after we disabled sbreadondata if necessary. */
 		if (!get_field(sbcs_read, DM_SBCS_SBERROR) &&
 				!get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
-			if (read_memory_bus_word(target, address + (count - 1) * increment, size,
-						buffer + (count - 1) * size) != ERROR_OK)
+			if (read_memory_bus_word(target, args.address
+					+ (args.count - 1) * args.increment, args.size,
+					args.buffer.read + (args.count - 1) * args.size) != ERROR_OK)
 				return ERROR_FAIL;
 
 			if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK)
@@ -3324,9 +3342,9 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 				/* Read the value for the last address. It's
 				 * sitting in the register for us, but we read it
 				 * too early (sbbusyerror became set). */
-				target_addr_t current_address = next_address - (increment ? size : 0);
-				if (read_memory_bus_word(target, current_address, size,
-							buffer + current_address - address) != ERROR_OK)
+				target_addr_t current_address = next_address - (args.increment ? args.size : 0);
+				if (read_memory_bus_word(target, current_address, args.size,
+							args.buffer.read + current_address - args.address) != ERROR_OK)
 					return ERROR_FAIL;
 			}
 
@@ -3463,41 +3481,41 @@ const char *mem_access_result_to_str(mem_access_result_t status)
 }
 
 static mem_access_result_t mem_should_skip_progbuf(struct target *target,
-	target_addr_t address, uint32_t size, bool is_read)
+	const riscv_mem_access_info_t args)
 {
 	if (!has_sufficient_progbuf(target, 1)) {
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf "
-				"- progbuf not present", is_read ? "read" : "write");
+				"- progbuf not present", args.is_read ? "read" : "write");
 		return MEM_ACCESS_SKIPPED_PROGBUF_NOT_PRESENT;
 	}
 	if (!has_sufficient_progbuf(target, 3)) {
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - insufficient progbuf size.",
-				is_read ? "read" : "write");
+				args.is_read ? "read" : "write");
 		return MEM_ACCESS_SKIPPED_PROGBUF_INSUFFICIENT;
 	}
 	if (target->state != TARGET_HALTED) {
 		LOG_TARGET_DEBUG(target,
 			"Skipping mem %s via progbuf - target not halted.",
-			is_read ? "read" : "write");
+			args.is_read ? "read" : "write");
 		return MEM_ACCESS_SKIPPED_TARGET_NOT_HALTED;
 	}
-	if (riscv_xlen(target) < size * 8) {
+	if (riscv_xlen(target) < args.size * 8) {
 		LOG_TARGET_DEBUG(target,
 			"Skipping mem %s via progbuf - "
 			"XLEN (%d) is too short for %d-bit memory access.",
-			is_read ? "read" : "write", riscv_xlen(target), size * 8);
+			args.is_read ? "read" : "write", riscv_xlen(target), args.size * 8);
 		return MEM_ACCESS_SKIPPED_XLEN_TOO_SHORT;
 	}
-	if (size > 8) {
+	if (args.size > 8) {
 		LOG_TARGET_DEBUG(target,
 			"Skipping mem %s via progbuf - unsupported size.",
-			is_read ? "read" : "write");
+			args.is_read ? "read" : "write");
 		return MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE;
 	}
-	if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+	if ((sizeof(args.address) * 8 > riscv_xlen(target)) && (args.address >> riscv_xlen(target))) {
 		LOG_TARGET_DEBUG(target,
 			"Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
-			is_read ? "read" : "write", riscv_xlen(target));
+			args.is_read ? "read" : "write", riscv_xlen(target));
 		return MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS;
 	}
 
@@ -3505,22 +3523,22 @@ static mem_access_result_t mem_should_skip_progbuf(struct target *target,
 }
 
 static mem_access_result_t
-mem_should_skip_sysbus(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t increment, bool is_read)
+mem_should_skip_sysbus(struct target *target, const riscv_mem_access_info_t args)
 {
 	RISCV013_INFO(info);
-	if (!sba_supports_access(target, size)) {
+	if (!sba_supports_access(target, args.size)) {
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - unsupported size.",
-				is_read ? "read" : "write");
+				args.is_read ? "read" : "write");
 		return MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE;
 	}
 	unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
-	if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) {
+	if ((sizeof(args.address) * 8 > sbasize) && (args.address >> sbasize)) {
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - sba only supports %u-bit address.",
-				is_read ? "read" : "write", sbasize);
+				args.is_read ? "read" : "write", sbasize);
 		return MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS;
 	}
-	if (is_read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
+	if (args.is_read && args.increment != args.size
+			&& (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || args.increment != 0)) {
 		LOG_TARGET_DEBUG(target, "Skipping mem read via system bus - "
 				"sba reads only support size==increment or also size==0 for sba v1.");
 		return MEM_ACCESS_SKIPPED_UNSUPPORTED_INCREMENT_SIZE;
@@ -3530,22 +3548,21 @@ mem_should_skip_sysbus(struct target *target, target_addr_t address,
 }
 
 static mem_access_result_t
-mem_should_skip_abstract(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t increment, bool is_read)
+mem_should_skip_abstract(struct target *target, const riscv_mem_access_info_t args)
 {
-	if (size > 8) {
+	if (args.size > 8) {
 		/* TODO: Add 128b support if it's ever used. Involves modifying
 				 read/write_abstract_arg() to work on two 64b values. */
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - unsupported size: %d bits",
-				is_read ? "read" : "write", size * 8);
+				args.is_read ? "read" : "write", args.size * 8);
 		return MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE;
 	}
-	if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+	if ((sizeof(args.address) * 8 > riscv_xlen(target)) && (args.address >> riscv_xlen(target))) {
 		LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
-				is_read ? "read" : "write", riscv_xlen(target));
+				args.is_read ? "read" : "write", riscv_xlen(target));
 		return MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS;
 	}
-	if (is_read && size != increment) {
+	if (args.is_read && args.size != args.increment) {
 		LOG_TARGET_ERROR(target, "Skipping mem read via abstract access - "
 				"abstract command reads only support size==increment.");
 		return MEM_ACCESS_SKIPPED_UNSUPPORTED_INCREMENT_SIZE;
@@ -3559,38 +3576,36 @@ mem_should_skip_abstract(struct target *target, target_addr_t address,
  * aamsize fields in the memory access abstract command.
  */
 static mem_access_result_t
-read_memory_abstract(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+read_memory_abstract(struct target *target, const riscv_mem_access_info_t args)
 {
-	mem_access_result_t skip_reason =
-		mem_should_skip_abstract(target, address, size, increment, /* is_read = */ true);
+	mem_access_result_t skip_reason = mem_should_skip_abstract(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
 	RISCV013_INFO(info);
 	bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
-	LOG_TARGET_DEBUG(target, "Reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
-			  size, address);
+	LOG_TARGET_DEBUG(target, "Reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, args.count,
+			  args.size, args.address);
 
-	memset(buffer, 0, count * size);
+	memset(args.buffer.read, 0, args.count * args.size);
 
 	/* Convert the size (bytes) to width (bits) */
-	unsigned int width = size << 3;
+	unsigned int width = args.size << 3;
 
 	/* Create the command (physical address, postincrement, read) */
 	uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false);
 
 	/* Execute the reads */
-	uint8_t *p = buffer;
+	uint8_t *p = args.buffer.read;
 	int result = ERROR_OK;
 	bool updateaddr = true;
 	unsigned int width32 = MAX(width, 32);
-	for (uint32_t c = 0; c < count; c++) {
+	for (uint32_t c = 0; c < args.count; c++) {
 		/* Update the address if it is the first time or aampostincrement is not supported by the target. */
 		if (updateaddr) {
 			/* Set arg1 to the address: address + c * size */
-			result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
+			result = write_abstract_arg(target, 1, args.address + c * args.size, riscv_xlen(target));
 			if (result != ERROR_OK) {
 				LOG_TARGET_ERROR(target, "Failed to write arg1.");
 				return MEM_ACCESS_FAILED_DM_ACCESS_FAILED;
@@ -3611,7 +3626,7 @@ read_memory_abstract(struct target *target, target_addr_t address,
 				if (result != ERROR_OK)
 					return MEM_ACCESS_FAILED_DM_ACCESS_FAILED;
 
-				if (new_address == address + size) {
+				if (new_address == args.address + args.size) {
 					LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
 					info->has_aampostincrement = YNM_YES;
 				} else {
@@ -3640,11 +3655,11 @@ read_memory_abstract(struct target *target, target_addr_t address,
 		result = read_abstract_arg(target, &value, 0, width32);
 		if (result != ERROR_OK)
 			return MEM_ACCESS_FAILED_DM_ACCESS_FAILED;
-		buf_set_u64(p, 0, 8 * size, value);
+		buf_set_u64(p, 0, 8 * args.size, value);
 
 		if (info->has_aampostincrement == YNM_YES)
 			updateaddr = false;
-		p += size;
+		p += args.size;
 	}
 
 	return MEM_ACCESS_OK;
@@ -3656,12 +3671,9 @@ read_memory_abstract(struct target *target, target_addr_t address,
  * byte aamsize fields in the memory access abstract command.
  */
 static mem_access_result_t
-write_memory_abstract(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t count, const uint8_t *buffer)
+write_memory_abstract(struct target *target, const riscv_mem_access_info_t args)
 {
-	mem_access_result_t skip_reason =
-		mem_should_skip_abstract(target, address, size,
-			/* increment = */ 0, /* is_read = */ false);
+	mem_access_result_t skip_reason = mem_should_skip_abstract(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
@@ -3669,21 +3681,21 @@ write_memory_abstract(struct target *target, target_addr_t address,
 	int result = ERROR_OK;
 	bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
-	LOG_TARGET_DEBUG(target, "writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
-			  size, address);
+	LOG_TARGET_DEBUG(target, "writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, args.count,
+			  args.size, args.address);
 
 	/* Convert the size (bytes) to width (bits) */
-	unsigned int width = size << 3;
+	unsigned int width = args.size << 3;
 
 	/* Create the command (physical address, postincrement, write) */
 	uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true);
 
 	/* Execute the writes */
-	const uint8_t *p = buffer;
+	const uint8_t *p = args.buffer.write;
 	bool updateaddr = true;
-	for (uint32_t c = 0; c < count; c++) {
+	for (uint32_t c = 0; c < args.count; c++) {
 		/* Move data to arg0 */
-		riscv_reg_t value = buf_get_u64(p, 0, 8 * size);
+		riscv_reg_t value = buf_get_u64(p, 0, 8 * args.size);
 		result = write_abstract_arg(target, 0, value, riscv_xlen(target));
 		if (result != ERROR_OK) {
 			LOG_TARGET_ERROR(target, "Failed to write arg0.");
@@ -3693,7 +3705,7 @@ write_memory_abstract(struct target *target, target_addr_t address,
 		/* Update the address if it is the first time or aampostincrement is not supported by the target. */
 		if (updateaddr) {
 			/* Set arg1 to the address: address + c * size */
-			result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
+			result = write_abstract_arg(target, 1, args.address + c * args.size, riscv_xlen(target));
 			if (result != ERROR_OK) {
 				LOG_TARGET_ERROR(target, "Failed to write arg1.");
 				return MEM_ACCESS_FAILED_DM_ACCESS_FAILED;
@@ -3714,7 +3726,7 @@ write_memory_abstract(struct target *target, target_addr_t address,
 				if (result != ERROR_OK)
 					return MEM_ACCESS_FAILED_DM_ACCESS_FAILED;
 
-				if (new_address == address + size) {
+				if (new_address == args.address + args.size) {
 					LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
 					info->has_aampostincrement = YNM_YES;
 				} else {
@@ -3740,7 +3752,7 @@ write_memory_abstract(struct target *target, target_addr_t address,
 
 		if (info->has_aampostincrement == YNM_YES)
 			updateaddr = false;
-		p += size;
+		p += args.size;
 	}
 
 	return MEM_ACCESS_OK;
@@ -3817,13 +3829,6 @@ static int read_memory_progbuf_inner_startup(struct target *target,
 	return ERROR_FAIL;
 }
 
-struct memory_access_info {
-	uint8_t *buffer_address;
-	target_addr_t target_address;
-	uint32_t element_size;
-	uint32_t increment;
-};
-
 /**
  * This function attempts to restore the pipeline after a busy on abstract
  * access.
@@ -3834,7 +3839,7 @@ struct memory_access_info {
  */
 static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
 		uint32_t start_index, uint32_t *elements_read,
-		struct memory_access_info access)
+		const riscv_mem_access_info_t args)
 {
 	int res = riscv013_clear_abstract_error(target);
 	if (res != ERROR_OK)
@@ -3849,7 +3854,7 @@ static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
 	/* See how far we got by reading s0/a0 */
 	uint32_t index_on_target;
 
-	if (/*is_repeated_read*/ access.increment == 0) {
+	if (/*is_repeated_read*/ args.increment == 0) {
 		/* s0 is constant, a0 is incremented by one each execution */
 		riscv_reg_t counter;
 
@@ -3861,8 +3866,8 @@ static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
 
 		if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK)
 			return ERROR_FAIL;
-		index_on_target = (address_on_target - access.target_address) /
-			access.increment;
+		index_on_target = (address_on_target - args.address) /
+			args.increment;
 	}
 
 	/* According to the spec, if an abstract command fails, one can't make any
@@ -3880,10 +3885,10 @@ static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
 	*elements_read = next_index - start_index;
 	LOG_TARGET_WARNING(target, "Re-reading memory from addresses 0x%"
 			TARGET_PRIxADDR " and 0x%" TARGET_PRIxADDR ".",
-			access.target_address + access.increment * next_index,
-			access.target_address + access.increment * (next_index + 1));
-	return read_memory_progbuf_inner_startup(target, access.target_address,
-			access.increment, next_index);
+			args.address + args.increment * next_index,
+			args.address + args.increment * (next_index + 1));
+	return read_memory_progbuf_inner_startup(target, args.address,
+			args.increment, next_index);
 }
 
 /**
@@ -3891,7 +3896,7 @@ static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
  */
 static int read_memory_progbuf_inner_on_dmi_busy(struct target *target,
 		uint32_t start_index, uint32_t next_start_index,
-		struct memory_access_info access)
+		const riscv_mem_access_info_t args)
 {
 	LOG_TARGET_DEBUG(target, "DMI_STATUS_BUSY encountered in batch. Memory read [%"
 			PRIu32 ", %" PRIu32 ")", start_index, next_start_index);
@@ -3900,8 +3905,8 @@ static int read_memory_progbuf_inner_on_dmi_busy(struct target *target,
 
 	if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK)
 		return ERROR_FAIL;
-	return read_memory_progbuf_inner_startup(target, access.target_address,
-			access.increment, next_start_index);
+	return read_memory_progbuf_inner_startup(target, args.address,
+			args.increment, next_start_index);
 }
 
 /**
@@ -3910,9 +3915,9 @@ static int read_memory_progbuf_inner_on_dmi_busy(struct target *target,
 static int read_memory_progbuf_inner_extract_batch_data(struct target *target,
 		const struct riscv_batch *batch,
 		uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read,
-		struct memory_access_info access)
+		const riscv_mem_access_info_t args)
 {
-	const bool two_reads_per_element = access.element_size > 4;
+	const bool two_reads_per_element = args.size > 4;
 	const uint32_t reads_per_element = (two_reads_per_element ? 2 : 1);
 	assert(!two_reads_per_element || riscv_xlen(target) == 64);
 	assert(elements_to_read <= UINT32_MAX / reads_per_element);
@@ -3922,7 +3927,7 @@ static int read_memory_progbuf_inner_extract_batch_data(struct target *target,
 		case DMI_STATUS_BUSY:
 			*elements_read = curr_idx - start_index;
 			return read_memory_progbuf_inner_on_dmi_busy(target, start_index, curr_idx
-					, access);
+					, args);
 		case DMI_STATUS_FAILED:
 			LOG_TARGET_DEBUG(target,
 					"Batch memory read encountered DMI_STATUS_FAILED on read %"
@@ -3934,11 +3939,11 @@ static int read_memory_progbuf_inner_extract_batch_data(struct target *target,
 			assert(0);
 		}
 		const uint32_t value = riscv_batch_get_dmi_read_data(batch, read);
-		uint8_t * const curr_buff = access.buffer_address +
-			curr_idx * access.element_size;
-		const target_addr_t curr_addr = access.target_address +
-			curr_idx * access.increment;
-		const uint32_t size = access.element_size;
+		uint8_t * const curr_buff = args.buffer.read +
+			curr_idx * args.size;
+		const target_addr_t curr_addr = args.address +
+			curr_idx * args.increment;
+		const uint32_t size = args.size;
 
 		assert(size <= 8);
 		const bool is_odd_read = read % 2;
@@ -3965,7 +3970,7 @@ static int read_memory_progbuf_inner_extract_batch_data(struct target *target,
  * - DM_ABSTRACTAUTO_AUTOEXECDATA is set.
  */
 static int read_memory_progbuf_inner_run_and_process_batch(struct target *target,
-		struct riscv_batch *batch, struct memory_access_info access,
+		struct riscv_batch *batch, const riscv_mem_access_info_t args,
 		uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read)
 {
 	dm013_info_t *dm = get_dm(target);
@@ -3993,7 +3998,7 @@ static int read_memory_progbuf_inner_run_and_process_batch(struct target *target
 	case CMDERR_BUSY:
 		LOG_TARGET_DEBUG(target, "memory read resulted in busy response");
 		if (read_memory_progbuf_inner_on_ac_busy(target, start_index,
-					&elements_to_extract_from_batch, access)
+					&elements_to_extract_from_batch, args)
 				!= ERROR_OK)
 			return ERROR_FAIL;
 		break;
@@ -4004,7 +4009,7 @@ static int read_memory_progbuf_inner_run_and_process_batch(struct target *target
 	}
 
 	if (read_memory_progbuf_inner_extract_batch_data(target, batch, start_index,
-				elements_to_extract_from_batch, elements_read, access) != ERROR_OK)
+				elements_to_extract_from_batch, elements_read, args) != ERROR_OK)
 		return ERROR_FAIL;
 
 	return ERROR_OK;
@@ -4034,7 +4039,7 @@ static uint32_t read_memory_progbuf_inner_fill_batch(struct riscv_batch *batch,
 }
 
 static int read_memory_progbuf_inner_try_to_read(struct target *target,
-		struct memory_access_info access, uint32_t *elements_read,
+		const riscv_mem_access_info_t args, uint32_t *elements_read,
 		uint32_t index, uint32_t loop_count)
 {
 	struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE);
@@ -4042,10 +4047,10 @@ static int read_memory_progbuf_inner_try_to_read(struct target *target,
 		return ERROR_FAIL;
 
 	const uint32_t elements_to_read = read_memory_progbuf_inner_fill_batch(batch,
-			loop_count - index, access.element_size);
+			loop_count - index, args.size);
 
 	int result = read_memory_progbuf_inner_run_and_process_batch(target, batch,
-			access, index, elements_to_read, elements_read);
+			args, index, elements_to_read, elements_read);
 	riscv_batch_free(batch);
 	return result;
 }
@@ -4055,20 +4060,20 @@ static int read_memory_progbuf_inner_try_to_read(struct target *target,
  * with the address argument equal to curr_target_address.
  */
 static int read_memory_progbuf_inner_ensure_forward_progress(struct target *target,
-		struct memory_access_info access, uint32_t start_index)
+		const riscv_mem_access_info_t args, uint32_t start_index)
 {
 	LOG_TARGET_DEBUG(target,
 			"Executing one loop iteration to ensure forward progress (index=%"
 			PRIu32 ")", start_index);
-	const target_addr_t curr_target_address = access.target_address +
-		start_index * access.increment;
-	uint8_t * const curr_buffer_address = access.buffer_address +
-		start_index * access.element_size;
-	const struct memory_access_info curr_access = {
-		.buffer_address = curr_buffer_address,
-		.target_address = curr_target_address,
-		.element_size = access.element_size,
-		.increment = access.increment,
+	const target_addr_t curr_target_address = args.address +
+		start_index * args.increment;
+	uint8_t * const curr_buffer_address = args.buffer.read +
+		start_index * args.size;
+	const riscv_mem_access_info_t curr_access = {
+		.buffer.read = curr_buffer_address,
+		.address = curr_target_address,
+		.size = args.size,
+		.increment = args.increment,
 	};
 	uint32_t elements_read;
 	if (read_memory_progbuf_inner_try_to_read(target, curr_access, &elements_read,
@@ -4086,13 +4091,13 @@ static int read_memory_progbuf_inner_ensure_forward_progress(struct target *targ
 	return ERROR_OK;
 }
 
-static void set_buffer_and_log_read(struct memory_access_info access,
+static void set_buffer_and_log_read(const riscv_mem_access_info_t args,
 		uint32_t index, uint64_t value)
 {
-	uint8_t * const buffer = access.buffer_address;
-	const uint32_t size = access.element_size;
-	const uint32_t increment = access.increment;
-	const target_addr_t address = access.target_address;
+	uint8_t * const buffer = args.buffer.read;
+	const uint32_t size = args.size;
+	const uint32_t increment = args.increment;
+	const target_addr_t address = args.address;
 
 	assert(size <= 8);
 	buf_set_u64(buffer + index * size, 0, 8 * size, value);
@@ -4101,25 +4106,25 @@ static void set_buffer_and_log_read(struct memory_access_info access,
 }
 
 static int read_word_from_dm_data_regs(struct target *target,
-		struct memory_access_info access, uint32_t index)
+		const riscv_mem_access_info_t args, uint32_t index)
 {
-	assert(access.element_size <= 8);
+	assert(args.size <= 8);
 	uint64_t value;
 	int result = read_abstract_arg(target, &value, /*index*/ 0,
-			access.element_size > 4 ? 64 : 32);
+			args.size > 4 ? 64 : 32);
 	if (result == ERROR_OK)
-		set_buffer_and_log_read(access, index, value);
+		set_buffer_and_log_read(args, index, value);
 	return result;
 }
 
 static int read_word_from_s1(struct target *target,
-		struct memory_access_info access, uint32_t index)
+		const riscv_mem_access_info_t args, uint32_t index)
 {
 	uint64_t value;
 
 	if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK)
 		return ERROR_FAIL;
-	set_buffer_and_log_read(access, index, value);
+	set_buffer_and_log_read(args, index, value);
 	return ERROR_OK;
 }
 
@@ -4182,16 +4187,16 @@ static int read_memory_progbuf_inner_fill_progbuf(struct target *target,
  * is encountered in the process.
  */
 static int read_memory_progbuf_inner(struct target *target,
-		struct memory_access_info access, uint32_t count, bool mprven)
+		const riscv_mem_access_info_t args, bool mprven)
 {
-	assert(count > 1 && "If count == 1, read_memory_progbuf_inner_one must be called");
+	assert(args.count > 1 && "If count == 1, read_memory_progbuf_inner_one must be called");
 
-	if (read_memory_progbuf_inner_fill_progbuf(target, access.increment,
-				access.element_size, mprven) != ERROR_OK)
+	if (read_memory_progbuf_inner_fill_progbuf(target, args.increment,
+				args.size, mprven) != ERROR_OK)
 		return ERROR_FAIL;
 
-	if (read_memory_progbuf_inner_startup(target, access.target_address,
-				access.increment, /*index*/ 0)
+	if (read_memory_progbuf_inner_startup(target, args.address,
+				args.increment, /*index*/ 0)
 			!= ERROR_OK)
 		return ERROR_FAIL;
 	/* The program in program buffer is executed twice during
@@ -4204,17 +4209,17 @@ static int read_memory_progbuf_inner(struct target *target,
 	 * No need to execute the program any more, since S1 will already contain
 	 * M[address + increment * (count - 1)] and we can read it directly.
 	 */
-	const uint32_t loop_count = count - 2;
+	const uint32_t loop_count = args.count - 2;
 
 	for (uint32_t index = 0; index < loop_count;) {
 		uint32_t elements_read;
-		if (read_memory_progbuf_inner_try_to_read(target, access, &elements_read,
+		if (read_memory_progbuf_inner_try_to_read(target, args, &elements_read,
 					index, loop_count) != ERROR_OK) {
 			dm_write(target, DM_ABSTRACTAUTO, 0);
 			return ERROR_FAIL;
 		}
 		if (elements_read == 0) {
-			if (read_memory_progbuf_inner_ensure_forward_progress(target, access,
+			if (read_memory_progbuf_inner_ensure_forward_progress(target, args,
 						index) != ERROR_OK) {
 				dm_write(target, DM_ABSTRACTAUTO, 0);
 				return ERROR_FAIL;
@@ -4228,11 +4233,11 @@ static int read_memory_progbuf_inner(struct target *target,
 		return ERROR_FAIL;
 
 	/* Read the penultimate word. */
-	if (read_word_from_dm_data_regs(target, access, count - 2)
+	if (read_word_from_dm_data_regs(target, args, args.count - 2)
 			!= ERROR_OK)
 		return ERROR_FAIL;
 	/* Read the last word. */
-	return read_word_from_s1(target, access, count - 1);
+	return read_word_from_s1(target, args, args.count - 1);
 }
 
 /**
@@ -4240,7 +4245,7 @@ static int read_memory_progbuf_inner(struct target *target,
  * program doesn't need to increment.
  */
 static int read_memory_progbuf_inner_one(struct target *target,
-		struct memory_access_info access, bool mprven)
+		const riscv_mem_access_info_t args, bool mprven)
 {
 	if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK)
 		return ERROR_FAIL;
@@ -4249,7 +4254,7 @@ static int read_memory_progbuf_inner_one(struct target *target,
 
 	riscv_program_init(&program, target);
 	if (riscv_program_load_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S1, 0,
-				access.element_size, mprven) != ERROR_OK)
+				args.size, mprven) != ERROR_OK)
 		return ERROR_FAIL;
 	if (riscv_program_ebreak(&program) != ERROR_OK)
 		return ERROR_FAIL;
@@ -4257,7 +4262,7 @@ static int read_memory_progbuf_inner_one(struct target *target,
 		return ERROR_FAIL;
 
 	/* Write address to S1, and execute buffer. */
-	if (write_abstract_arg(target, 0, access.target_address, riscv_xlen(target))
+	if (write_abstract_arg(target, 0, args.address, riscv_xlen(target))
 			!= ERROR_OK)
 		return ERROR_FAIL;
 	uint32_t command = riscv013_access_register_command(target, GDB_REGNO_S1,
@@ -4267,30 +4272,28 @@ static int read_memory_progbuf_inner_one(struct target *target,
 	if (riscv013_execute_abstract_command(target, command, &cmderr) != ERROR_OK)
 		return ERROR_FAIL;
 
-	return read_word_from_s1(target, access, 0);
+	return read_word_from_s1(target, args, 0);
 }
 
 /**
  * Read the requested memory, silently handling memory access errors.
  */
 static mem_access_result_t
-read_memory_progbuf(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+read_memory_progbuf(struct target *target, const riscv_mem_access_info_t args)
 {
-	mem_access_result_t skip_reason =
-		mem_should_skip_progbuf(target, address, size, /* is_read = */ true);
+	mem_access_result_t skip_reason = mem_should_skip_progbuf(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
 	LOG_TARGET_DEBUG(target, "reading %" PRIu32 " elements of %" PRIu32
-			" bytes from 0x%" TARGET_PRIxADDR, count, size, address);
+			" bytes from 0x%" TARGET_PRIxADDR, args.count, args.size, args.address);
 
 	if (dm013_select_target(target) != ERROR_OK)
 		return MEM_ACCESS_SKIPPED_TARGET_SELECT_FAILED;
 
 	select_dmi(target);
 
-	memset(buffer, 0, count*size);
+	memset(args.buffer.read, 0, args.count * args.size);
 
 	if (execute_autofence(target) != ERROR_OK)
 		return MEM_ACCESS_SKIPPED_FENCE_EXEC_FAILED;
@@ -4302,15 +4305,9 @@ read_memory_progbuf(struct target *target, target_addr_t address,
 
 	const bool mprven = riscv_virt2phys_mode_is_hw(target)
 			&& get_field(mstatus, MSTATUS_MPRV);
-	const struct memory_access_info access = {
-		.target_address = address,
-		.increment = increment,
-		.buffer_address = buffer,
-		.element_size = size,
-	};
-	int result = (count == 1) ?
-		read_memory_progbuf_inner_one(target, access, mprven) :
-		read_memory_progbuf_inner(target, access, count, mprven);
+	int result = (args.count == 1) ?
+		read_memory_progbuf_inner_one(target, args, mprven) :
+		read_memory_progbuf_inner(target, args, mprven);
 
 	if (mstatus != mstatus_old &&
 			register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old) != ERROR_OK)
@@ -4320,36 +4317,34 @@ read_memory_progbuf(struct target *target, target_addr_t address,
 }
 
 static mem_access_result_t
-read_memory_sysbus(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+read_memory_sysbus(struct target *target, const riscv_mem_access_info_t args)
 {
-	mem_access_result_t skip_reason =
-		mem_should_skip_sysbus(target, address, size, increment, /* is_read = */ true);
+	mem_access_result_t skip_reason = mem_should_skip_sysbus(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
 	int ret = ERROR_FAIL;
 	uint64_t sbver = get_field(get_info(target)->sbcs, DM_SBCS_SBVERSION);
-	if (sbver == 0) {
-		ret = read_memory_bus_v0(target, address, size, count, buffer, increment);
-	} else if (sbver == 1) {
-		ret = read_memory_bus_v1(target, address, size, count, buffer, increment);
-	} else {
+	if (sbver == 0)
+		ret = read_memory_bus_v0(target, args);
+	else if (sbver == 1)
+		ret = read_memory_bus_v1(target, args);
+	else
 		LOG_TARGET_ERROR(target,
 			"Unknown system bus version: %" PRIu64, sbver);
-	}
 
 	return (ret == ERROR_OK) ? MEM_ACCESS_OK : MEM_ACCESS_FAILED;
 }
 
-static int read_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+static int read_memory(struct target *target, const riscv_mem_access_info_t args)
 {
-	if (count == 0)
+	assert(args.is_read && "function should accept only read accesses");
+
+	if (args.count == 0)
 		return ERROR_OK;
 
-	if (!IS_PWR_OF_2(size) || size < 1 || size > 16) {
-		LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory read: %d", size);
+	if (!IS_PWR_OF_2(args.size) || args.size < 1 || args.size > 16) {
+		LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory read: %d", args.size);
 		return ERROR_FAIL;
 	}
 
@@ -4364,19 +4359,13 @@ static int read_memory(struct target *target, target_addr_t address,
 		riscv_mem_access_method_t method = r->mem_access_methods[i];
 		switch (method) {
 			case RISCV_MEM_ACCESS_PROGBUF:
-				skip_reason[method] =
-					read_memory_progbuf(target, address,
-						size, count, buffer, increment);
+				skip_reason[method] = read_memory_progbuf(target, args);
 				break;
 			case RISCV_MEM_ACCESS_SYSBUS:
-				skip_reason[method] =
-					read_memory_sysbus(target, address,
-						size, count, buffer, increment);
+				skip_reason[method] = read_memory_sysbus(target, args);
 				break;
 			case RISCV_MEM_ACCESS_ABSTRACT:
-				skip_reason[method] =
-					read_memory_abstract(target, address,
-						size, count, buffer, increment);
+				skip_reason[method] = read_memory_abstract(target, args);
 				break;
 			default:
 				LOG_TARGET_ERROR(target, "Unknown memory access method: %d", method);
@@ -4395,32 +4384,31 @@ static int read_memory(struct target *target, target_addr_t address,
 
 failure:
 	LOG_TARGET_ERROR(target, "Failed to read memory (addr=0x%" PRIx64 ")\n"
-			"  progbuf=%s, sysbus=%s, abstract=%s", address,
+			"  progbuf=%s, sysbus=%s, abstract=%s", args.address,
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_PROGBUF]),
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_SYSBUS]),
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_ABSTRACT]));
 	return ERROR_FAIL;
 }
 
-static int write_memory_bus_v0(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer)
+static int write_memory_bus_v0(struct target *target, const riscv_mem_access_info_t args)
 {
 	/*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/
 	LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
-			TARGET_PRIxADDR, size, count, address);
-	dm_write(target, DM_SBADDRESS0, address);
+			TARGET_PRIxADDR, args.size, args.count, args.address);
+	dm_write(target, DM_SBADDRESS0, args.address);
 	int64_t value = 0;
 	int64_t access = 0;
 	riscv_addr_t offset = 0;
 	riscv_addr_t t_addr = 0;
-	const uint8_t *t_buffer = buffer + offset;
+	const uint8_t *t_buffer = args.buffer.write + offset;
 
 	/* B.8 Writing Memory, single write check if we write in one go */
-	if (count == 1) { /* count is in bytes here */
-		value = buf_get_u64(t_buffer, 0, 8 * size);
+	if (args.count == 1) { /* count is in bytes here */
+		value = buf_get_u64(t_buffer, 0, 8 * args.size);
 
 		access = 0;
-		access = set_field(access, DM_SBCS_SBACCESS, size/2);
+		access = set_field(access, DM_SBCS_SBACCESS, args.size / 2);
 		dm_write(target, DM_SBCS, access);
 		LOG_TARGET_DEBUG(target, "  access:  0x%08" PRIx64, access);
 		LOG_TARGET_DEBUG(target, "  write_memory:SAB: ONE OFF: value 0x%08" PRIx64, value);
@@ -4431,19 +4419,19 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
 	/*B.8 Writing Memory, using autoincrement*/
 
 	access = 0;
-	access = set_field(access, DM_SBCS_SBACCESS, size/2);
+	access = set_field(access, DM_SBCS_SBACCESS, args.size / 2);
 	access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1);
 	LOG_TARGET_DEBUG(target, "  access:  0x%08" PRIx64, access);
 	dm_write(target, DM_SBCS, access);
 
 	/*2)set the value according to the size required and write*/
-	for (riscv_addr_t i = 0; i < count; ++i) {
-		offset = size*i;
+	for (riscv_addr_t i = 0; i < args.count; ++i) {
+		offset = args.size * i;
 		/* for monitoring only */
-		t_addr = address + offset;
-		t_buffer = buffer + offset;
+		t_addr = args.address + offset;
+		t_buffer = args.buffer.write + offset;
 
-		value = buf_get_u64(t_buffer, 0, 8 * size);
+		value = buf_get_u64(t_buffer, 0, 8 * args.size);
 		LOG_TARGET_DEBUG(target, "SAB:autoincrement: expected address: 0x%08x value: 0x%08x"
 				PRIx64, (uint32_t)t_addr, (uint32_t)value);
 		dm_write(target, DM_SBDATA0, value);
@@ -4455,16 +4443,15 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
 	return ERROR_OK;
 }
 
-static int write_memory_bus_v1(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer)
+static int write_memory_bus_v1(struct target *target, const riscv_mem_access_info_t args)
 {
 	RISCV013_INFO(info);
-	uint32_t sbcs = sb_sbaccess(size);
+	uint32_t sbcs = sb_sbaccess(args.size);
 	sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1);
 	dm_write(target, DM_SBCS, sbcs);
 
-	target_addr_t next_address = address;
-	target_addr_t end_address = address + count * size;
+	target_addr_t next_address = args.address;
+	target_addr_t end_address = args.address + args.count * args.size;
 
 	int result = sb_write_address(target, next_address, RISCV_DELAY_BASE);
 	if (result != ERROR_OK)
@@ -4478,14 +4465,14 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 		if (!batch)
 			return ERROR_FAIL;
 
-		for (uint32_t i = (next_address - address) / size; i < count; i++) {
-			const uint8_t *p = buffer + i * size;
+		for (uint32_t i = (next_address - args.address) / args.size; i < args.count; i++) {
+			const uint8_t *p = args.buffer.write + i * args.size;
 
-			if (riscv_batch_available_scans(batch) < (size + 3) / 4)
+			if (riscv_batch_available_scans(batch) < (args.size + 3) / 4)
 				break;
 
 			uint32_t sbvalue[4] = { 0 };
-			if (size > 12) {
+			if (args.size > 12) {
 				sbvalue[3] = ((uint32_t)p[12]) |
 						(((uint32_t)p[13]) << 8) |
 						(((uint32_t)p[14]) << 16) |
@@ -4494,7 +4481,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 						RISCV_DELAY_BASE);
 			}
 
-			if (size > 8) {
+			if (args.size > 8) {
 				sbvalue[2] = ((uint32_t)p[8]) |
 						(((uint32_t)p[9]) << 8) |
 						(((uint32_t)p[10]) << 16) |
@@ -4502,7 +4489,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 				riscv_batch_add_dm_write(batch, DM_SBDATA2, sbvalue[2], false,
 						RISCV_DELAY_BASE);
 			}
-			if (size > 4) {
+			if (args.size > 4) {
 				sbvalue[1] = ((uint32_t)p[4]) |
 						(((uint32_t)p[5]) << 8) |
 						(((uint32_t)p[6]) << 16) |
@@ -4512,19 +4499,19 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 			}
 
 			sbvalue[0] = p[0];
-			if (size > 2) {
+			if (args.size > 2) {
 				sbvalue[0] |= ((uint32_t)p[2]) << 16;
 				sbvalue[0] |= ((uint32_t)p[3]) << 24;
 			}
-			if (size > 1)
+			if (args.size > 1)
 				sbvalue[0] |= ((uint32_t)p[1]) << 8;
 
 			riscv_batch_add_dm_write(batch, DM_SBDATA0, sbvalue[0], false,
 						RISCV_DELAY_SYSBUS_WRITE);
 
-			log_memory_access(address + i * size, sbvalue, size, false);
+			log_memory_access(args.address + i * args.size, sbvalue, args.size, false);
 
-			next_address += size;
+			next_address += args.size;
 		}
 
 		/* Execute the batch of writes */
@@ -4559,7 +4546,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 			/* Recover from the case when the write commands were issued too fast.
 			 * Determine the address from which to resume writing. */
 			next_address = sb_read_address(target);
-			if (next_address < address) {
+			if (next_address < args.address) {
 				/* This should never happen, probably buggy hardware. */
 				LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR
 						  " - buggy sbautoincrement in hw?", next_address);
@@ -4579,7 +4566,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 			target_addr_t sbaddress = sb_read_address(target);
 			LOG_TARGET_DEBUG(target, "System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")",
 					  sberror, sbaddress);
-			if (sbaddress < address) {
+			if (sbaddress < args.address) {
 				/* This should never happen, probably buggy hardware.
 				 * Make a note to the user not to trust the sbaddress value. */
 				LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR
@@ -4822,24 +4809,23 @@ static int write_memory_progbuf_fill_progbuf(struct target *target,
 	return riscv_program_write(&program);
 }
 
-static int write_memory_progbuf_inner(struct target *target, target_addr_t start_addr,
-		uint32_t size, uint32_t count, const uint8_t *buffer, bool mprven)
+static int write_memory_progbuf_inner(struct target *target, const riscv_mem_access_info_t args, bool mprven)
 {
-	if (write_memory_progbuf_fill_progbuf(target, size,
+	if (write_memory_progbuf_fill_progbuf(target, args.size,
 				mprven) != ERROR_OK)
 		return ERROR_FAIL;
 
-	target_addr_t addr_on_target = start_addr;
-	if (write_memory_progbuf_startup(target, &addr_on_target, buffer, size) != ERROR_OK)
+	target_addr_t addr_on_target = args.address;
+	if (write_memory_progbuf_startup(target, &addr_on_target, args.buffer.write, args.size) != ERROR_OK)
 		return ERROR_FAIL;
 
-	const target_addr_t end_addr = start_addr + (target_addr_t)size * count;
+	const target_addr_t end_addr = args.address + (target_addr_t)args.size * args.count;
 
 	for (target_addr_t next_addr_on_target = addr_on_target; addr_on_target != end_addr;
 			addr_on_target = next_addr_on_target) {
-		const uint8_t * const curr_buff = buffer + (addr_on_target - start_addr);
+		const uint8_t * const curr_buff = args.buffer.write + (addr_on_target - args.address);
 		if (write_memory_progbuf_try_to_write(target, &next_addr_on_target,
-					end_addr, size, curr_buff) != ERROR_OK) {
+					end_addr, args.size, curr_buff) != ERROR_OK) {
 			write_memory_progbuf_teardown(target);
 			return ERROR_FAIL;
 		}
@@ -4847,23 +4833,21 @@ static int write_memory_progbuf_inner(struct target *target, target_addr_t start
 		 * gets successfully written even when busy condition is encountered.
 		 * These assertions shuld hold when next_address_on_target overflows. */
 		assert(next_addr_on_target - addr_on_target > 0);
-		assert(next_addr_on_target - start_addr <= (target_addr_t)size * count);
+		assert(next_addr_on_target - args.address <= (target_addr_t)args.size * args.count);
 	}
 
 	return write_memory_progbuf_teardown(target);
 }
 
 static mem_access_result_t
-write_memory_progbuf(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer)
+write_memory_progbuf(struct target *target, const riscv_mem_access_info_t args)
 {
-	mem_access_result_t skip_reason =
-		mem_should_skip_progbuf(target, address, size, /* is_read = */ false);
+	mem_access_result_t skip_reason = mem_should_skip_progbuf(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
 	LOG_TARGET_DEBUG(target, "writing %" PRIu32 " words of %" PRIu32
-			" bytes to 0x%" TARGET_PRIxADDR, count, size, address);
+			" bytes to 0x%" TARGET_PRIxADDR, args.count, args.size, args.address);
 
 	if (dm013_select_target(target) != ERROR_OK)
 		return MEM_ACCESS_SKIPPED_TARGET_SELECT_FAILED;
@@ -4876,7 +4860,7 @@ write_memory_progbuf(struct target *target, target_addr_t address,
 	const bool mprven = riscv_virt2phys_mode_is_hw(target)
 			&& get_field(mstatus, MSTATUS_MPRV);
 
-	int result = write_memory_progbuf_inner(target, address, size, count, buffer, mprven);
+	int result = write_memory_progbuf_inner(target, args, mprven);
 
 	/* Restore MSTATUS */
 	if (mstatus != mstatus_old)
@@ -4890,26 +4874,23 @@ write_memory_progbuf(struct target *target, target_addr_t address,
 }
 
 static mem_access_result_t
-write_memory_sysbus(struct target *target, target_addr_t address,
-	uint32_t size, uint32_t count, const uint8_t *buffer)
+write_memory_sysbus(struct target *target, const riscv_mem_access_info_t args)
 {
 	riscv013_info_t *info = get_info(target);
-	mem_access_result_t skip_reason =
-		mem_should_skip_sysbus(target, address, size, 0, /* is_read = */ false);
+	mem_access_result_t skip_reason = mem_should_skip_sysbus(target, args);
 	if (skip_reason != MEM_ACCESS_OK)
 		return skip_reason;
 
 	/* TODO: write_memory_bus_* should return mem_access_result_t too*/
 	int ret = ERROR_FAIL;
 	uint64_t sbver = get_field(info->sbcs, DM_SBCS_SBVERSION);
-	if (sbver == 0) {
-		ret = write_memory_bus_v0(target, address, size, count, buffer);
-	} else if (sbver == 1) {
-		ret = write_memory_bus_v1(target, address, size, count, buffer);
-	} else {
+	if (sbver == 0)
+		ret = write_memory_bus_v0(target, args);
+	else if (sbver == 1)
+		ret = write_memory_bus_v1(target, args);
+	else
 		LOG_TARGET_ERROR(target,
 			"Unknown system bus version: %" PRIu64, sbver);
-	}
 
 	if (ret != ERROR_OK)
 		skip_reason = MEM_ACCESS_FAILED;
@@ -4917,11 +4898,17 @@ write_memory_sysbus(struct target *target, target_addr_t address,
 	return skip_reason;
 }
 
-static int write_memory(struct target *target, target_addr_t address,
-		uint32_t size, uint32_t count, const uint8_t *buffer)
+static int write_memory(struct target *target, const riscv_mem_access_info_t args)
 {
-	if (!IS_PWR_OF_2(size) || size < 1 || size > 16) {
-		LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory write: %d", size);
+	assert(!args.is_read && "function should accept only write accesses");
+
+	if (args.increment != args.size) {
+		LOG_TARGET_ERROR(target, "Write increment size has to be equal to element size");
+		return ERROR_NOT_IMPLEMENTED;
+	}
+
+	if (!IS_PWR_OF_2(args.size) || args.size < 1 || args.size > 16) {
+		LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory write: %d", args.size);
 		return ERROR_FAIL;
 	}
 
@@ -4936,19 +4923,13 @@ static int write_memory(struct target *target, target_addr_t address,
 		riscv_mem_access_method_t method = r->mem_access_methods[i];
 		switch (method) {
 			case RISCV_MEM_ACCESS_PROGBUF:
-				skip_reason[method] =
-					write_memory_progbuf(target, address,
-						size, count, buffer);
+				skip_reason[method] = write_memory_progbuf(target, args);
 				break;
 			case RISCV_MEM_ACCESS_SYSBUS:
-				skip_reason[method] =
-					write_memory_sysbus(target, address,
-						size, count, buffer);
+				skip_reason[method] = write_memory_sysbus(target, args);
 				break;
 			case RISCV_MEM_ACCESS_ABSTRACT:
-				skip_reason[method] =
-					write_memory_abstract(target, address,
-						size, count, buffer);
+				skip_reason[method] = write_memory_abstract(target, args);
 				break;
 			default:
 				LOG_TARGET_ERROR(target, "Unknown memory access method: %d", method);
@@ -4967,7 +4948,7 @@ static int write_memory(struct target *target, target_addr_t address,
 
 failure:
 	LOG_TARGET_ERROR(target, "Failed to write memory (addr=0x%" PRIx64 ")\n"
-			"progbuf=%s, sysbus=%s, abstract=%s", address,
+			"progbuf=%s, sysbus=%s, abstract=%s", args.address,
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_PROGBUF]),
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_SYSBUS]),
 			mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_ABSTRACT]));
@@ -5005,8 +4986,6 @@ struct target_type riscv013_target = {
 	.assert_reset = assert_reset,
 	.deassert_reset = deassert_reset,
 
-	.write_memory = write_memory,
-
 	.arch_state = arch_state
 };
 
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 9e8890054..097420787 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "helper/types.h"
 #include <assert.h>
 #include <stdlib.h>
 #include <time.h>
@@ -2867,8 +2868,16 @@ static int riscv_address_translate(struct target *target,
 
 		uint8_t buffer[8];
 		assert(info->pte_shift <= 3);
-		int retval = r->read_memory(target, pte_address,
-				4, (1 << info->pte_shift) / 4, buffer, 4);
+		const riscv_mem_access_info_t args = {
+			.address = pte_address,
+			.buffer.read = buffer,
+			.size = 4,
+			.increment = 4,
+			.count = (1 << info->pte_shift) / 4,
+			.is_virtual = false,
+			.is_read = true,
+		};
+		int retval = r->read_memory(target, args);
 		if (retval != ERROR_OK)
 			return ERROR_FAIL;
 
@@ -3104,29 +3113,42 @@ static int check_virt_memory_access(struct target *target, target_addr_t address
 static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
 			uint32_t size, uint32_t count, uint8_t *buffer)
 {
+	const riscv_mem_access_info_t args = {
+		.address = phys_address,
+		.buffer.read = buffer,
+		.size = size,
+		.increment = size,
+		.count = count,
+		.is_read = true,
+		.is_virtual = false,
+	};
 	RISCV_INFO(r);
-	return r->read_memory(target, phys_address, size, count, buffer, size);
+	return r->read_memory(target, args);
 }
 
 static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
 			uint32_t size, uint32_t count, const uint8_t *buffer)
 {
-	struct target_type *tt = get_target_type(target);
-	if (!tt)
-		return ERROR_FAIL;
-	return tt->write_memory(target, phys_address, size, count, buffer);
+	const riscv_mem_access_info_t args = {
+		.address = phys_address,
+		.buffer.write = buffer,
+		.size = size,
+		.increment = size,
+		.count = count,
+		.is_read = false,
+		.is_virtual = false,
+	};
+
+	RISCV_INFO(r);
+	return r->write_memory(target, args);
 }
 
-static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_t size,
-		uint32_t count, uint8_t *read_buffer, const uint8_t *write_buffer)
+static int riscv_rw_memory(struct target *target, const riscv_mem_access_info_t args)
 {
-	/* Exactly one of the buffers must be set, the other must be NULL */
-	assert(!!read_buffer != !!write_buffer);
-
-	const bool is_write = write_buffer ? true : false;
-	if (count == 0) {
+	const bool is_write = !args.is_read;
+	if (args.count == 0) {
 		LOG_TARGET_WARNING(target, "0-length %s 0x%" TARGET_PRIxADDR,
-				is_write ? "write to" : "read from", address);
+				is_write ? "write to" : "read from", args.address);
 		return ERROR_OK;
 	}
 
@@ -3136,25 +3158,23 @@ static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_
 		return result;
 
 	RISCV_INFO(r);
-	struct target_type *tt = get_target_type(target);
-	if (!tt)
-		return ERROR_FAIL;
-
 	if (!mmu_enabled) {
 		if (is_write)
-			return tt->write_memory(target, address, size, count, write_buffer);
+			return r->write_memory(target, args);
 		else
-			return r->read_memory(target, address, size, count, read_buffer, size);
+			return r->read_memory(target, args);
 	}
 
-	result = check_virt_memory_access(target, address, size, count, is_write);
+	result = check_virt_memory_access(target, args.address,
+			args.size, args.count, is_write);
 	if (result != ERROR_OK)
 		return result;
 
 	uint32_t current_count = 0;
-	while (current_count < count) {
+	target_addr_t current_address = args.address;
+	while (current_count < args.count) {
 		target_addr_t physical_addr;
-		result = target->type->virt2phys(target, address, &physical_addr);
+		result = target->type->virt2phys(target, current_address, &physical_addr);
 		if (result != ERROR_OK) {
 			LOG_TARGET_ERROR(target, "Address translation failed.");
 			return result;
@@ -3163,16 +3183,24 @@ static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_
 		/* TODO: For simplicity, this algorithm assumes the worst case - the smallest possible page size,
 		 * which is  4 KiB. The algorithm can be improved to detect the real page size, and allow to use larger
 		 * memory transfers and avoid extra unnecessary virt2phys address translations. */
-		uint32_t chunk_count = MIN(count - current_count, (RISCV_PGSIZE - RISCV_PGOFFSET(address)) / size);
+		uint32_t chunk_count = MIN(args.count - current_count,
+				(RISCV_PGSIZE - RISCV_PGOFFSET(current_address))
+				/ args.size);
+
+		riscv_mem_access_info_t current_access = args;
+		current_access.address = physical_addr;
+		current_access.buffer.write += current_count * args.size;
+		current_access.count = chunk_count;
+		current_access.is_virtual = false;
 		if (is_write)
-			result = tt->write_memory(target, physical_addr, size, chunk_count, write_buffer + current_count * size);
+			result = r->write_memory(target, current_access);
 		else
-			result = r->read_memory(target, physical_addr, size, chunk_count, read_buffer + current_count * size, size);
+			result = r->read_memory(target, current_access);
 		if (result != ERROR_OK)
 			return result;
 
 		current_count += chunk_count;
-		address += chunk_count * size;
+		current_address += chunk_count * args.size;
 	}
 	return ERROR_OK;
 }
@@ -3180,13 +3208,33 @@ static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_
 static int riscv_read_memory(struct target *target, target_addr_t address,
 		uint32_t size, uint32_t count, uint8_t *buffer)
 {
-	return riscv_rw_memory(target, address, size, count, buffer, NULL);
+	const riscv_mem_access_info_t args = {
+		.address = address,
+		.buffer.write = buffer,
+		.size = size,
+		.increment = size,
+		.count = count,
+		.is_read = true,
+		.is_virtual = riscv_virt2phys_mode_is_hw(target),
+	};
+
+	return riscv_rw_memory(target, args);
 }
 
 static int riscv_write_memory(struct target *target, target_addr_t address,
 		uint32_t size, uint32_t count, const uint8_t *buffer)
 {
-	return riscv_rw_memory(target, address, size, count, NULL, buffer);
+	const riscv_mem_access_info_t args = {
+		.address = address,
+		.buffer.write = buffer,
+		.size = size,
+		.increment = size,
+		.count = count,
+		.is_read = false,
+		.is_virtual = riscv_virt2phys_mode_is_hw(target),
+	};
+
+	return riscv_rw_memory(target, args);
 }
 
 static const char *riscv_get_gdb_arch(const struct target *target)
@@ -4797,7 +4845,16 @@ COMMAND_HANDLER(handle_repeat_read)
 		LOG_ERROR("malloc failed");
 		return ERROR_FAIL;
 	}
-	int result = r->read_memory(target, address, size, count, buffer, 0);
+	const riscv_mem_access_info_t args = {
+		.address = address,
+		.buffer.read = buffer,
+		.size = size,
+		.count = count,
+		.increment = 0,
+		.is_read = true,
+		.is_virtual = false,
+	};
+	int result = r->read_memory(target, args);
 	if (result == ERROR_OK) {
 		target_handle_md_output(cmd, target, address, size, count, buffer,
 			false);
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 79d6ba847..04fc3e13b 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -129,6 +129,22 @@ struct reg_name_table {
 	char **reg_names;
 };
 
+typedef struct riscv_mem_access_info {
+	target_addr_t address;
+
+	union {
+		uint8_t *read;
+		const uint8_t *write;
+	} buffer;
+
+	uint32_t size;
+	uint32_t increment;
+	uint32_t count;
+
+	bool is_virtual;
+	bool is_read;
+} riscv_mem_access_info_t;
+
 struct riscv_info {
 	unsigned int common_magic;
 
@@ -256,8 +272,8 @@ struct riscv_info {
 						 riscv_sample_config_t *config,
 						 int64_t until_ms);
 
-	int (*read_memory)(struct target *target, target_addr_t address,
-			uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
+	int (*read_memory)(struct target *target, const riscv_mem_access_info_t args);
+	int (*write_memory)(struct target *target, const riscv_mem_access_info_t args);
 
 	unsigned int (*data_bits)(struct target *target);