Message ID | 20210305135451.15427-4-alex.bennee@linaro.org |
---|---|
State | New |
Headers | show |
Series | semihosting/next (move from hw, heapinfo) | expand |
On Fri, 5 Mar 2021 at 13:54, Alex Bennée <alex.bennee@linaro.org> wrote: > > I'm not sure this every worked properly and it's certainly not > exercised by check-tcg or Peter's semihosting tests. Hoist it into > it's own helper function and attempt to validate the results in the > linux-user semihosting test at the least. > > Bug: https://bugs.launchpad.net/bugs/1915925 > Cc: Bug 1915925 <1915925@bugs.launchpad.net> > Cc: Keith Packard <keithp@keithp.com> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org> > --- > tests/tcg/arm/semicall.h | 1 + > semihosting/arm-compat-semi.c | 129 +++++++++++++++++++--------------- > tests/tcg/arm/semihosting.c | 34 ++++++++- > 3 files changed, 107 insertions(+), 57 deletions(-) > +#else > + limit = current_machine->ram_size; > + /* TODO: Make this use the limit of the loaded application. */ > + info.heap_base = rambase + limit / 2; > + info.heap_limit = rambase + limit; > + info.stack_base = rambase + limit; /* Stack base */ > + info.stack_limit = rambase; /* Stack limit. */ > + > + if (cpu_memory_rw_debug(cs, arg0, &info, sizeof(info), true)) { Blatting a C struct into guest memory has endianness and padding problems. Why not just do things the way the old Arm code did it ? Also, you don't seem to have the correct "is the CPU in 32-bit or 64-bit mode" test here: you cannot rely on target_ulong being the right size, you must make a runtime check. I suggested in the other email the way I think we should fix this. thanks -- PMM
Alex Bennée <alex.bennee@linaro.org> writes: > I'm not sure this every worked properly and it's certainly not > exercised by check-tcg or Peter's semihosting tests. Hoist it into > it's own helper function and attempt to validate the results in the > linux-user semihosting test at the least. The patch is mostly code motion, moving the existing heapinfo stuff into a separate function. That makes it really hard to see how you've changed the values being returned. I'd love to see a two patch series, one of which moves the code as-is and a second patch which fixes whatever bugs you've found. -- -keith
Peter Maydell <peter.maydell@linaro.org> writes: > Also, you don't seem to have the correct "is the CPU in > 32-bit or 64-bit mode" test here: you cannot rely on target_ulong > being the right size, you must make a runtime check. Do you mean whether a dual aarch64/arm core is in arm or aarch64 mode, or whether an aarch64 is running a 32-bit ABI? -- -keith
On Fri, 5 Mar 2021 at 20:22, Keith Packard <keithp@keithp.com> wrote: > > Peter Maydell <peter.maydell@linaro.org> writes: > > > Also, you don't seem to have the correct "is the CPU in > > 32-bit or 64-bit mode" test here: you cannot rely on target_ulong > > being the right size, you must make a runtime check. > > Do you mean whether a dual aarch64/arm core is in arm or aarch64 mode, > or whether an aarch64 is running a 32-bit ABI? For semihosting for Arm what matters is "what state is the core in at the point where it makes the semihosting SVC/HLT/etc insn?". How does RISCV specify it? thanks -- PMM
Peter Maydell <peter.maydell@linaro.org> writes: > For semihosting for Arm what matters is "what state is the core > in at the point where it makes the semihosting SVC/HLT/etc insn?". Ok, that means we *aren't* talking about -mabi=ilp32, which is good -- in my current picolibc implementation, the semihosting code uses a pure 64-bit interface for aarch64 targets, even when using ilp32 ABI. > How does RISCV specify it? Because the ISA is identical between 64- and 32- bit (and 128-bit) execution modes, the only difference between the two is the Machine XLEN value which encodes the native base integer ISA width. You switch modes by modifying this value. I don't know of any implementation in hardware or software that supports modifying this value. I'm not sure we need to support this in the semihosting code for qemu as I'm pretty sure getting qemu to support dynamic XLEN values would be a large project (a project which I don't personally feel would offer much value). -- -keith
On 3/5/21 3:54 PM, Keith Packard via wrote: > I don't know of any implementation in hardware or software that supports > modifying this value. I'm not sure we need to support this in the > semihosting code for qemu as I'm pretty sure getting qemu to support > dynamic XLEN values would be a large project I think it would be fairly trivial, now. We have riscv_cpu_is_32bit (which checks misa, not xlen, fwiw). It's not examined in cpu_get_tb_cpu_state(), but it is many other places. > (a project which I don't > personally feel would offer much value). Now that's a different story. I'll take no position there. However! We do have a function, that can be made into a TCG hook, which could then be queried by semihosting/. r~
On Fri, 5 Mar 2021 at 23:54, Keith Packard <keithp@keithp.com> wrote: > > Peter Maydell <peter.maydell@linaro.org> writes: > > > For semihosting for Arm what matters is "what state is the core > > in at the point where it makes the semihosting SVC/HLT/etc insn?". > > Ok, that means we *aren't* talking about -mabi=ilp32, which is good -- > in my current picolibc implementation, the semihosting code uses a pure > 64-bit interface for aarch64 targets, even when using ilp32 ABI. ILP32 for AArch64 is a zombie target -- it is kinda-sorta supported in some toolchains but has no support in eg the Linux syscall ABI. The semihosting ABI does not implement any kind of ILP32 variant -- you can have A32/T32 (AArch32) semihosting, where register and field sizes are 32 bit, or you can have A64 (AArch64) semihosting, where register and field sizes are 64 bit. > > How does RISCV specify it? > > Because the ISA is identical between 64- and 32- bit (and 128-bit) > execution modes, the only difference between the two is the Machine XLEN > value which encodes the native base integer ISA width. You switch modes > by modifying this value. I meant, how does the RISCV semihosting ABI specify what the field size is? To answer my own question, I just looked at the spec doc and it says "depends on whether the caller is 32-bit or 64-bit", which implies that we need to look at the current state of the CPU in some way. > I don't know of any implementation in hardware or software that supports > modifying this value. I'm not sure we need to support this in the > semihosting code for qemu as I'm pretty sure getting qemu to support > dynamic XLEN values would be a large project (a project which I don't > personally feel would offer much value). Part of why I asked is that the current RISCV implementation is just looking at sizeof(target_ulong); but the qemu-system-riscv64 executable AIUI now supports emulating both "this is a 64 bit guest CPU" and "this is a 32 bit host CPU", and so looking at a QEMU-compile-time value like "sizeof(target_ulong)" will produce the wrong answer for 32-bit CPUs emulated in the qemu-system-riscv64 binary. My guess is maybe it should be looking at the result of riscv_cpu_is_32bit() instead. thanks -- PMM
Peter Maydell <peter.maydell@linaro.org> writes: > ILP32 for AArch64 is a zombie target -- it is kinda-sorta > supported in some toolchains but has no support in eg > the Linux syscall ABI. The semihosting ABI does not implement > any kind of ILP32 variant -- you can have A32/T32 (AArch32) > semihosting, where register and field sizes are 32 bit, or > you can have A64 (AArch64) semihosting, where register and > field sizes are 64 bit. Yeah, I did ILP32 support for Picolibc; all of the aarch64 asm support needed fixing as ilp32 doesn't specify that register arguments clear the top 32 bits. Seems pretty obvious that it's little used. For semihosting, as the ABI isn't visible to the hardware/emulator, the only reasonable answer that I could come up with was to treat ILP32 the same as the LP64 and pass 64 bit parameters. As picolibc is designed for bare-metal environments, it's pretty easy to support ilp32 otherwise. > I meant, how does the RISCV semihosting ABI specify what > the field size is? To answer my own question, I just looked at > the spec doc and it says "depends on whether the caller is > 32-bit or 64-bit", which implies that we need to look at the > current state of the CPU in some way. Yes. As qemu currently fixes that value based on compilation parameters, we can use the relevant native types directly and ignore the CPU state. Adding dynamic XLEN support to qemu would involve a bunch of work as the same code can be run in both 64- and 32- bit modes, so you'd have to translate it twice and select which to execute based on the CPU state. > Part of why I asked is that the current RISCV implementation > is just looking at sizeof(target_ulong); but the qemu-system-riscv64 > executable AIUI now supports emulating both "this is a 64 bit > guest CPU" and "this is a 32 bit host CPU", and so looking at > a QEMU-compile-time value like "sizeof(target_ulong)" will > produce the wrong answer for 32-bit CPUs emulated in > the qemu-system-riscv64 binary. My guess is maybe > it should be looking at the result of riscv_cpu_is_32bit() instead. Wow. I read through the code and couldn't find anything that looked like it supported that, sounds like I must have missed something? -- -keith
On Sat, 6 Mar 2021 at 16:54, Keith Packard <keithp@keithp.com> wrote: > > Peter Maydell <peter.maydell@linaro.org> writes: > > Part of why I asked is that the current RISCV implementation > > is just looking at sizeof(target_ulong); but the qemu-system-riscv64 > > executable AIUI now supports emulating both "this is a 64 bit > > guest CPU" and "this is a 32 bit host CPU", and so looking at > > a QEMU-compile-time value like "sizeof(target_ulong)" will > > produce the wrong answer for 32-bit CPUs emulated in > > the qemu-system-riscv64 binary. My guess is maybe > > it should be looking at the result of riscv_cpu_is_32bit() instead. > > Wow. I read through the code and couldn't find anything that looked like > it supported that, sounds like I must have missed something? I thought Alistair had done that work (which brings riscv into line with the other 32/64 bit QEMU targets, which all support the 32-bit CPU types in the 64-bit-capable executable). But maybe it hasn't landed in master yet? thanks -- PMM
On Mon, Mar 8, 2021 at 5:10 AM Peter Maydell <peter.maydell@linaro.org> wrote: > > On Sat, 6 Mar 2021 at 16:54, Keith Packard <keithp@keithp.com> wrote: > > > > Peter Maydell <peter.maydell@linaro.org> writes: > > > Part of why I asked is that the current RISCV implementation > > > is just looking at sizeof(target_ulong); but the qemu-system-riscv64 > > > executable AIUI now supports emulating both "this is a 64 bit > > > guest CPU" and "this is a 32 bit host CPU", and so looking at > > > a QEMU-compile-time value like "sizeof(target_ulong)" will > > > produce the wrong answer for 32-bit CPUs emulated in > > > the qemu-system-riscv64 binary. My guess is maybe > > > it should be looking at the result of riscv_cpu_is_32bit() instead. > > > > Wow. I read through the code and couldn't find anything that looked like > > it supported that, sounds like I must have missed something? riscv_cpu_is_32bit() is somewhat new, so it might not have been there when you wrote the patches. > > I thought Alistair had done that work (which brings riscv into > line with the other 32/64 bit QEMU targets, which all support > the 32-bit CPU types in the 64-bit-capable executable). But maybe > it hasn't landed in master yet? I have started on the effort, but I have not finished yet. Adding riscv_cpu_is_32bit() was the first step there and I have some more patches locally but I don't have anything working yet. Alistair > > thanks > -- PMM
Alistair Francis <alistair23@gmail.com> writes: > I have started on the effort, but I have not finished yet. Adding > riscv_cpu_is_32bit() was the first step there and I have some more > patches locally but I don't have anything working yet. That's awesome. I think waiting until we see what APIs you're developing for detecting and operating in 32-bit mode on a 64-bit capable processor seems like a good idea for now. -- -keith
diff --git a/tests/tcg/arm/semicall.h b/tests/tcg/arm/semicall.h index d4f6818192..676a542be5 100644 --- a/tests/tcg/arm/semicall.h +++ b/tests/tcg/arm/semicall.h @@ -9,6 +9,7 @@ #define SYS_WRITE0 0x04 #define SYS_READC 0x07 +#define SYS_HEAPINFO 0x16 #define SYS_REPORTEXC 0x18 uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 94950b6c56..a8fdbceb5f 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -822,6 +822,78 @@ static const GuestFDFunctions guestfd_fns[] = { put_user_utl(val, args + (n) * sizeof(target_ulong)) #endif +/* + * SYS_HEAPINFO is a little weird: "On entry, the PARAMETER REGISTER + * contains the address of a pointer to a four-field data block" which + * we then fill in. The PARAMETER REGISTER is unchanged. + */ + +struct HeapInfo { + target_ulong heap_base; + target_ulong heap_limit; + target_ulong stack_base; + target_ulong stack_limit; +}; + +static bool do_heapinfo(CPUState *cs, target_long arg0) +{ + target_ulong limit; + struct HeapInfo info = {}; +#ifdef CONFIG_USER_ONLY + TaskState *ts = cs->opaque; +#else + target_ulong rambase = common_semi_rambase(cs); +#endif + +#ifdef CONFIG_USER_ONLY + /* + * Some C libraries assume the heap immediately follows .bss, so + * allocate it using sbrk. + */ + if (!ts->heap_limit) { + abi_ulong ret; + + ts->heap_base = do_brk(0); + limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; + /* Try a big heap, and reduce the size if that fails. */ + for (;;) { + ret = do_brk(limit); + if (ret >= limit) { + break; + } + limit = (ts->heap_base >> 1) + (limit >> 1); + } + ts->heap_limit = limit; + } + + info.heap_base = ts->heap_base; + info.heap_limit = ts->heap_limit; + info.stack_base = ts->stack_base; + info.stack_limit = 0; /* Stack limit. */ + + if (copy_to_user(arg0, &info, sizeof(info))) { + errno = EFAULT; + return set_swi_errno(cs, -1); + } +#else + limit = current_machine->ram_size; + /* TODO: Make this use the limit of the loaded application. */ + info.heap_base = rambase + limit / 2; + info.heap_limit = rambase + limit; + info.stack_base = rambase + limit; /* Stack base */ + info.stack_limit = rambase; /* Stack limit. */ + + if (cpu_memory_rw_debug(cs, arg0, &info, sizeof(info), true)) { + errno = EFAULT; + return set_swi_errno(cs, -1); + } + +#endif + + return 0; +} + + /* * Do a semihosting call. * @@ -1184,63 +1256,8 @@ target_ulong do_common_semihosting(CPUState *cs) } case TARGET_SYS_HEAPINFO: { - target_ulong retvals[4]; - target_ulong limit; - int i; -#ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; -#else - target_ulong rambase = common_semi_rambase(cs); -#endif - GET_ARG(0); - -#ifdef CONFIG_USER_ONLY - /* - * Some C libraries assume the heap immediately follows .bss, so - * allocate it using sbrk. - */ - if (!ts->heap_limit) { - abi_ulong ret; - - ts->heap_base = do_brk(0); - limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; - /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(limit); - if (ret >= limit) { - break; - } - limit = (ts->heap_base >> 1) + (limit >> 1); - } - ts->heap_limit = limit; - } - - retvals[0] = ts->heap_base; - retvals[1] = ts->heap_limit; - retvals[2] = ts->stack_base; - retvals[3] = 0; /* Stack limit. */ -#else - limit = current_machine->ram_size; - /* TODO: Make this use the limit of the loaded application. */ - retvals[0] = rambase + limit / 2; - retvals[1] = rambase + limit; - retvals[2] = rambase + limit; /* Stack base */ - retvals[3] = rambase; /* Stack limit. */ -#endif - - for (i = 0; i < ARRAY_SIZE(retvals); i++) { - bool fail; - - fail = SET_ARG(i, retvals[i]); - - if (fail) { - /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); - } - } - return 0; + return do_heapinfo(cs, arg0); } case TARGET_SYS_EXIT: case TARGET_SYS_EXIT_EXTENDED: diff --git a/tests/tcg/arm/semihosting.c b/tests/tcg/arm/semihosting.c index 33faac9916..fd5780ec3c 100644 --- a/tests/tcg/arm/semihosting.c +++ b/tests/tcg/arm/semihosting.c @@ -7,7 +7,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#define _GNU_SOURCE /* asprintf is a GNU extension */ + #include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> #include "semicall.h" int main(int argc, char *argv[argc]) @@ -18,8 +24,34 @@ int main(int argc, char *argv[argc]) uintptr_t exit_block[2] = {0x20026, 0}; uintptr_t exit_code = (uintptr_t) &exit_block; #endif + struct { + void *heap_base; + void *heap_limit; + void *stack_base; + void *stack_limit; + } info; + void *ptr_to_info = (void *) &info; + char *heap_info, *stack_info; + void *brk = sbrk(0); + + __semi_call(SYS_WRITE0, (uintptr_t) "Hello World\n"); + + memset(&info, 0, sizeof(info)); + __semi_call(SYS_HEAPINFO, (uintptr_t) &ptr_to_info); + + asprintf(&heap_info, "heap: %p -> %p\n", info.heap_base, info.heap_limit); + __semi_call(SYS_WRITE0, (uintptr_t) heap_info); + if (info.heap_base != brk) { + sprintf(heap_info, "heap mismatch: %p\n", brk); + __semi_call(SYS_WRITE0, (uintptr_t) heap_info); + return -1; + } + + asprintf(&stack_info, "stack: %p -> %p\n", info.stack_base, info.stack_limit); + __semi_call(SYS_WRITE0, (uintptr_t) stack_info); + free(heap_info); + free(stack_info); - __semi_call(SYS_WRITE0, (uintptr_t) "Hello World"); __semi_call(SYS_REPORTEXC, exit_code); /* if we get here we failed */ return -1;
I'm not sure this every worked properly and it's certainly not exercised by check-tcg or Peter's semihosting tests. Hoist it into it's own helper function and attempt to validate the results in the linux-user semihosting test at the least. Bug: https://bugs.launchpad.net/bugs/1915925 Cc: Bug 1915925 <1915925@bugs.launchpad.net> Cc: Keith Packard <keithp@keithp.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> --- tests/tcg/arm/semicall.h | 1 + semihosting/arm-compat-semi.c | 129 +++++++++++++++++++--------------- tests/tcg/arm/semihosting.c | 34 ++++++++- 3 files changed, 107 insertions(+), 57 deletions(-) -- 2.20.1