Commit ae9108f8 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-4.1-pull-request' into staging



Update ppc64 feature and default CPU
next setsockops() options
Improve "-L" option
Another fix for 5.2-rc1 headers

# gpg: Signature made Wed 26 Jun 2019 13:11:04 BST
# gpg:                using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C
# gpg:                issuer "laurent@vivier.eu"
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full]
# gpg:                 aka "Laurent Vivier <laurent@vivier.eu>" [full]
# gpg:                 aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full]
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F  5173 F30C 38BD 3F2F BE3C

* remotes/vivier2/tags/linux-user-for-4.1-pull-request:
  linux-user: set default PPC64 CPU
  linux-user: update PPC64 HWCAP2 feature list
  linux-user: Add support for setsockopt() options IPV6_<ADD|DROP>_MEMBERSHIP
  linux-user: Add support for setsockopt() option SOL_ALG
  linux-user: emulate msgsnd(), msgrcv() and semtimedop()
  util/path: Do not cache all filenames at startup

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 8351ef7a b50d1e42
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -768,7 +768,13 @@ enum {
    QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */
    QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */
    QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */
    QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000,
    QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000,
    QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */
    QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */
    QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */
    QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */
    QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */
};

#define ELF_HWCAP get_elf_hwcap()
@@ -822,8 +828,10 @@ static uint32_t get_elf_hwcap2(void)
    GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL);
    GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR);
    GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
                  PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07);
    GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00);
                  PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 |
                  QEMU_PPC_FEATURE2_VEC_CRYPTO);
    GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 |
                 QEMU_PPC_FEATURE2_DARN);

#undef GET_FEATURE
#undef GET_FEATURE2
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@
static inline const char *cpu_get_model(uint32_t eflags)
{
#ifdef TARGET_PPC64
    return "POWER8";
    return "POWER9";
#else
    return "750";
#endif
+80 −31
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@
#include <linux/blkpg.h>
#include <netpacket/packet.h>
#include <linux/netlink.h>
#include <linux/if_alg.h>
#include "linux_loop.h"
#include "uname.h"

@@ -762,50 +763,21 @@ safe_syscall2(int, nanosleep, const struct timespec *, req,
safe_syscall4(int, clock_nanosleep, const clockid_t, clock, int, flags,
              const struct timespec *, req, struct timespec *, rem)
#endif
#if !defined(__NR_msgsnd) || !defined(__NR_msgrcv) || !defined(__NR_semtimedop)
/* This host kernel architecture uses a single ipc syscall; fake up
 * wrappers for the sub-operations to hide this implementation detail.
 * Annoyingly we can't include linux/ipc.h to get the constant definitions
 * for the call parameter because some structs in there conflict with the
 * sys/ipc.h ones. So we just define them here, and rely on them being
 * the same for all host architectures.
 */
#define Q_SEMTIMEDOP 4
#define Q_MSGSND 11
#define Q_MSGRCV 12
#define Q_IPCCALL(VERSION, OP) ((VERSION) << 16 | (OP))

#ifdef __NR_ipc
safe_syscall6(int, ipc, int, call, long, first, long, second, long, third,
              void *, ptr, long, fifth)
#endif
#ifdef __NR_msgsnd
safe_syscall4(int, msgsnd, int, msgid, const void *, msgp, size_t, sz,
              int, flags)
#else
static int safe_msgsnd(int msgid, const void *msgp, size_t sz, int flags)
{
    return safe_ipc(Q_IPCCALL(0, Q_MSGSND), msgid, sz, flags, (void *)msgp, 0);
}
#endif
#ifdef __NR_msgrcv
safe_syscall5(int, msgrcv, int, msgid, void *, msgp, size_t, sz,
              long, msgtype, int, flags)
#else
static int safe_msgrcv(int msgid, void *msgp, size_t sz, long type, int flags)
{
    return safe_ipc(Q_IPCCALL(1, Q_MSGRCV), msgid, sz, flags, msgp, type);
}
#endif
#ifdef __NR_semtimedop
safe_syscall4(int, semtimedop, int, semid, struct sembuf *, tsops,
              unsigned, nsops, const struct timespec *, timeout)
#else
static int safe_semtimedop(int semid, struct sembuf *tsops, unsigned nsops,
                           const struct timespec *timeout)
{
    return safe_ipc(Q_IPCCALL(0, Q_SEMTIMEDOP), semid, nsops, 0, tsops,
                    (long)timeout);
}
#endif
#if defined(TARGET_NR_mq_open) && defined(__NR_mq_open)
safe_syscall5(int, mq_timedsend, int, mqdes, const char *, msg_ptr,
@@ -1920,6 +1892,25 @@ static abi_long do_setsockopt(int sockfd, int level, int optname,
                                       &pki, sizeof(pki)));
            break;
        }
        case IPV6_ADD_MEMBERSHIP:
        case IPV6_DROP_MEMBERSHIP:
        {
            struct ipv6_mreq ipv6mreq;

            if (optlen < sizeof(ipv6mreq)) {
                return -TARGET_EINVAL;
            }

            if (copy_from_user(&ipv6mreq, optval_addr, sizeof(ipv6mreq))) {
                return -TARGET_EFAULT;
            }

            ipv6mreq.ipv6mr_interface = tswap32(ipv6mreq.ipv6mr_interface);

            ret = get_errno(setsockopt(sockfd, level, optname,
                                       &ipv6mreq, sizeof(ipv6mreq)));
            break;
        }
        default:
            goto unimplemented;
        }
