QEMU main repository: Please see https://www.qemu.org/docs/master/devel/submitting-a-patch.html for how to submit changes to QEMU. Pull Requests are ignored. Please only use release tarballs from the QEMU website. http://www.qemu.org
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
8.4 KiB
366 lines
8.4 KiB
/*
|
|
* miscellaneous BSD system call shims
|
|
*
|
|
* Copyright (c) 2013 Stacey D. Son
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#ifndef BSD_MISC_H
|
|
#define BSD_MISC_H
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/msg.h>
|
|
#include <sys/sem.h>
|
|
#include <sys/uuid.h>
|
|
|
|
#include "qemu-bsd.h"
|
|
|
|
static int bsd_msgmax;
|
|
|
|
/* quotactl(2) */
|
|
static inline abi_long do_bsd_quotactl(abi_ulong path, abi_long cmd,
|
|
__unused abi_ulong target_addr)
|
|
{
|
|
qemu_log("qemu: Unsupported syscall quotactl()\n");
|
|
return -TARGET_ENOSYS;
|
|
}
|
|
|
|
/* reboot(2) */
|
|
static inline abi_long do_bsd_reboot(abi_long how)
|
|
{
|
|
qemu_log("qemu: Unsupported syscall reboot()\n");
|
|
return -TARGET_ENOSYS;
|
|
}
|
|
|
|
/* uuidgen(2) */
|
|
static inline abi_long do_bsd_uuidgen(abi_ulong target_addr, int count)
|
|
{
|
|
int i;
|
|
abi_long ret;
|
|
g_autofree struct uuid *host_uuid = NULL;
|
|
|
|
/*
|
|
* 2048 is the kernel limit, but there's no #define for it, nor any sysctl
|
|
* to query it.
|
|
*/
|
|
if (count < 1 || count > 2048) {
|
|
return -TARGET_EINVAL;
|
|
}
|
|
|
|
host_uuid = g_malloc(count * sizeof(struct uuid));
|
|
|
|
ret = get_errno(uuidgen(host_uuid, count));
|
|
if (is_error(ret)) {
|
|
goto out;
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
ret = host_to_target_uuid(target_addr +
|
|
(abi_ulong)(sizeof(struct target_uuid) * i), &host_uuid[i]);
|
|
if (is_error(ret)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* System V Semaphores
|
|
*/
|
|
|
|
/* semget(2) */
|
|
static inline abi_long do_bsd_semget(abi_long key, int nsems,
|
|
int target_flags)
|
|
{
|
|
return get_errno(semget(key, nsems,
|
|
target_to_host_bitmask(target_flags, ipc_flags_tbl)));
|
|
}
|
|
|
|
/* semop(2) */
|
|
static inline abi_long do_bsd_semop(int semid, abi_long ptr, unsigned nsops)
|
|
{
|
|
g_autofree struct sembuf *sops = g_malloc(nsops * sizeof(struct sembuf));
|
|
struct target_sembuf *target_sembuf;
|
|
int i;
|
|
|
|
target_sembuf = lock_user(VERIFY_READ, ptr,
|
|
nsops * sizeof(struct target_sembuf), 1);
|
|
if (target_sembuf == NULL) {
|
|
return -TARGET_EFAULT;
|
|
}
|
|
for (i = 0; i < nsops; i++) {
|
|
__get_user(sops[i].sem_num, &target_sembuf[i].sem_num);
|
|
__get_user(sops[i].sem_op, &target_sembuf[i].sem_op);
|
|
__get_user(sops[i].sem_flg, &target_sembuf[i].sem_flg);
|
|
}
|
|
unlock_user(target_sembuf, ptr, 0);
|
|
|
|
return semop(semid, sops, nsops);
|
|
}
|
|
|
|
/* __semctl(2) */
|
|
static inline abi_long do_bsd___semctl(int semid, int semnum, int target_cmd,
|
|
abi_ptr un_ptr)
|
|
{
|
|
void *target_un;
|
|
union semun arg;
|
|
struct semid_ds dsarg;
|
|
unsigned short *array = NULL;
|
|
int host_cmd;
|
|
abi_long ret = 0;
|
|
abi_ulong target_array, target_buffer;
|
|
|
|
switch (target_cmd) {
|
|
case TARGET_GETVAL:
|
|
host_cmd = GETVAL;
|
|
break;
|
|
|
|
case TARGET_SETVAL:
|
|
host_cmd = SETVAL;
|
|
break;
|
|
|
|
case TARGET_GETALL:
|
|
host_cmd = GETALL;
|
|
break;
|
|
|
|
case TARGET_SETALL:
|
|
host_cmd = SETALL;
|
|
break;
|
|
|
|
case TARGET_IPC_STAT:
|
|
host_cmd = IPC_STAT;
|
|
break;
|
|
|
|
case TARGET_IPC_SET:
|
|
host_cmd = IPC_SET;
|
|
break;
|
|
|
|
case TARGET_IPC_RMID:
|
|
host_cmd = IPC_RMID;
|
|
break;
|
|
|
|
case TARGET_GETPID:
|
|
host_cmd = GETPID;
|
|
break;
|
|
|
|
case TARGET_GETNCNT:
|
|
host_cmd = GETNCNT;
|
|
break;
|
|
|
|
case TARGET_GETZCNT:
|
|
host_cmd = GETZCNT;
|
|
break;
|
|
|
|
default:
|
|
return -TARGET_EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Unlike Linux and the semctl system call, we take a pointer
|
|
* to the union arg here.
|
|
*/
|
|
target_un = lock_user(VERIFY_READ, un_ptr, sizeof(union target_semun), 1);
|
|
|
|
switch (host_cmd) {
|
|
case GETVAL:
|
|
case SETVAL:
|
|
__get_user(arg.val, (abi_int *)target_un);
|
|
ret = get_errno(semctl(semid, semnum, host_cmd, arg));
|
|
break;
|
|
|
|
case GETALL:
|
|
case SETALL:
|
|
__get_user(target_array, (abi_ulong *)target_un);
|
|
ret = target_to_host_semarray(semid, &array, target_array);
|
|
if (is_error(ret)) {
|
|
goto out;
|
|
}
|
|
arg.array = array;
|
|
ret = get_errno(semctl(semid, semnum, host_cmd, arg));
|
|
if (!is_error(ret)) {
|
|
ret = host_to_target_semarray(semid, target_array, &array);
|
|
}
|
|
break;
|
|
|
|
case IPC_STAT:
|
|
case IPC_SET:
|
|
__get_user(target_buffer, (abi_ulong *)target_un);
|
|
ret = target_to_host_semid_ds(&dsarg, target_buffer);
|
|
if (is_error(ret)) {
|
|
goto out;
|
|
}
|
|
arg.buf = &dsarg;
|
|
ret = get_errno(semctl(semid, semnum, host_cmd, arg));
|
|
if (!is_error(ret)) {
|
|
ret = host_to_target_semid_ds(target_buffer, &dsarg);
|
|
}
|
|
break;
|
|
|
|
case IPC_RMID:
|
|
case GETPID:
|
|
case GETNCNT:
|
|
case GETZCNT:
|
|
ret = get_errno(semctl(semid, semnum, host_cmd, NULL));
|
|
break;
|
|
|
|
default:
|
|
ret = -TARGET_EINVAL;
|
|
break;
|
|
}
|
|
out:
|
|
unlock_user(target_un, un_ptr, 1);
|
|
return ret;
|
|
}
|
|
|
|
/* msgctl(2) */
|
|
static inline abi_long do_bsd_msgctl(int msgid, int target_cmd, abi_long ptr)
|
|
{
|
|
struct msqid_ds dsarg;
|
|
abi_long ret = -TARGET_EINVAL;
|
|
int host_cmd;
|
|
|
|
switch (target_cmd) {
|
|
case TARGET_IPC_STAT:
|
|
host_cmd = IPC_STAT;
|
|
break;
|
|
|
|
case TARGET_IPC_SET:
|
|
host_cmd = IPC_SET;
|
|
break;
|
|
|
|
case TARGET_IPC_RMID:
|
|
host_cmd = IPC_RMID;
|
|
break;
|
|
|
|
default:
|
|
return -TARGET_EINVAL;
|
|
}
|
|
|
|
switch (host_cmd) {
|
|
case IPC_STAT:
|
|
case IPC_SET:
|
|
if (target_to_host_msqid_ds(&dsarg, ptr)) {
|
|
return -TARGET_EFAULT;
|
|
}
|
|
ret = get_errno(msgctl(msgid, host_cmd, &dsarg));
|
|
if (host_to_target_msqid_ds(ptr, &dsarg)) {
|
|
return -TARGET_EFAULT;
|
|
}
|
|
break;
|
|
|
|
case IPC_RMID:
|
|
ret = get_errno(msgctl(msgid, host_cmd, NULL));
|
|
break;
|
|
|
|
default:
|
|
ret = -TARGET_EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
struct kern_mymsg {
|
|
long mtype;
|
|
char mtext[1];
|
|
};
|
|
|
|
static inline abi_long bsd_validate_msgsz(abi_ulong msgsz)
|
|
{
|
|
/* Fetch msgmax the first time we need it. */
|
|
if (bsd_msgmax == 0) {
|
|
size_t len = sizeof(bsd_msgmax);
|
|
|
|
if (sysctlbyname("kern.ipc.msgmax", &bsd_msgmax, &len, NULL, 0) == -1) {
|
|
return -TARGET_EINVAL;
|
|
}
|
|
}
|
|
|
|
if (msgsz > bsd_msgmax) {
|
|
return -TARGET_EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* msgsnd(2) */
|
|
static inline abi_long do_bsd_msgsnd(int msqid, abi_long msgp,
|
|
abi_ulong msgsz, int msgflg)
|
|
{
|
|
struct target_msgbuf *target_mb;
|
|
struct kern_mymsg *host_mb;
|
|
abi_long ret;
|
|
|
|
ret = bsd_validate_msgsz(msgsz);
|
|
if (is_error(ret)) {
|
|
return ret;
|
|
}
|
|
if (!lock_user_struct(VERIFY_READ, target_mb, msgp, 0)) {
|
|
return -TARGET_EFAULT;
|
|
}
|
|
host_mb = g_malloc(msgsz + sizeof(long));
|
|
host_mb->mtype = (abi_long) tswapal(target_mb->mtype);
|
|
memcpy(host_mb->mtext, target_mb->mtext, msgsz);
|
|
ret = get_errno(msgsnd(msqid, host_mb, msgsz, msgflg));
|
|
g_free(host_mb);
|
|
unlock_user_struct(target_mb, msgp, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* msgget(2) */
|
|
static inline abi_long do_bsd_msgget(abi_long key, abi_long msgflag)
|
|
{
|
|
abi_long ret;
|
|
|
|
ret = get_errno(msgget(key, msgflag));
|
|
return ret;
|
|
}
|
|
|
|
/* msgrcv(2) */
|
|
static inline abi_long do_bsd_msgrcv(int msqid, abi_long msgp,
|
|
abi_ulong msgsz, abi_long msgtyp, int msgflg)
|
|
{
|
|
struct target_msgbuf *target_mb = NULL;
|
|
char *target_mtext;
|
|
struct kern_mymsg *host_mb;
|
|
abi_long ret = 0;
|
|
|
|
ret = bsd_validate_msgsz(msgsz);
|
|
if (is_error(ret)) {
|
|
return ret;
|
|
}
|
|
if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) {
|
|
return -TARGET_EFAULT;
|
|
}
|
|
host_mb = g_malloc(msgsz + sizeof(long));
|
|
ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapal(msgtyp), msgflg));
|
|
if (ret > 0) {
|
|
abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong);
|
|
target_mtext = lock_user(VERIFY_WRITE, target_mtext_addr, ret, 0);
|
|
if (target_mtext == NULL) {
|
|
ret = -TARGET_EFAULT;
|
|
goto end;
|
|
}
|
|
memcpy(target_mb->mtext, host_mb->mtext, ret);
|
|
unlock_user(target_mtext, target_mtext_addr, ret);
|
|
}
|
|
if (!is_error(ret)) {
|
|
target_mb->mtype = tswapal(host_mb->mtype);
|
|
}
|
|
end:
|
|
if (target_mb != NULL) {
|
|
unlock_user_struct(target_mb, msgp, 1);
|
|
}
|
|
g_free(host_mb);
|
|
return ret;
|
|
}
|
|
|
|
/* getdtablesize(2) */
|
|
static inline abi_long do_bsd_getdtablesize(void)
|
|
{
|
|
return get_errno(getdtablesize());
|
|
}
|
|
|
|
#endif /* BSD_MISC_H */
|
|
|