#include "qemu/memfd.h"
#include "qemu/queue.h"
#include "qemu/plugin.h"
+#include "tcg/startup.h"
#include "target_mman.h"
#include <elf.h>
#include <endian.h>
#include "special-errno.h"
#include "qapi/error.h"
#include "fd-trans.h"
-#include "tcg/tcg.h"
#include "cpu_loop-common.h"
#ifndef CLONE_IO
#endif
#if defined(TARGET_NR_getdents) && defined(EMULATE_GETDENTS_WITH_GETDENTS)
-_syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count);
+_syscall3(int, sys_getdents, unsigned int, fd, struct linux_dirent *, dirp, unsigned int, count);
#endif
#if (defined(TARGET_NR_getdents) && \
!defined(EMULATE_GETDENTS_WITH_GETDENTS)) || \
(defined(TARGET_NR_getdents64) && defined(__NR_getdents64))
-_syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count);
+_syscall3(int, sys_getdents64, unsigned int, fd, struct linux_dirent64 *, dirp, unsigned int, count);
#endif
#if defined(TARGET_NR__llseek) && defined(__NR_llseek)
-_syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
- loff_t *, res, uint, wh);
+_syscall5(int, _llseek, unsigned int, fd, unsigned long, hi, unsigned long, lo,
+ loff_t *, res, unsigned int, wh);
#endif
_syscall3(int, sys_rt_sigqueueinfo, pid_t, pid, int, sig, siginfo_t *, uinfo)
_syscall4(int, sys_rt_tgsigqueueinfo, pid_t, pid, pid_t, tid, int, sig,
#if TARGET_O_LARGEFILE != 0 || O_LARGEFILE != 0
{ TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, },
#endif
- { 0, 0, 0, 0 }
};
_syscall2(int, sys_getcwd1, char *, buf, size_t, size)
#endif
safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \
int, options, struct rusage *, rusage)
+safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp)
safe_syscall5(int, execveat, int, dirfd, const char *, filename,
char **, argv, char **, envp, int, flags)
#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \
return target_type;
}
-static abi_ulong target_brk;
-static abi_ulong brk_page;
+static abi_ulong target_brk, initial_target_brk;
void target_set_brk(abi_ulong new_brk)
{
- target_brk = new_brk;
- brk_page = HOST_PAGE_ALIGN(target_brk);
+ target_brk = TARGET_PAGE_ALIGN(new_brk);
+ initial_target_brk = target_brk;
}
/* do_brk() must return target values and target errnos. */
abi_long do_brk(abi_ulong brk_val)
{
abi_long mapped_addr;
- abi_ulong new_alloc_size;
- abi_ulong new_brk, new_host_brk_page;
+ abi_ulong new_brk;
+ abi_ulong old_brk;
/* brk pointers are always untagged */
- /* return old brk value if brk_val unchanged or zero */
- if (!brk_val || brk_val == target_brk) {
+ /* do not allow to shrink below initial brk value */
+ if (brk_val < initial_target_brk) {
return target_brk;
}
new_brk = TARGET_PAGE_ALIGN(brk_val);
- new_host_brk_page = HOST_PAGE_ALIGN(brk_val);
+ old_brk = TARGET_PAGE_ALIGN(target_brk);
- /* brk_val and old target_brk might be on the same page */
- if (new_brk == TARGET_PAGE_ALIGN(target_brk)) {
- if (brk_val > target_brk) {
- /* empty remaining bytes in (possibly larger) host page */
- memset(g2h_untagged(target_brk), 0, new_host_brk_page - target_brk);
- }
+ /* new and old target_brk might be on the same page */
+ if (new_brk == old_brk) {
target_brk = brk_val;
return target_brk;
}
- /* Release heap if necesary */
- if (new_brk < target_brk) {
- /* empty remaining bytes in (possibly larger) host page */
- memset(g2h_untagged(brk_val), 0, new_host_brk_page - brk_val);
-
- /* free unused host pages and set new brk_page */
- target_munmap(new_host_brk_page, brk_page - new_host_brk_page);
- brk_page = new_host_brk_page;
+ /* Release heap if necessary */
+ if (new_brk < old_brk) {
+ target_munmap(new_brk, old_brk - new_brk);
target_brk = brk_val;
return target_brk;
}
- /* We need to allocate more memory after the brk... Note that
- * we don't use MAP_FIXED because that will map over the top of
- * any existing mapping (like the one with the host libc or qemu
- * itself); instead we treat "mapped but at wrong address" as
- * a failure and unmap again.
- */
- new_alloc_size = new_host_brk_page - brk_page;
- if (new_alloc_size) {
- mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
- PROT_READ|PROT_WRITE,
- MAP_ANON|MAP_PRIVATE, 0, 0));
- } else {
- mapped_addr = brk_page;
- }
-
- if (mapped_addr == brk_page) {
- /* Heap contents are initialized to zero, as for anonymous
- * mapped pages. Technically the new pages are already
- * initialized to zero since they *are* anonymous mapped
- * pages, however we have to take care with the contents that
- * come from the remaining part of the previous page: it may
- * contains garbage data due to a previous heap usage (grown
- * then shrunken). */
- memset(g2h_untagged(target_brk), 0, brk_page - target_brk);
+ mapped_addr = target_mmap(old_brk, new_brk - old_brk,
+ PROT_READ | PROT_WRITE,
+ MAP_FIXED_NOREPLACE | MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if (mapped_addr == old_brk) {
target_brk = brk_val;
- brk_page = new_host_brk_page;
return target_brk;
- } else if (mapped_addr != -1) {
- /* Mapped but at wrong address, meaning there wasn't actually
- * enough space for this brk.
- */
- target_munmap(mapped_addr, new_alloc_size);
- mapped_addr = -1;
}
#if defined(TARGET_ALPHA)
uint32_t *dst = (uint32_t *)data;
memcpy(dst, target_data, len);
- /* fix endianess of first 32-bit word */
+ /* fix endianness of first 32-bit word */
if (len >= sizeof(uint32_t)) {
*dst = tswap32(*dst);
}
unlock_user(results, optval_addr, 0);
return ret;
}
- /* swap host endianess to target endianess. */
+ /* swap host endianness to target endianness. */
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
results[i] = tswap32(results[i]);
}
abi_long ret;
int host_flags;
- host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl);
+ if (flags & ~(TARGET_SOCK_CLOEXEC | TARGET_SOCK_NONBLOCK)) {
+ return -TARGET_EINVAL;
+ }
+
+ host_flags = 0;
+ if (flags & TARGET_SOCK_NONBLOCK) {
+ host_flags |= SOCK_NONBLOCK;
+ }
+ if (flags & TARGET_SOCK_CLOEXEC) {
+ host_flags |= SOCK_CLOEXEC;
+ }
if (target_addr == 0) {
return get_errno(safe_accept4(fd, NULL, NULL, host_flags));
}
#endif
-#define N_SHM_REGIONS 32
-
-static struct shm_region {
- abi_ulong start;
- abi_ulong size;
- bool in_use;
-} shm_regions[N_SHM_REGIONS];
-
#ifndef TARGET_SEMID64_DS
/* asm-generic version of this struct */
struct target_semid64_ds
return ret;
}
-#ifndef TARGET_FORCE_SHMLBA
-/* For most architectures, SHMLBA is the same as the page size;
- * some architectures have larger values, in which case they should
- * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function.
- * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA
- * and defining its own value for SHMLBA.
- *
- * The kernel also permits SHMLBA to be set by the architecture to a
- * value larger than the page size without setting __ARCH_FORCE_SHMLBA;
- * this means that addresses are rounded to the large size if
- * SHM_RND is set but addresses not aligned to that size are not rejected
- * as long as they are at least page-aligned. Since the only architecture
- * which uses this is ia64 this code doesn't provide for that oddity.
- */
-static inline abi_ulong target_shmlba(CPUArchState *cpu_env)
-{
- return TARGET_PAGE_SIZE;
-}
-#endif
-
-static inline abi_ulong do_shmat(CPUArchState *cpu_env,
- int shmid, abi_ulong shmaddr, int shmflg)
-{
- CPUState *cpu = env_cpu(cpu_env);
- abi_long raddr;
- void *host_raddr;
- struct shmid_ds shm_info;
- int i,ret;
- abi_ulong shmlba;
-
- /* shmat pointers are always untagged */
-
- /* find out the length of the shared memory segment */
- ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
- if (is_error(ret)) {
- /* can't get length, bail out */
- return ret;
- }
-
- shmlba = target_shmlba(cpu_env);
-
- if (shmaddr & (shmlba - 1)) {
- if (shmflg & SHM_RND) {
- shmaddr &= ~(shmlba - 1);
- } else {
- return -TARGET_EINVAL;
- }
- }
- if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) {
- return -TARGET_EINVAL;
- }
-
- mmap_lock();
-
- /*
- * We're mapping shared memory, so ensure we generate code for parallel
- * execution and flush old translations. This will work up to the level
- * supported by the host -- anything that requires EXCP_ATOMIC will not
- * be atomic with respect to an external process.
- */
- if (!(cpu->tcg_cflags & CF_PARALLEL)) {
- cpu->tcg_cflags |= CF_PARALLEL;
- tb_flush(cpu);
- }
-
- if (shmaddr)
- host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg);
- else {
- abi_ulong mmap_start;
-
- /* In order to use the host shmat, we need to honor host SHMLBA. */
- mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba));
-
- if (mmap_start == -1) {
- errno = ENOMEM;
- host_raddr = (void *)-1;
- } else
- host_raddr = shmat(shmid, g2h_untagged(mmap_start),
- shmflg | SHM_REMAP);
- }
-
- if (host_raddr == (void *)-1) {
- mmap_unlock();
- return get_errno((long)host_raddr);
- }
- raddr=h2g((unsigned long)host_raddr);
-
- page_set_flags(raddr, raddr + shm_info.shm_segsz - 1,
- PAGE_VALID | PAGE_RESET | PAGE_READ |
- (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
-
- for (i = 0; i < N_SHM_REGIONS; i++) {
- if (!shm_regions[i].in_use) {
- shm_regions[i].in_use = true;
- shm_regions[i].start = raddr;
- shm_regions[i].size = shm_info.shm_segsz;
- break;
- }
- }
-
- mmap_unlock();
- return raddr;
-
-}
-
-static inline abi_long do_shmdt(abi_ulong shmaddr)
-{
- int i;
- abi_long rv;
-
- /* shmdt pointers are always untagged */
-
- mmap_lock();
-
- for (i = 0; i < N_SHM_REGIONS; ++i) {
- if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) {
- shm_regions[i].in_use = false;
- page_set_flags(shmaddr, shmaddr + shm_regions[i].size - 1, 0);
- break;
- }
- }
- rv = get_errno(shmdt(g2h_untagged(shmaddr)));
-
- mmap_unlock();
-
- return rv;
-}
-
#ifdef TARGET_NR_ipc
/* ??? This only works with linear mappings. */
/* do_ipc() must return target values and target errnos. */
default:
{
abi_ulong raddr;
- raddr = do_shmat(cpu_env, first, ptr, second);
+ raddr = target_shmat(cpu_env, first, ptr, second);
if (is_error(raddr))
return get_errno(raddr);
if (put_user_ual(raddr, third))
}
break;
case IPCOP_shmdt:
- ret = do_shmdt(ptr);
+ ret = target_shmdt(ptr);
break;
case IPCOP_shmget:
{
void *gspec = argptr;
void *cur_data = host_data;
- const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
- int spec_size = thunk_type_size(arg_type, 0);
+ const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
+ int spec_size = thunk_type_size(dm_arg_type, 0);
int i;
for (i = 0; i < host_dm->target_count; i++) {
uint32_t next;
int slen;
- thunk_convert(spec, gspec, arg_type, THUNK_HOST);
+ thunk_convert(spec, gspec, dm_arg_type, THUNK_HOST);
slen = strlen((char*)gspec + spec_size) + 1;
next = spec->next;
spec->next = sizeof(*spec) + slen;
struct dm_name_list *nl = (void*)host_dm + host_dm->data_start;
uint32_t remaining_data = guest_data_size;
void *cur_data = argptr;
- const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };
+ const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };
int nl_size = 12; /* can't use thunk_size due to alignment */
while (1) {
host_dm->flags |= DM_BUFFER_FULL_FLAG;
break;
}
- thunk_convert(cur_data, nl, arg_type, THUNK_TARGET);
+ thunk_convert(cur_data, nl, dm_arg_type, THUNK_TARGET);
strcpy(cur_data + nl_size, nl->name);
cur_data += nl->next;
remaining_data -= nl->next;
{
struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start;
void *cur_data = argptr;
- const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
- int spec_size = thunk_type_size(arg_type, 0);
+ const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
+ int spec_size = thunk_type_size(dm_arg_type, 0);
int i;
for (i = 0; i < host_dm->target_count; i++) {
host_dm->flags |= DM_BUFFER_FULL_FLAG;
break;
}
- thunk_convert(cur_data, spec, arg_type, THUNK_TARGET);
+ thunk_convert(cur_data, spec, dm_arg_type, THUNK_TARGET);
strcpy(cur_data + spec_size, (char*)&spec[1]);
cur_data = argptr + spec->next;
spec = (void*)host_dm + host_dm->data_start + next;
struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start;
uint32_t remaining_data = guest_data_size;
void *cur_data = argptr;
- const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) };
- int vers_size = thunk_type_size(arg_type, 0);
+ const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) };
+ int vers_size = thunk_type_size(dm_arg_type, 0);
while (1) {
uint32_t next = vers->next;
host_dm->flags |= DM_BUFFER_FULL_FLAG;
break;
}
- thunk_convert(cur_data, vers, arg_type, THUNK_TARGET);
+ thunk_convert(cur_data, vers, dm_arg_type, THUNK_TARGET);
strcpy(cur_data + vers_size, vers->name);
cur_data += vers->next;
remaining_data -= vers->next;
{ TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF },
{ TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL },
{ TARGET_IUTF8, TARGET_IUTF8, IUTF8, IUTF8},
- { 0, 0, 0, 0 }
};
static const bitmask_transtbl oflag_tbl[] = {
{ TARGET_VTDLY, TARGET_VT1, VTDLY, VT1 },
{ TARGET_FFDLY, TARGET_FF0, FFDLY, FF0 },
{ TARGET_FFDLY, TARGET_FF1, FFDLY, FF1 },
- { 0, 0, 0, 0 }
};
static const bitmask_transtbl cflag_tbl[] = {
{ TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL },
{ TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL },
{ TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS },
- { 0, 0, 0, 0 }
};
static const bitmask_transtbl lflag_tbl[] = {
{ TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN },
{ TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN },
{ TARGET_EXTPROC, TARGET_EXTPROC, EXTPROC, EXTPROC},
- { 0, 0, 0, 0 }
};
static void target_to_host_termios (void *dst, const void *src)
.print = print_termios,
};
+/* If the host does not provide these bits, they may be safely discarded. */
+#ifndef MAP_SYNC
+#define MAP_SYNC 0
+#endif
+#ifndef MAP_UNINITIALIZED
+#define MAP_UNINITIALIZED 0
+#endif
+
static const bitmask_transtbl mmap_flags_tbl[] = {
- { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED },
- { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE },
{ TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED },
{ TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS,
MAP_ANONYMOUS, MAP_ANONYMOUS },
Recognize it for the target insofar as we do not want to pass
it through to the host. */
{ TARGET_MAP_STACK, TARGET_MAP_STACK, 0, 0 },
- { 0, 0, 0, 0 }
+ { TARGET_MAP_NONBLOCK, TARGET_MAP_NONBLOCK, MAP_NONBLOCK, MAP_NONBLOCK },
+ { TARGET_MAP_POPULATE, TARGET_MAP_POPULATE, MAP_POPULATE, MAP_POPULATE },
+ { TARGET_MAP_FIXED_NOREPLACE, TARGET_MAP_FIXED_NOREPLACE,
+ MAP_FIXED_NOREPLACE, MAP_FIXED_NOREPLACE },
+ { TARGET_MAP_UNINITIALIZED, TARGET_MAP_UNINITIALIZED,
+ MAP_UNINITIALIZED, MAP_UNINITIALIZED },
};
+/*
+ * Arrange for legacy / undefined architecture specific flags to be
+ * ignored by mmap handling code.
+ */
+#ifndef TARGET_MAP_32BIT
+#define TARGET_MAP_32BIT 0
+#endif
+#ifndef TARGET_MAP_HUGE_2MB
+#define TARGET_MAP_HUGE_2MB 0
+#endif
+#ifndef TARGET_MAP_HUGE_1GB
+#define TARGET_MAP_HUGE_1GB 0
+#endif
+
+static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot,
+ int target_flags, int fd, off_t offset)
+{
+ /*
+ * The historical set of flags that all mmap types implicitly support.
+ */
+ enum {
+ TARGET_LEGACY_MAP_MASK = TARGET_MAP_SHARED
+ | TARGET_MAP_PRIVATE
+ | TARGET_MAP_FIXED
+ | TARGET_MAP_ANONYMOUS
+ | TARGET_MAP_DENYWRITE
+ | TARGET_MAP_EXECUTABLE
+ | TARGET_MAP_UNINITIALIZED
+ | TARGET_MAP_GROWSDOWN
+ | TARGET_MAP_LOCKED
+ | TARGET_MAP_NORESERVE
+ | TARGET_MAP_POPULATE
+ | TARGET_MAP_NONBLOCK
+ | TARGET_MAP_STACK
+ | TARGET_MAP_HUGETLB
+ | TARGET_MAP_32BIT
+ | TARGET_MAP_HUGE_2MB
+ | TARGET_MAP_HUGE_1GB
+ };
+ int host_flags;
+
+ switch (target_flags & TARGET_MAP_TYPE) {
+ case TARGET_MAP_PRIVATE:
+ host_flags = MAP_PRIVATE;
+ break;
+ case TARGET_MAP_SHARED:
+ host_flags = MAP_SHARED;
+ break;
+ case TARGET_MAP_SHARED_VALIDATE:
+ /*
+ * MAP_SYNC is only supported for MAP_SHARED_VALIDATE, and is
+ * therefore omitted from mmap_flags_tbl and TARGET_LEGACY_MAP_MASK.
+ */
+ if (target_flags & ~(TARGET_LEGACY_MAP_MASK | TARGET_MAP_SYNC)) {
+ return -TARGET_EOPNOTSUPP;
+ }
+ host_flags = MAP_SHARED_VALIDATE;
+ if (target_flags & TARGET_MAP_SYNC) {
+ host_flags |= MAP_SYNC;
+ }
+ break;
+ default:
+ return -TARGET_EINVAL;
+ }
+ host_flags |= target_to_host_bitmask(target_flags, mmap_flags_tbl);
+
+ return get_errno(target_mmap(addr, len, prot, host_flags, fd, offset));
+}
+
/*
* NOTE: TARGET_ABI32 is defined for TARGET_I386 (but not for TARGET_X86_64)
* TARGET_I386 is defined if TARGET_X86_64 is defined
ret = get_errno(safe_fcntl(fd, host_cmd, arg));
if (ret >= 0) {
ret = host_to_target_bitmask(ret, fcntl_flags_tbl);
+ /* tell 32-bit guests it uses largefile on 64-bit hosts: */
+ if (O_LARGEFILE == 0 && HOST_LONG_BITS == 64) {
+ ret |= TARGET_O_LARGEFILE;
+ }
}
break;
return 0;
}
-static int open_self_maps(CPUArchState *cpu_env, int fd)
+struct open_self_maps_data {
+ TaskState *ts;
+ IntervalTreeRoot *host_maps;
+ int fd;
+ bool smaps;
+};
+
+/*
+ * Subroutine to output one line of /proc/self/maps,
+ * or one region of /proc/self/smaps.
+ */
+
+#ifdef TARGET_HPPA
+# define test_stack(S, E, L) (E == L)
+#else
+# define test_stack(S, E, L) (S == L)
+#endif
+
+static void open_self_maps_4(const struct open_self_maps_data *d,
+ const MapInfo *mi, abi_ptr start,
+ abi_ptr end, unsigned flags)
{
- CPUState *cpu = env_cpu(cpu_env);
- TaskState *ts = cpu->opaque;
- GSList *map_info = read_self_maps();
- GSList *s;
+ const struct image_info *info = d->ts->info;
+ const char *path = mi->path;
+ uint64_t offset;
+ int fd = d->fd;
int count;
- for (s = map_info; s; s = g_slist_next(s)) {
- MapInfo *e = (MapInfo *) s->data;
+ if (test_stack(start, end, info->stack_limit)) {
+ path = "[stack]";
+ } else if (start == info->brk) {
+ path = "[heap]";
+ } else if (start == info->vdso) {
+ path = "[vdso]";
+ }
+
+ /* Except null device (MAP_ANON), adjust offset for this fragment. */
+ offset = mi->offset;
+ if (mi->dev) {
+ uintptr_t hstart = (uintptr_t)g2h_untagged(start);
+ offset += hstart - mi->itree.start;
+ }
+
+ count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr
+ " %c%c%c%c %08" PRIx64 " %02x:%02x %"PRId64,
+ start, end,
+ (flags & PAGE_READ) ? 'r' : '-',
+ (flags & PAGE_WRITE_ORG) ? 'w' : '-',
+ (flags & PAGE_EXEC) ? 'x' : '-',
+ mi->is_priv ? 'p' : 's',
+ offset, major(mi->dev), minor(mi->dev),
+ (uint64_t)mi->inode);
+ if (path) {
+ dprintf(fd, "%*s%s\n", 73 - count, "", path);
+ } else {
+ dprintf(fd, "\n");
+ }
+
+ if (d->smaps) {
+ unsigned long size = end - start;
+ unsigned long page_size_kb = TARGET_PAGE_SIZE >> 10;
+ unsigned long size_kb = size >> 10;
+
+ dprintf(fd, "Size: %lu kB\n"
+ "KernelPageSize: %lu kB\n"
+ "MMUPageSize: %lu kB\n"
+ "Rss: 0 kB\n"
+ "Pss: 0 kB\n"
+ "Pss_Dirty: 0 kB\n"
+ "Shared_Clean: 0 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 0 kB\n"
+ "Anonymous: %lu kB\n"
+ "LazyFree: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "ShmemPmdMapped: 0 kB\n"
+ "FilePmdMapped: 0 kB\n"
+ "Shared_Hugetlb: 0 kB\n"
+ "Private_Hugetlb: 0 kB\n"
+ "Swap: 0 kB\n"
+ "SwapPss: 0 kB\n"
+ "Locked: 0 kB\n"
+ "THPeligible: 0\n"
+ "VmFlags:%s%s%s%s%s%s%s%s\n",
+ size_kb, page_size_kb, page_size_kb,
+ (flags & PAGE_ANON ? size_kb : 0),
+ (flags & PAGE_READ) ? " rd" : "",
+ (flags & PAGE_WRITE_ORG) ? " wr" : "",
+ (flags & PAGE_EXEC) ? " ex" : "",
+ mi->is_priv ? "" : " sh",
+ (flags & PAGE_READ) ? " mr" : "",
+ (flags & PAGE_WRITE_ORG) ? " mw" : "",
+ (flags & PAGE_EXEC) ? " me" : "",
+ mi->is_priv ? "" : " ms");
+ }
+}
- if (h2g_valid(e->start)) {
- unsigned long min = e->start;
- unsigned long max = e->end;
- int flags = page_get_flags(h2g(min));
- const char *path;
+/*
+ * Callback for walk_memory_regions, when read_self_maps() fails.
+ * Proceed without the benefit of host /proc/self/maps cross-check.
+ */
+static int open_self_maps_3(void *opaque, target_ulong guest_start,
+ target_ulong guest_end, unsigned long flags)
+{
+ static const MapInfo mi = { .is_priv = true };
- max = h2g_valid(max - 1) ?
- max : (uintptr_t) g2h_untagged(GUEST_ADDR_MAX) + 1;
+ open_self_maps_4(opaque, &mi, guest_start, guest_end, flags);
+ return 0;
+}
- if (page_check_range(h2g(min), max - min, flags) == -1) {
- continue;
- }
+/*
+ * Callback for walk_memory_regions, when read_self_maps() succeeds.
+ */
+static int open_self_maps_2(void *opaque, target_ulong guest_start,
+ target_ulong guest_end, unsigned long flags)
+{
+ const struct open_self_maps_data *d = opaque;
+ uintptr_t host_start = (uintptr_t)g2h_untagged(guest_start);
+ uintptr_t host_last = (uintptr_t)g2h_untagged(guest_end - 1);
-#ifdef TARGET_HPPA
- if (h2g(max) == ts->info->stack_limit) {
-#else
- if (h2g(min) == ts->info->stack_limit) {
-#endif
- path = "[stack]";
- } else {
- path = e->path;
- }
-
- count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr
- " %c%c%c%c %08" PRIx64 " %s %"PRId64,
- h2g(min), h2g(max - 1) + 1,
- (flags & PAGE_READ) ? 'r' : '-',
- (flags & PAGE_WRITE_ORG) ? 'w' : '-',
- (flags & PAGE_EXEC) ? 'x' : '-',
- e->is_priv ? 'p' : 's',
- (uint64_t) e->offset, e->dev, e->inode);
- if (path) {
- dprintf(fd, "%*s%s\n", 73 - count, "", path);
- } else {
- dprintf(fd, "\n");
- }
+ while (1) {
+ IntervalTreeNode *n =
+ interval_tree_iter_first(d->host_maps, host_start, host_start);
+ MapInfo *mi = container_of(n, MapInfo, itree);
+ uintptr_t this_hlast = MIN(host_last, n->last);
+ target_ulong this_gend = h2g(this_hlast) + 1;
+
+ open_self_maps_4(d, mi, guest_start, this_gend, flags);
+
+ if (this_hlast == host_last) {
+ return 0;
}
+ host_start = this_hlast + 1;
+ guest_start = h2g(host_start);
}
+}
- free_self_maps(map_info);
-
-#ifdef TARGET_VSYSCALL_PAGE
- /*
- * We only support execution from the vsyscall page.
- * This is as if CONFIG_LEGACY_VSYSCALL_XONLY=y from v5.3.
- */
- count = dprintf(fd, TARGET_FMT_lx "-" TARGET_FMT_lx
- " --xp 00000000 00:00 0",
- TARGET_VSYSCALL_PAGE, TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE);
- dprintf(fd, "%*s%s\n", 73 - count, "", "[vsyscall]");
-#endif
+static int open_self_maps_1(CPUArchState *env, int fd, bool smaps)
+{
+ struct open_self_maps_data d = {
+ .ts = env_cpu(env)->opaque,
+ .host_maps = read_self_maps(),
+ .fd = fd,
+ .smaps = smaps
+ };
+ if (d.host_maps) {
+ walk_memory_regions(&d, open_self_maps_2);
+ free_self_maps(d.host_maps);
+ } else {
+ walk_memory_regions(&d, open_self_maps_3);
+ }
return 0;
}
+static int open_self_maps(CPUArchState *cpu_env, int fd)
+{
+ return open_self_maps_1(cpu_env, fd, false);
+}
+
+static int open_self_smaps(CPUArchState *cpu_env, int fd)
+{
+ return open_self_maps_1(cpu_env, fd, true);
+}
+
static int open_self_stat(CPUArchState *cpu_env, int fd)
{
CPUState *cpu = env_cpu(cpu_env);
}
}
+#include "target_proc.h"
+
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \
- defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) || \
- defined(TARGET_RISCV)
+ defined(HAVE_ARCH_PROC_CPUINFO) || \
+ defined(HAVE_ARCH_PROC_HARDWARE)
static int is_proc(const char *filename, const char *entry)
{
return strcmp(filename, entry) == 0;
}
#endif
-#if defined(TARGET_SPARC)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- dprintf(fd, "type\t\t: sun4u\n");
- return 0;
-}
-#endif
-
-#if defined(TARGET_HPPA)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- int i, num_cpus;
-
- num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- for (i = 0; i < num_cpus; i++) {
- dprintf(fd, "processor\t: %d\n", i);
- dprintf(fd, "cpu family\t: PA-RISC 1.1e\n");
- dprintf(fd, "cpu\t\t: PA7300LC (PCX-L2)\n");
- dprintf(fd, "capabilities\t: os32\n");
- dprintf(fd, "model\t\t: 9000/778/B160L - "
- "Merlin L2 160 QEMU (9000/778/B160L)\n\n");
- }
- return 0;
-}
-#endif
-
-#if defined(TARGET_RISCV)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- int i;
- int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- RISCVCPU *cpu = env_archcpu(cpu_env);
- const RISCVCPUConfig *cfg = riscv_cpu_cfg((CPURISCVState *) cpu_env);
- char *isa_string = riscv_isa_string(cpu);
- const char *mmu;
-
- if (cfg->mmu) {
- mmu = (cpu_env->xl == MXL_RV32) ? "sv32" : "sv48";
- } else {
- mmu = "none";
- }
-
- for (i = 0; i < num_cpus; i++) {
- dprintf(fd, "processor\t: %d\n", i);
- dprintf(fd, "hart\t\t: %d\n", i);
- dprintf(fd, "isa\t\t: %s\n", isa_string);
- dprintf(fd, "mmu\t\t: %s\n", mmu);
- dprintf(fd, "uarch\t\t: qemu\n\n");
- }
-
- g_free(isa_string);
- return 0;
-}
-#endif
-
-#if defined(TARGET_M68K)
-static int open_hardware(CPUArchState *cpu_env, int fd)
-{
- dprintf(fd, "Model:\t\tqemu-m68k\n");
- return 0;
-}
-#endif
-
-static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode)
+int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
+ int flags, mode_t mode, bool safe)
{
+ g_autofree char *proc_name = NULL;
+ const char *pathname;
struct fake_open {
const char *filename;
int (*fill)(CPUArchState *cpu_env, int fd);
const struct fake_open *fake_open;
static const struct fake_open fakes[] = {
{ "maps", open_self_maps, is_proc_myself },
+ { "smaps", open_self_smaps, is_proc_myself },
{ "stat", open_self_stat, is_proc_myself },
{ "auxv", open_self_auxv, is_proc_myself },
{ "cmdline", open_self_cmdline, is_proc_myself },
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
{ "/proc/net/route", open_net_route, is_proc },
#endif
-#if defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined(TARGET_RISCV)
+#if defined(HAVE_ARCH_PROC_CPUINFO)
{ "/proc/cpuinfo", open_cpuinfo, is_proc },
#endif
-#if defined(TARGET_M68K)
+#if defined(HAVE_ARCH_PROC_HARDWARE)
{ "/proc/hardware", open_hardware, is_proc },
#endif
{ NULL, NULL, NULL }
};
+ /* if this is a file from /proc/ filesystem, expand full name */
+ proc_name = realpath(fname, NULL);
+ if (proc_name && strncmp(proc_name, "/proc/", 6) == 0) {
+ pathname = proc_name;
+ } else {
+ pathname = fname;
+ }
+
if (is_proc_myself(pathname, "exe")) {
- return safe_openat(dirfd, exec_path, flags, mode);
+ if (safe) {
+ return safe_openat(dirfd, exec_path, flags, mode);
+ } else {
+ return openat(dirfd, exec_path, flags, mode);
+ }
}
for (fake_open = fakes; fake_open->filename; fake_open++) {
return fd;
}
- return safe_openat(dirfd, path(pathname), flags, mode);
+ if (safe) {
+ return safe_openat(dirfd, path(pathname), flags, mode);
+ } else {
+ return openat(dirfd, path(pathname), flags, mode);
+ }
}
-static int do_execveat(CPUArchState *cpu_env, int dirfd,
- abi_long pathname, abi_long guest_argp,
- abi_long guest_envp, int flags)
+ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz)
+{
+ ssize_t ret;
+
+ if (!pathname || !buf) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (!bufsiz) {
+ /* Short circuit this for the magic exe check. */
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (is_proc_myself((const char *)pathname, "exe")) {
+ /*
+ * Don't worry about sign mismatch as earlier mapping
+ * logic would have thrown a bad address error.
+ */
+ ret = MIN(strlen(exec_path), bufsiz);
+ /* We cannot NUL terminate the string. */
+ memcpy(buf, exec_path, ret);
+ } else {
+ ret = readlink(path(pathname), buf, bufsiz);
+ }
+
+ return ret;
+}
+
+static int do_execv(CPUArchState *cpu_env, int dirfd,
+ abi_long pathname, abi_long guest_argp,
+ abi_long guest_envp, int flags, bool is_execveat)
{
int ret;
char **argp, **envp;
goto execve_efault;
}
+ const char *exe = p;
if (is_proc_myself(p, "exe")) {
- ret = get_errno(safe_execveat(dirfd, exec_path, argp, envp, flags));
- } else {
- ret = get_errno(safe_execveat(dirfd, p, argp, envp, flags));
+ exe = exec_path;
}
+ ret = is_execveat
+ ? safe_execveat(dirfd, exe, argp, envp, flags)
+ : safe_execve(exe, argp, envp);
+ ret = get_errno(ret);
unlock_user(p, pathname, 0);
}
#endif /* TARGET_NR_getdents64 */
+#if defined(TARGET_NR_riscv_hwprobe)
+
+#define RISCV_HWPROBE_KEY_MVENDORID 0
+#define RISCV_HWPROBE_KEY_MARCHID 1
+#define RISCV_HWPROBE_KEY_MIMPID 2
+
+#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
+#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
+
+#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
+#define RISCV_HWPROBE_IMA_FD (1 << 0)
+#define RISCV_HWPROBE_IMA_C (1 << 1)
+#define RISCV_HWPROBE_IMA_V (1 << 2)
+#define RISCV_HWPROBE_EXT_ZBA (1 << 3)
+#define RISCV_HWPROBE_EXT_ZBB (1 << 4)
+#define RISCV_HWPROBE_EXT_ZBS (1 << 5)
+
+#define RISCV_HWPROBE_KEY_CPUPERF_0 5
+#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
+#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
+#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
+#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
+#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
+#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
+
+struct riscv_hwprobe {
+ abi_llong key;
+ abi_ullong value;
+};
+
+static void risc_hwprobe_fill_pairs(CPURISCVState *env,
+ struct riscv_hwprobe *pair,
+ size_t pair_count)
+{
+ const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
+
+ for (; pair_count > 0; pair_count--, pair++) {
+ abi_llong key;
+ abi_ullong value;
+ __put_user(0, &pair->value);
+ __get_user(key, &pair->key);
+ switch (key) {
+ case RISCV_HWPROBE_KEY_MVENDORID:
+ __put_user(cfg->mvendorid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_MARCHID:
+ __put_user(cfg->marchid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_MIMPID:
+ __put_user(cfg->mimpid, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
+ value = riscv_has_ext(env, RVI) &&
+ riscv_has_ext(env, RVM) &&
+ riscv_has_ext(env, RVA) ?
+ RISCV_HWPROBE_BASE_BEHAVIOR_IMA : 0;
+ __put_user(value, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_IMA_EXT_0:
+ value = riscv_has_ext(env, RVF) &&
+ riscv_has_ext(env, RVD) ?
+ RISCV_HWPROBE_IMA_FD : 0;
+ value |= riscv_has_ext(env, RVC) ?
+ RISCV_HWPROBE_IMA_C : 0;
+ value |= riscv_has_ext(env, RVV) ?
+ RISCV_HWPROBE_IMA_V : 0;
+ value |= cfg->ext_zba ?
+ RISCV_HWPROBE_EXT_ZBA : 0;
+ value |= cfg->ext_zbb ?
+ RISCV_HWPROBE_EXT_ZBB : 0;
+ value |= cfg->ext_zbs ?
+ RISCV_HWPROBE_EXT_ZBS : 0;
+ __put_user(value, &pair->value);
+ break;
+ case RISCV_HWPROBE_KEY_CPUPERF_0:
+ __put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value);
+ break;
+ default:
+ __put_user(-1, &pair->key);
+ break;
+ }
+ }
+}
+
+static int cpu_set_valid(abi_long arg3, abi_long arg4)
+{
+ int ret, i, tmp;
+ size_t host_mask_size, target_mask_size;
+ unsigned long *host_mask;
+
+ /*
+ * cpu_set_t represent CPU masks as bit masks of type unsigned long *.
+ * arg3 contains the cpu count.
+ */
+ tmp = (8 * sizeof(abi_ulong));
+ target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong);
+ host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) &
+ ~(sizeof(*host_mask) - 1);
+
+ host_mask = alloca(host_mask_size);
+
+ ret = target_to_host_cpu_mask(host_mask, host_mask_size,
+ arg4, target_mask_size);
+ if (ret != 0) {
+ return ret;
+ }
+
+ for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) {
+ if (host_mask[i] != 0) {
+ return 0;
+ }
+ }
+ return -TARGET_EINVAL;
+}
+
+static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
+ abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5)
+{
+ int ret;
+ struct riscv_hwprobe *host_pairs;
+
+ /* flags must be 0 */
+ if (arg5 != 0) {
+ return -TARGET_EINVAL;
+ }
+
+ /* check cpu_set */
+ if (arg3 != 0) {
+ ret = cpu_set_valid(arg3, arg4);
+ if (ret != 0) {
+ return ret;
+ }
+ } else if (arg4 != 0) {
+ return -TARGET_EINVAL;
+ }
+
+ /* no pairs */
+ if (arg2 == 0) {
+ return 0;
+ }
+
+ host_pairs = lock_user(VERIFY_WRITE, arg1,
+ sizeof(*host_pairs) * (size_t)arg2, 0);
+ if (host_pairs == NULL) {
+ return -TARGET_EFAULT;
+ }
+ risc_hwprobe_fill_pairs(cpu_env, host_pairs, arg2);
+ unlock_user(host_pairs, arg1, sizeof(*host_pairs) * (size_t)arg2);
+ return 0;
+}
+#endif /* TARGET_NR_riscv_hwprobe */
+
#if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root)
_syscall2(int, pivot_root, const char *, new_root, const char *, put_old)
#endif
+#if defined(TARGET_NR_open_tree) && defined(__NR_open_tree)
+#define __NR_sys_open_tree __NR_open_tree
+_syscall3(int, sys_open_tree, int, __dfd, const char *, __filename,
+ unsigned int, __flags)
+#endif
+
#if defined(TARGET_NR_move_mount) && defined(__NR_move_mount)
#define __NR_sys_move_mount __NR_move_mount
_syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname,
case TARGET_NR_open:
if (!(p = lock_user_string(arg1)))
return -TARGET_EFAULT;
- ret = get_errno(do_openat(cpu_env, AT_FDCWD, p,
+ ret = get_errno(do_guest_openat(cpu_env, AT_FDCWD, p,
target_to_host_bitmask(arg2, fcntl_flags_tbl),
- arg3));
+ arg3, true));
fd_trans_unregister(ret);
unlock_user(p, arg1, 0);
return ret;
case TARGET_NR_openat:
if (!(p = lock_user_string(arg2)))
return -TARGET_EFAULT;
- ret = get_errno(do_openat(cpu_env, arg1, p,
+ ret = get_errno(do_guest_openat(cpu_env, arg1, p,
target_to_host_bitmask(arg3, fcntl_flags_tbl),
- arg4));
+ arg4, true));
fd_trans_unregister(ret);
unlock_user(p, arg2, 0);
return ret;
return ret;
#endif
case TARGET_NR_execveat:
- return do_execveat(cpu_env, arg1, arg2, arg3, arg4, arg5);
+ return do_execv(cpu_env, arg1, arg2, arg3, arg4, arg5, true);
case TARGET_NR_execve:
- return do_execveat(cpu_env, AT_FDCWD, arg1, arg2, arg3, 0);
+ return do_execv(cpu_env, AT_FDCWD, arg1, arg2, arg3, 0, false);
case TARGET_NR_chdir:
if (!(p = lock_user_string(arg1)))
return -TARGET_EFAULT;
return ret;
}
#endif
+#if defined(TARGET_NR_open_tree) && defined(__NR_open_tree)
+ case TARGET_NR_open_tree:
+ {
+ void *p2;
+ int host_flags;
+
+ if (!arg2) {
+ return -TARGET_EFAULT;
+ }
+
+ p2 = lock_user_string(arg2);
+ if (!p2) {
+ return -TARGET_EFAULT;
+ }
+
+ host_flags = arg3 & ~TARGET_O_CLOEXEC;
+ if (arg3 & TARGET_O_CLOEXEC) {
+ host_flags |= O_CLOEXEC;
+ }
+
+ ret = get_errno(sys_open_tree(arg1, p2, host_flags));
+
+ unlock_user(p2, arg2, 0);
+
+ return ret;
+ }
+#endif
#ifdef TARGET_NR_stime /* not on alpha */
case TARGET_NR_stime:
{
void *p2;
p = lock_user_string(arg1);
p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
- if (!p || !p2) {
- ret = -TARGET_EFAULT;
- } else if (!arg3) {
- /* Short circuit this for the magic exe check. */
- ret = -TARGET_EINVAL;
- } else if (is_proc_myself((const char *)p, "exe")) {
- /*
- * Don't worry about sign mismatch as earlier mapping
- * logic would have thrown a bad address error.
- */
- ret = MIN(strlen(exec_path), arg3);
- /* We cannot NUL terminate the string. */
- memcpy(p2, exec_path, ret);
- } else {
- ret = get_errno(readlink(path(p), p2, arg3));
- }
+ ret = get_errno(do_guest_readlink(p, p2, arg3));
unlock_user(p2, arg2, ret);
unlock_user(p, arg1, 0);
}
v5 = tswapal(v[4]);
v6 = tswapal(v[5]);
unlock_user(v, arg1, 0);
- ret = get_errno(target_mmap(v1, v2, v3,
- target_to_host_bitmask(v4, mmap_flags_tbl),
- v5, v6));
+ return do_mmap(v1, v2, v3, v4, v5, v6);
}
#else
/* mmap pointers are always untagged */
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6));
+ return do_mmap(arg1, arg2, arg3, arg4, arg5, arg6);
#endif
- return ret;
#endif
#ifdef TARGET_NR_mmap2
case TARGET_NR_mmap2:
#ifndef MMAP_SHIFT
#define MMAP_SHIFT 12
#endif
- ret = target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5, arg6 << MMAP_SHIFT);
- return get_errno(ret);
+ return do_mmap(arg1, arg2, arg3, arg4, arg5,
+ (off_t)(abi_ulong)arg6 << MMAP_SHIFT);
#endif
case TARGET_NR_munmap:
arg1 = cpu_untagged_addr(cpu, arg1);
#endif
#ifdef TARGET_NR_shmat
case TARGET_NR_shmat:
- return do_shmat(cpu_env, arg1, arg2, arg3);
+ return target_shmat(cpu_env, arg1, arg2, arg3);
#endif
#ifdef TARGET_NR_shmdt
case TARGET_NR_shmdt:
- return do_shmdt(arg1);
+ return target_shmdt(arg1);
#endif
case TARGET_NR_fsync:
return get_errno(fsync(arg1));
#if defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME)
case TARGET_NR_clock_adjtime:
{
- struct timex htx, *phtx = &htx;
+ struct timex htx;
- if (target_to_host_timex(phtx, arg2) != 0) {
+ if (target_to_host_timex(&htx, arg2) != 0) {
return -TARGET_EFAULT;
}
- ret = get_errno(clock_adjtime(arg1, phtx));
- if (!is_error(ret) && phtx) {
- if (host_to_target_timex(arg2, phtx) != 0) {
- return -TARGET_EFAULT;
- }
+ ret = get_errno(clock_adjtime(arg1, &htx));
+ if (!is_error(ret) && host_to_target_timex(arg2, &htx)) {
+ return -TARGET_EFAULT;
}
}
return ret;
}
case TARGET_NR_getcpu:
{
- unsigned cpu, node;
- ret = get_errno(sys_getcpu(arg1 ? &cpu : NULL,
+ unsigned cpuid, node;
+ ret = get_errno(sys_getcpu(arg1 ? &cpuid : NULL,
arg2 ? &node : NULL,
NULL));
if (is_error(ret)) {
return ret;
}
- if (arg1 && put_user_u32(cpu, arg1)) {
+ if (arg1 && put_user_u32(cpuid, arg1)) {
return -TARGET_EFAULT;
}
if (arg2 && put_user_u32(node, arg2)) {
case TARGET_NR_setregid:
return get_errno(setregid(low2highgid(arg1), low2highgid(arg2)));
case TARGET_NR_getgroups:
- {
+ { /* the same code as for TARGET_NR_getgroups32 */
int gidsetsize = arg1;
target_id *target_grouplist;
- gid_t *grouplist;
+ g_autofree gid_t *grouplist = NULL;
int i;
- grouplist = alloca(gidsetsize * sizeof(gid_t));
+ if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (gidsetsize > 0) {
+ grouplist = g_try_new(gid_t, gidsetsize);
+ if (!grouplist) {
+ return -TARGET_ENOMEM;
+ }
+ }
ret = get_errno(getgroups(gidsetsize, grouplist));
- if (gidsetsize == 0)
- return ret;
- if (!is_error(ret)) {
- target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0);
- if (!target_grouplist)
+ if (!is_error(ret) && gidsetsize > 0) {
+ target_grouplist = lock_user(VERIFY_WRITE, arg2,
+ gidsetsize * sizeof(target_id), 0);
+ if (!target_grouplist) {
return -TARGET_EFAULT;
- for(i = 0;i < ret; i++)
+ }
+ for (i = 0; i < ret; i++) {
target_grouplist[i] = tswapid(high2lowgid(grouplist[i]));
- unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id));
+ }
+ unlock_user(target_grouplist, arg2,
+ gidsetsize * sizeof(target_id));
}
+ return ret;
}
- return ret;
case TARGET_NR_setgroups:
- {
+ { /* the same code as for TARGET_NR_setgroups32 */
int gidsetsize = arg1;
target_id *target_grouplist;
- gid_t *grouplist = NULL;
+ g_autofree gid_t *grouplist = NULL;
int i;
- if (gidsetsize) {
- grouplist = alloca(gidsetsize * sizeof(gid_t));
- target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1);
+
+ if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (gidsetsize > 0) {
+ grouplist = g_try_new(gid_t, gidsetsize);
+ if (!grouplist) {
+ return -TARGET_ENOMEM;
+ }
+ target_grouplist = lock_user(VERIFY_READ, arg2,
+ gidsetsize * sizeof(target_id), 1);
if (!target_grouplist) {
return -TARGET_EFAULT;
}
for (i = 0; i < gidsetsize; i++) {
grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
}
- unlock_user(target_grouplist, arg2, 0);
+ unlock_user(target_grouplist, arg2,
+ gidsetsize * sizeof(target_id));
}
return get_errno(setgroups(gidsetsize, grouplist));
}
#endif
#ifdef TARGET_NR_getgroups32
case TARGET_NR_getgroups32:
- {
+ { /* the same code as for TARGET_NR_getgroups */
int gidsetsize = arg1;
uint32_t *target_grouplist;
- gid_t *grouplist;
+ g_autofree gid_t *grouplist = NULL;
int i;
- grouplist = alloca(gidsetsize * sizeof(gid_t));
+ if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (gidsetsize > 0) {
+ grouplist = g_try_new(gid_t, gidsetsize);
+ if (!grouplist) {
+ return -TARGET_ENOMEM;
+ }
+ }
ret = get_errno(getgroups(gidsetsize, grouplist));
- if (gidsetsize == 0)
- return ret;
- if (!is_error(ret)) {
- target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0);
+ if (!is_error(ret) && gidsetsize > 0) {
+ target_grouplist = lock_user(VERIFY_WRITE, arg2,
+ gidsetsize * 4, 0);
if (!target_grouplist) {
return -TARGET_EFAULT;
}
- for(i = 0;i < ret; i++)
+ for (i = 0; i < ret; i++) {
target_grouplist[i] = tswap32(grouplist[i]);
+ }
unlock_user(target_grouplist, arg2, gidsetsize * 4);
}
+ return ret;
}
- return ret;
#endif
#ifdef TARGET_NR_setgroups32
case TARGET_NR_setgroups32:
- {
+ { /* the same code as for TARGET_NR_setgroups */
int gidsetsize = arg1;
uint32_t *target_grouplist;
- gid_t *grouplist;
+ g_autofree gid_t *grouplist = NULL;
int i;
- grouplist = alloca(gidsetsize * sizeof(gid_t));
- target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1);
- if (!target_grouplist) {
- return -TARGET_EFAULT;
+ if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
+ return -TARGET_EINVAL;
+ }
+ if (gidsetsize > 0) {
+ grouplist = g_try_new(gid_t, gidsetsize);
+ if (!grouplist) {
+ return -TARGET_ENOMEM;
+ }
+ target_grouplist = lock_user(VERIFY_READ, arg2,
+ gidsetsize * 4, 1);
+ if (!target_grouplist) {
+ return -TARGET_EFAULT;
+ }
+ for (i = 0; i < gidsetsize; i++) {
+ grouplist[i] = tswap32(target_grouplist[i]);
+ }
+ unlock_user(target_grouplist, arg2, 0);
}
- for(i = 0;i < gidsetsize; i++)
- grouplist[i] = tswap32(target_grouplist[i]);
- unlock_user(target_grouplist, arg2, 0);
return get_errno(setgroups(gidsetsize, grouplist));
}
#endif
#ifdef TARGET_NR_mincore
case TARGET_NR_mincore:
{
- void *a = lock_user(VERIFY_READ, arg1, arg2, 0);
+ void *a = lock_user(VERIFY_NONE, arg1, arg2, 0);
if (!a) {
return -TARGET_ENOMEM;
}
case TARGET_NR_listxattr:
case TARGET_NR_llistxattr:
{
- void *p, *b = 0;
+ void *b = 0;
if (arg2) {
b = lock_user(VERIFY_WRITE, arg2, arg3, 0);
if (!b) {
case TARGET_NR_setxattr:
case TARGET_NR_lsetxattr:
{
- void *p, *n, *v = 0;
+ void *n, *v = 0;
if (arg3) {
v = lock_user(VERIFY_READ, arg3, arg4, 1);
if (!v) {
case TARGET_NR_getxattr:
case TARGET_NR_lgetxattr:
{
- void *p, *n, *v = 0;
+ void *n, *v = 0;
if (arg3) {
v = lock_user(VERIFY_WRITE, arg3, arg4, 0);
if (!v) {
case TARGET_NR_removexattr:
case TARGET_NR_lremovexattr:
{
- void *p, *n;
+ void *n;
p = lock_user_string(arg1);
n = lock_user_string(arg2);
if (p && n) {
return ret;
#endif
+#if defined(TARGET_NR_riscv_hwprobe)
+ case TARGET_NR_riscv_hwprobe:
+ return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5);
+#endif
+
default:
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
return -TARGET_ENOSYS;