@@ -1970,6 +1961,36 @@ static abi_long do_setsockopt(int sockfd, int level, int optname,
            goto unimplemented;
        }
        break;
#if defined(SOL_ALG) && defined(ALG_SET_KEY) && defined(ALG_SET_AEAD_AUTHSIZE)
    case SOL_ALG:
        switch (optname) {
        case ALG_SET_KEY:
        {
            char *alg_key = g_malloc(optlen);

            if (!alg_key) {
                return -TARGET_ENOMEM;
            }
            if (copy_from_user(alg_key, optval_addr, optlen)) {
                g_free(alg_key);
                return -TARGET_EFAULT;
            }
            ret = get_errno(setsockopt(sockfd, level, optname,
                                       alg_key, optlen));
            g_free(alg_key);
            break;
        }
        case ALG_SET_AEAD_AUTHSIZE:
        {
            ret = get_errno(setsockopt(sockfd, level, optname,
                                       NULL, optlen));
            break;
        }
        default:
            goto unimplemented;
        }
        break;
#endif
    case TARGET_SOL_SOCKET:
        switch (optname) {
        case TARGET_SO_RCVTIMEO:
@@ -3529,11 +3550,21 @@ static inline abi_long target_to_host_sembuf(struct sembuf *host_sembuf,
static inline abi_long do_semop(int semid, abi_long ptr, unsigned nsops)
{
    struct sembuf sops[nsops];
    abi_long ret;

    if (target_to_host_sembuf(sops, ptr, nsops))
        return -TARGET_EFAULT;

    return get_errno(safe_semtimedop(semid, sops, nsops, NULL));
    ret = -TARGET_ENOSYS;
#ifdef __NR_semtimedop
    ret = get_errno(safe_semtimedop(semid, sops, nsops, NULL));
#endif
#ifdef __NR_ipc
    if (ret == -TARGET_ENOSYS) {
        ret = get_errno(safe_ipc(IPCOP_semtimedop, semid, nsops, 0, sops, 0));
    }
#endif
    return ret;
}

struct target_msqid_ds
@@ -3688,7 +3719,16 @@ static inline abi_long do_msgsnd(int msqid, abi_long msgp,
    }
    host_mb->mtype = (abi_long) tswapal(target_mb->mtype);
    memcpy(host_mb->mtext, target_mb->mtext, msgsz);
    ret = -TARGET_ENOSYS;
#ifdef __NR_msgsnd
    ret = get_errno(safe_msgsnd(msqid, host_mb, msgsz, msgflg));
#endif
#ifdef __NR_ipc
    if (ret == -TARGET_ENOSYS) {
        ret = get_errno(safe_ipc(IPCOP_msgsnd, msqid, msgsz, msgflg,
                                 host_mb, 0));
    }
#endif
    g_free(host_mb);
    unlock_user_struct(target_mb, msgp, 0);

@@ -3716,7 +3756,16 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp,
        ret = -TARGET_ENOMEM;
        goto end;
    }
    ret = -TARGET_ENOSYS;
#ifdef __NR_msgrcv
    ret = get_errno(safe_msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg));
