diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2017-05-12 12:05:23 -0700 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2017-06-06 02:34:04 -0700 |
commit | 347ec03093f9668a379ef6b7fa1feb332fff039c (patch) | |
tree | f337050aab9791c9d5f5bdf32eb8d86bea367b36 /target/xtensa | |
parent | 30c2afd151cbc38c012f7b441088980807183da6 (diff) |
target/xtensa: fix return value of read/write simcalls
Return value of read/write simcalls is not calculated correctly in case
of operations crossing page boundary and in case of short reads/writes.
Read and write simcalls should return the size of data actually
read/written or -1 in case of error.
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'target/xtensa')
-rw-r--r-- | target/xtensa/xtensa-semi.c | 25 |
1 files changed, 20 insertions, 5 deletions
diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 98ae28ce71..ffcaf8d0ac 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -166,6 +166,7 @@ void HELPER(simcall)(CPUXtensaState *env) uint32_t fd = regs[3]; uint32_t vaddr = regs[4]; uint32_t len = regs[5]; + uint32_t len_done = 0; while (len > 0) { hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr); @@ -174,24 +175,38 @@ void HELPER(simcall)(CPUXtensaState *env) uint32_t io_sz = page_left < len ? page_left : len; hwaddr sz = io_sz; void *buf = cpu_physical_memory_map(paddr, &sz, !is_write); + uint32_t io_done; + bool error = false; if (buf) { vaddr += io_sz; len -= io_sz; - regs[2] = is_write ? + io_done = is_write ? write(fd, buf, io_sz) : read(fd, buf, io_sz); regs[3] = errno_h2g(errno); - cpu_physical_memory_unmap(buf, sz, !is_write, sz); - if (regs[2] == -1) { - break; + if (io_done == -1) { + error = true; + io_done = 0; } + cpu_physical_memory_unmap(buf, sz, !is_write, io_done); } else { - regs[2] = -1; + error = true; regs[3] = TARGET_EINVAL; break; } + if (error) { + if (!len_done) { + len_done = -1; + } + break; + } + len_done += io_done; + if (io_done < io_sz) { + break; + } } + regs[2] = len_done; } break; |