From: Stacey Son <s...@freebsd.org> oidfmt uses undocumented system call to get the type of the sysctl. sysctl_oldcvt does the byte swapping in the data to return it to the target.
Co-Authored-by: Sean Bruno <sbr...@freebsd.org> Signed-off-by: Sean Bruno <sbr...@freebsd.org> Co-Authored-by: Juergen Lock <n...@jelal.kn-bremen.de> Signed-off-by: Juergen Lock <n...@jelal.kn-bremen.de> Co-Authored-by: Raphael Kubo da Costa <rak...@freebsd.org> Signed-off-by: Raphael Kubo da Costa <rak...@freebsd.org> Signed-off-by: Stacey Son <s...@freebsd.org> Signed-off-by: Warner Losh <i...@bsdimp.com> --- bsd-user/freebsd/os-sys.c | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/bsd-user/freebsd/os-sys.c b/bsd-user/freebsd/os-sys.c index 1676ec10f83..e3b9f168a2b 100644 --- a/bsd-user/freebsd/os-sys.c +++ b/bsd-user/freebsd/os-sys.c @@ -21,6 +21,100 @@ #include "qemu.h" #include "target_arch_sysarch.h" +#include <sys/sysctl.h> + +/* + * This uses the undocumented oidfmt interface to find the kind of a requested + * sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() (compare to + * src/sbin/sysctl/sysctl.c) + */ +static int oidfmt(int *oid, int len, char *fmt, uint32_t *kind) +{ + int qoid[CTL_MAXNAME + 2]; + uint8_t buf[BUFSIZ]; + int i; + size_t j; + + qoid[0] = 0; + qoid[1] = 4; + memcpy(qoid + 2, oid, len * sizeof(int)); + + j = sizeof(buf); + i = sysctl(qoid, len + 2, buf, &j, 0, 0); + if (i) { + return i; + } + + if (kind) { + *kind = *(uint32_t *)buf; + } + + if (fmt) { + strcpy(fmt, (char *)(buf + sizeof(uint32_t))); + } + return 0; +} + +/* + * try and convert sysctl return data for the target. + * Note: doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. + */ +static int sysctl_oldcvt(void *holdp, size_t *holdlen, uint32_t kind) +{ + switch (kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); + break; + +#ifdef TARGET_ABI32 + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + /* + * If the sysctl has a type of long/ulong but seems to be bigger than + * these data types, its probably an array. Double check that its + * evenly divisible by the size of long and convert holdp to a series of + * 32bit elements instead, adjusting holdlen to the new size. + */ + if ((*holdlen > sizeof(abi_ulong)) && + ((*holdlen % sizeof(abi_ulong)) == 0)) { + int array_size = *holdlen / sizeof(long); + int i; + if (holdp) { + for (i = 0; i < array_size; i++) { + ((uint32_t *)holdp)[i] = tswap32(((long *)holdp)[i]); + } + *holdlen = array_size * sizeof(abi_ulong); + } else { + *holdlen = sizeof(abi_ulong); + } + } else { + *(uint32_t *)holdp = tswap32(*(long *)holdp); + *holdlen = sizeof(uint32_t); + } + break; +#else + case CTLTYPE_LONG: + *(uint64_t *)holdp = tswap64(*(long *)holdp); + break; + case CTLTYPE_ULONG: + *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); + break; +#endif + case CTLTYPE_U64: + case CTLTYPE_S64: + *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); + break; + + case CTLTYPE_STRING: + break; + + default: + return -1; + } + return 0; +} + /* sysarch() is architecture dependent. */ abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) { -- 2.39.1