#endif
#ifdef __NR_ipc
    if (ret == -TARGET_ENOSYS) {
        ret = get_errno(safe_ipc(IPCOP_CALL(1, IPCOP_msgrcv), msqid, msgsz,
                        msgflg, host_mb, msgtyp));
    }
#endif

    if (ret > 0) {
        abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong);
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#define TARGET_SYS_RECVMMSG     19        /* recvmmsg()            */
#define TARGET_SYS_SENDMMSG     20        /* sendmmsg()            */

#define IPCOP_CALL(VERSION, OP) ((VERSION) << 16 | (OP))
#define IPCOP_semop		1
#define IPCOP_semget		2
#define IPCOP_semctl		3
+43 −150
Original line number Diff line number Diff line
@@ -8,170 +8,63 @@
#include <dirent.h>
#include "qemu/cutils.h"
#include "qemu/path.h"
#include "qemu/thread.h"

struct pathelem
{
    /* Name of this, eg. lib */
    char *name;
    /* Full path name, eg. /usr/gnemul/x86-linux/lib. */
    char *pathname;
    struct pathelem *parent;
    /* Children */
    unsigned int num_entries;
    struct pathelem *entries[0];
};

static struct pathelem *base;

/* First N chars of S1 match S2, and S2 is N chars long. */
static int strneq(const char *s1, unsigned int n, const char *s2)
{
    unsigned int i;

    for (i = 0; i < n; i++)
        if (s1[i] != s2[i])
            return 0;
    return s2[i] == 0;
}

static struct pathelem *add_entry(struct pathelem *root, const char *name,
                                  unsigned type);

static struct pathelem *new_entry(const char *root,
                                  struct pathelem *parent,
                                  const char *name)
{
    struct pathelem *new = g_malloc(sizeof(*new));
    new->name = g_strdup(name);
    new->pathname = g_strdup_printf("%s/%s", root, name);
    new->num_entries = 0;
    return new;
}

#define streq(a,b) (strcmp((a), (b)) == 0)

/* Not all systems provide this feature */
#if defined(DT_DIR) && defined(DT_UNKNOWN) && defined(DT_LNK)
# define dirent_type(dirent) ((dirent)->d_type)
# define is_dir_maybe(type) \
    ((type) == DT_DIR || (type) == DT_UNKNOWN || (type) == DT_LNK)
#else
# define dirent_type(dirent) (1)
# define is_dir_maybe(type)  (type)
#endif
static const char *base;
static GHashTable *hash;
static QemuMutex lock;

static struct pathelem *add_dir_maybe(struct pathelem *path)
{
    DIR *dir;

    if ((dir = opendir(path->pathname)) != NULL) {
        struct dirent *dirent;

        while ((dirent = readdir(dir)) != NULL) {
            if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
                path = add_entry(path, dirent->d_name, dirent_type(dirent));
            }
        }
        closedir(dir);
    }
    return path;
}

static struct pathelem *add_entry(struct pathelem *root, const char *name,
                                  unsigned type)
