Skip to content

Commit

Permalink
linux-user: Move shmat and shmdt implementations to mmap.c
Browse files Browse the repository at this point in the history
Rename from do_* to target_*.  Fix some minor checkpatch errors.

Tested-by: Helge Deller <deller@gmx.de>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Warner Losh <imp@bsdimp.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
  • Loading branch information
rth7680 committed Sep 1, 2023
1 parent f6d4554 commit 225a206
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 139 deletions.
138 changes: 138 additions & 0 deletions linux-user/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include <sys/shm.h>
#include "trace.h"
#include "exec/log.h"
#include "qemu.h"
Expand All @@ -27,6 +28,14 @@
static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
static __thread int mmap_lock_count;

#define N_SHM_REGIONS 32

static struct shm_region {
abi_ulong start;
abi_ulong size;
bool in_use;
} shm_regions[N_SHM_REGIONS];

void mmap_lock(void)
{
if (mmap_lock_count++ == 0) {
Expand Down Expand Up @@ -981,3 +990,132 @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice)

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

abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
abi_ulong shmaddr, int shmflg)
{
CPUState *cpu = env_cpu(cpu_env);
abi_ulong 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((intptr_t)host_raddr);
}
raddr = h2g((uintptr_t)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;
}

abi_long target_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;
}
143 changes: 4 additions & 139 deletions linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -3725,14 +3725,6 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
}
#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
Expand Down Expand Up @@ -4482,133 +4474,6 @@ static inline abi_long do_shmctl(int shmid, int cmd, abi_long buf)
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 abi_ulong do_shmat(CPUArchState *cpu_env, int shmid,
abi_ulong shmaddr, int shmflg)
{
CPUState *cpu = env_cpu(cpu_env);
abi_ulong 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((intptr_t)host_raddr);
}
raddr = h2g((uintptr_t)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. */
Expand Down Expand Up @@ -4695,7 +4560,7 @@ static abi_long do_ipc(CPUArchState *cpu_env,
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))
Expand All @@ -4708,7 +4573,7 @@ static abi_long do_ipc(CPUArchState *cpu_env,
}
break;
case IPCOP_shmdt:
ret = do_shmdt(ptr);
ret = target_shmdt(ptr);
break;

case IPCOP_shmget:
Expand Down Expand Up @@ -11008,11 +10873,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long 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));
Expand Down
4 changes: 4 additions & 0 deletions linux-user/user-mmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong);
void mmap_fork_start(void);
void mmap_fork_end(int child);

abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
abi_ulong shmaddr, int shmflg);
abi_long target_shmdt(abi_ulong shmaddr);

#endif /* LINUX_USER_USER_MMAP_H */

0 comments on commit 225a206

Please sign in to comment.