void init_paths(const char *prefix)
{
    struct pathelem **e;

    root->num_entries++;

    root = g_realloc(root, sizeof(*root)
                   + sizeof(root->entries[0])*root->num_entries);
    e = &root->entries[root->num_entries-1];

    *e = new_entry(root->pathname, root, name);
    if (is_dir_maybe(type)) {
        *e = add_dir_maybe(*e);
    if (prefix[0] == '\0' || !strcmp(prefix, "/")) {
        return;
    }

    return root;
    if (prefix[0] == '/') {
        base = g_strdup(prefix);
    } else {
        char *cwd = g_get_current_dir();
        base = g_build_filename(cwd, prefix, NULL);
        g_free(cwd);
    }

/* This needs to be done after tree is stabilized (ie. no more reallocs!). */
static void set_parents(struct pathelem *child, struct pathelem *parent)
{
    unsigned int i;

    child->parent = parent;
    for (i = 0; i < child->num_entries; i++)
        set_parents(child->entries[i], child);
    hash = g_hash_table_new(g_str_hash, g_str_equal);
    qemu_mutex_init(&lock);
}

/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
static const char *
follow_path(const struct pathelem *cursor, const char *name)
/* Look for path in emulation dir, otherwise return name. */
const char *path(const char *name)
{
    unsigned int i, namelen;
    gpointer key, value;
    const char *ret;

    name += strspn(name, "/");
    namelen = strcspn(name, "/");

    if (namelen == 0)
        return cursor->pathname;

    if (strneq(name, namelen, ".."))
        return follow_path(cursor->parent, name + namelen);

    if (strneq(name, namelen, "."))
        return follow_path(cursor, name + namelen);

    for (i = 0; i < cursor->num_entries; i++)
        if (strneq(name, namelen, cursor->entries[i]->name))
            return follow_path(cursor->entries[i], name + namelen);

    /* Not found */
    return NULL;
    /* Only do absolute paths: quick and dirty, but should mostly be OK.  */
    if (!base || !name || name[0] != '/') {
        return name;
    }

void init_paths(const char *prefix)
{
    char pref_buf[PATH_MAX];

    if (prefix[0] == '\0' ||
        !strcmp(prefix, "/"))
        return;

    if (prefix[0] != '/') {
        char *cwd = getcwd(NULL, 0);
        size_t pref_buf_len = sizeof(pref_buf);

        if (!cwd)
            abort();
        pstrcpy(pref_buf, sizeof(pref_buf), cwd);
        pstrcat(pref_buf, pref_buf_len, "/");
        pstrcat(pref_buf, pref_buf_len, prefix);
        free(cwd);
    } else
        pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1);
    qemu_mutex_lock(&lock);

    base = new_entry("", NULL, pref_buf);
    base = add_dir_maybe(base);
    if (base->num_entries == 0) {
        g_free(base->pathname);
        g_free(base->name);
        g_free(base);
        base = NULL;
    /* Have we looked up this file before?  */
    if (g_hash_table_lookup_extended(hash, name, &key, &value)) {
        ret = value ? value : name;
    } else {
        set_parents(base, base);
        char *save = g_strdup(name);
        char *full = g_build_filename(base, name, NULL);

        /* Look for the path; record the result, pass or fail.  */
        if (access(full, F_OK) == 0) {
            /* Exists.  */
            g_hash_table_insert(hash, save, full);
            ret = full;
        } else {
            /* Does not exist.  */
            g_free(full);
            g_hash_table_insert(hash, save, NULL);
            ret = name;
        }
    }

/* Look for path in emulation dir, otherwise return name. */
const char *path(const char *name)
{
    /* Only do absolute paths: quick and dirty, but should mostly be OK.
       Could do relative by tracking cwd. */
    if (!base || !name || name[0] != '/')
        return name;

    return follow_path(base, name) ?: name;
    qemu_mutex_unlock(&lock);
    return ret;
}