Commit 0e97e2a2 authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[PATCH] x86-64 IA32 emulation updates

Some bugfixes for the 32bit emulation for x86-64 and make it all compile
again.

Has rewritten ioctl registration code and some other updates.  New
system calls are supported.

It unfortunately broke some time ago by binfmt_elf changes for all
shared linked libraries.  Will fix that later.
parent fb06dbb1
......@@ -2,17 +2,10 @@
# Makefile for the ia32 kernel emulation subsystem.
#
USE_STANDARD_AS_RULE := true
export-objs := ia32_ioctl.o sys_ia32.o
all: ia32.o
O_TARGET := ia32.o
obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_ioctl.o \
ia32_signal.o \
ia32_binfmt.o fpu32.o socket32.o ptrace32.o ipc32.o
clean::
include $(TOPDIR)/Rules.make
......@@ -58,6 +58,8 @@ struct timeval32
int tv_sec, tv_usec;
};
#define jiffies_to_timeval(a,b) do { (b)->tv_usec = 0; (b)->tv_sec = (a)/HZ; }while(0)
struct elf_prstatus
{
struct elf_siginfo pr_info; /* Info associated with signal */
......@@ -162,6 +164,7 @@ do { \
#define ELF_PLAT_INIT(r) elf32_init(r)
#define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm)
int ia32_setup_arg_pages(struct linux_binprm *bprm);
#undef start_thread
#define start_thread(regs,new_rip,new_rsp) do { \
......
......@@ -55,6 +55,7 @@
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/reiserfs_fs.h>
#include <linux/dirent.h>
#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
/* Ugh. This header really is not clean */
#define min min
......@@ -2000,6 +2001,7 @@ static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg)
#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */
/* AK: dev_t/kdev_t use here is somewhat dubious */
typedef struct {
uint8_t vg_name[NAME_LEN];
uint32_t vg_number;
......@@ -2034,7 +2036,7 @@ typedef struct {
uint8_t pv_name[NAME_LEN];
uint8_t vg_name[NAME_LEN];
uint8_t system_id[NAME_LEN];
kdev_t pv_dev;
__u32 pv_dev;
uint32_t pv_number;
uint32_t pv_status;
uint32_t pv_allocatable;
......@@ -3084,38 +3086,108 @@ static int serial_struct_ioctl(unsigned fd, unsigned cmd, void *ptr)
return err;
}
static int generic_long_put(unsigned int fd, unsigned int cmd, unsigned long arg)
struct dirent32 {
unsigned int d_ino;
__kernel_off_t32 d_off;
unsigned short d_reclen;
char d_name[256]; /* We must not include limits.h! */
};
#define VFAT_IOCTL_READDIR_BOTH32 _IOR('r', 1, struct dirent32 [2])
#define VFAT_IOCTL_READDIR_SHORT32 _IOR('r', 2, struct dirent32 [2])
static int put_dirent32(struct dirent *src, struct dirent32 *dst)
{
int ret;
unsigned long val = 0;
mm_segment_t oldseg = get_fs();
ret = put_user(src->d_ino, &dst->d_ino);
ret |= __put_user(src->d_off, &dst->d_off);
ret |= __put_user(src->d_reclen, &dst->d_reclen);
if (__copy_to_user(&dst->d_name, src->d_name, src->d_reclen))
ret |= -EFAULT;
return ret;
}
static int vfat_ioctl32(unsigned fd, unsigned cmd, void *ptr)
{
int ret;
mm_segment_t oldfs = get_fs();
struct dirent d[2];
set_fs(KERNEL_DS);
cmd = (cmd & 0xc000ffff) | (sizeof(unsigned long) << _IOC_SIZESHIFT);
ret = sys_ioctl(fd, cmd, (unsigned long)&val);
set_fs(oldseg);
if (!ret || val) {
if (put_user((int)val, (unsigned int *)arg))
return -EFAULT;
ret = sys_ioctl(fd,cmd,(unsigned long)&d);
set_fs(oldfs);
if (!ret) {
ret |= put_dirent32(&d[0], (struct dirent32 *)ptr);
ret |= put_dirent32(&d[1], ((struct dirent32 *)ptr) + 1);
}
return ret;
}
static int generic_long_get(unsigned int fd, unsigned int cmd, unsigned long arg)
#define REISERFS_IOC_UNPACK32 _IOW(0xCD,1,int)
static int reiserfs_ioctl32(unsigned fd, unsigned cmd, unsigned long ptr)
{
if (cmd == REISERFS_IOC_UNPACK32)
cmd = REISERFS_IOC_UNPACK;
return sys_ioctl(fd,cmd,ptr);
}
#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int)
static int autofs_ioctl32(unsigned fd, unsigned cmd, unsigned long ptr)
{
int ret;
unsigned int ival;
unsigned long val = 0;
mm_segment_t oldseg = get_fs();
if (get_user(ival, (unsigned int *)arg))
return -EFAULT;
val = ival;
unsigned long val;
mm_segment_t oldfs = get_fs();
set_fs(KERNEL_DS);
cmd = (cmd & 0xc000ffff) | (sizeof(unsigned long) << _IOC_SIZESHIFT);
ret = sys_ioctl(fd, cmd, (unsigned long)&val);
set_fs(oldseg);
ret = sys_ioctl(fd, AUTOFS_IOC_SETTIMEOUT32, (unsigned long)&val);
set_fs(oldfs);
if (!ret)
ret = put_user(val, (int *) ptr);
return ret;
}
#define RTC_IRQP_READ32 _IOR('p', 0x0b, unsigned int) /* Read IRQ rate */
#define RTC_IRQP_SET32 _IOW('p', 0x0c, unsigned int) /* Set IRQ rate */
#define RTC_EPOCH_READ32 _IOR('p', 0x0d, unsigned) /* Read epoch */
#define RTC_EPOCH_SET32 _IOW('p', 0x0e, unsigned) /* Set epoch */
static int rtc32_ioctl(unsigned fd, unsigned cmd, unsigned long arg)
{
unsigned long val;
mm_segment_t oldfs = get_fs();
int ret;
switch (cmd) {
case RTC_IRQP_READ32:
set_fs(KERNEL_DS);
ret = sys_ioctl(fd, RTC_IRQP_READ, (unsigned long)&val);
set_fs(oldfs);
if (!ret)
ret = put_user(val, (unsigned int*) arg);
return ret;
case RTC_IRQP_SET32:
cmd = RTC_EPOCH_SET;
break;
case RTC_EPOCH_READ32:
set_fs(KERNEL_DS);
ret = sys_ioctl(fd, RTC_EPOCH_READ, (unsigned long) &val);
set_fs(oldfs);
if (!ret)
ret = put_user(val, (unsigned int*) arg);
return ret;
case RTC_EPOCH_SET32:
cmd = RTC_EPOCH_SET;
break;
}
return sys_ioctl(fd,cmd,arg);
}
struct ioctl_trans {
unsigned long cmd;
......@@ -3344,14 +3416,10 @@ COMPATIBLE_IOCTL(RTC_RD_TIME)
COMPATIBLE_IOCTL(RTC_SET_TIME)
COMPATIBLE_IOCTL(RTC_WKALM_SET)
COMPATIBLE_IOCTL(RTC_WKALM_RD)
#define RTC_IRQP_READ32 _IOR('p', 0x0b, unsigned int) /* Read IRQ rate */
HANDLE_IOCTL(RTC_IRQP_READ32,generic_long_put)
#define RTC_IRQP_SET32 _IOW('p', 0x0c, unsigned int) /* Set IRQ rate */
HANDLE_IOCTL(RTC_IRQP_SET32,generic_long_get)
#define RTC_EPOCH_READ32 _IOR('p', 0x0d, unsigned long) /* Read epoch */
#define RTC_EPOCH_SET32 _IOW('p', 0x0e, unsigned long) /* Set epoch */
HANDLE_IOCTL(RTC_EPOCH_READ32, generic_long_put)
HANDLE_IOCTL(RTC_EPOCH_SET32, generic_long_get)
HANDLE_IOCTL(RTC_IRQP_READ32,rtc32_ioctl)
HANDLE_IOCTL(RTC_IRQP_SET32,rtc32_ioctl)
HANDLE_IOCTL(RTC_EPOCH_READ32, rtc32_ioctl)
HANDLE_IOCTL(RTC_EPOCH_SET32, rtc32_ioctl)
/* Little m */
COMPATIBLE_IOCTL(MTIOCTOP)
/* Socket level stuff */
......@@ -3619,8 +3687,7 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL)
COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int)
HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, generic_long_get);
HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, autofs_ioctl32);
/* DEVFS */
COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)
COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
......@@ -3687,7 +3754,9 @@ COMPATIBLE_IOCTL(DRM_IOCTL_LOCK)
COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK)
COMPATIBLE_IOCTL(DRM_IOCTL_FINISH)
#endif /* DRM */
COMPATIBLE_IOCTL(REISERFS_IOC_UNPACK);
HANDLE_IOCTL(REISERFS_IOC_UNPACK32, reiserfs_ioctl32);
HANDLE_IOCTL(VFAT_IOCTL_READDIR_BOTH32, vfat_ioctl32);
HANDLE_IOCTL(VFAT_IOCTL_READDIR_SHORT32, vfat_ioctl32);
/* serial driver */
HANDLE_IOCTL(TIOCGSERIAL, serial_struct_ioctl);
HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl);
......@@ -3854,6 +3923,8 @@ IOCTL_TABLE_END
#define IOCTL_HASHSIZE 256
struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
extern struct ioctl_trans ioctl_start[], ioctl_end[];
static inline unsigned long ioctl32_hash(unsigned long cmd)
{
return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
......@@ -3879,7 +3950,6 @@ static void ioctl32_insert_translation(struct ioctl_trans *trans)
static int __init init_sys32_ioctl(void)
{
int i;
extern struct ioctl_trans ioctl_start[], ioctl_end[];
for (i = 0; &ioctl_start[i] < &ioctl_end[0]; i++) {
if (ioctl_start[i].next != 0) {
......@@ -3894,57 +3964,108 @@ static int __init init_sys32_ioctl(void)
__initcall(init_sys32_ioctl);
static struct ioctl_trans *additional_ioctls;
/* Always call these with kernel lock held! */
static struct ioctl_trans *ioctl_free_list;
/* Never free them really. This avoids SMP races. With a Read-Copy-Update
enabled kernel we could just use the RCU infrastructure for this. */
static void free_ioctl(struct ioctl_trans *t)
{
t->cmd = 0;
mb();
t->next = ioctl_free_list;
ioctl_free_list = t;
}
int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
{
int i;
if (!additional_ioctls) {
additional_ioctls = (struct ioctl_trans *)get_zeroed_page(GFP_KERNEL);
if (!additional_ioctls)
return -ENOMEM;
struct ioctl_trans *t;
unsigned long hash = ioctl32_hash(cmd);
lock_kernel();
for (t = (struct ioctl_trans *)ioctl32_hash_table[hash];
t;
t = t->next) {
if (t->cmd == cmd) {
printk("Trying to register duplicated ioctl32 handler %x\n", cmd);
unlock_kernel();
return -EINVAL;
}
for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++)
if (!additional_ioctls[i].cmd)
break;
if (i == PAGE_SIZE/sizeof(struct ioctl_trans))
}
if (ioctl_free_list) {
t = ioctl_free_list;
ioctl_free_list = t->next;
} else {
t = kmalloc(sizeof(struct ioctl_trans), GFP_KERNEL);
if (!t) {
unlock_kernel();
return -ENOMEM;
additional_ioctls[i].cmd = cmd;
if (!handler)
additional_ioctls[i].handler =
(int (*)(unsigned,unsigned,unsigned long, struct file *))sys_ioctl;
else
additional_ioctls[i].handler = handler;
ioctl32_insert_translation(&additional_ioctls[i]);
}
}
t->next = NULL;
t->cmd = cmd;
t->handler = handler;
ioctl32_insert_translation(t);
unlock_kernel();
return 0;
}
static inline int builtin_ioctl(struct ioctl_trans *t)
{
return t >= (struct ioctl_trans *)ioctl_start &&
t < (struct ioctl_trans *)ioctl_end;
}
/* Problem:
This function cannot unregister duplicate ioctls, because they are not
unique.
When they happen we need to extend the prototype to pass handler too. */
int unregister_ioctl32_conversion(unsigned int cmd)
{
unsigned long hash = ioctl32_hash(cmd);
struct ioctl_trans *t, *t1;
t = (struct ioctl_trans *)(long)ioctl32_hash_table[hash];
if (!t) return -EINVAL;
if (t->cmd == cmd && t >= additional_ioctls &&
(unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) {
lock_kernel();
t = (struct ioctl_trans *)ioctl32_hash_table[hash];
if (!t) {
unlock_kernel();
return -EINVAL;
}
if (t->cmd == cmd) {
if (builtin_ioctl(t)) {
printk("%p tried to unregister builtin ioctl %x\n",
__builtin_return_address(0), cmd);
} else {
ioctl32_hash_table[hash] = t->next;
t->cmd = 0;
free_ioctl(t);
unlock_kernel();
return 0;
} else while (t->next) {
}
}
while (t->next) {
t1 = (struct ioctl_trans *)(long)t->next;
if (t1->cmd == cmd && t1 >= additional_ioctls &&
(unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) {
t1->cmd = 0;
if (t1->cmd == cmd) {
if (builtin_ioctl(t1)) {
printk("%p tried to unregister builtin ioctl %x\n",
__builtin_return_address(0), cmd);
goto out;
} else {
t->next = t1->next;
free_ioctl(t1);
unlock_kernel();
return 0;
}
}
t = t1;
}
printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd);
out:
unlock_kernel();
return -EINVAL;
}
......@@ -3967,10 +4088,10 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
goto out;
}
t = (struct ioctl_trans *)(long)ioctl32_hash_table [ioctl32_hash (cmd)];
t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)];
while (t && t->cmd != cmd)
t = (struct ioctl_trans *)(long)t->next;
t = (struct ioctl_trans *)t->next;
if (t) {
handler = t->handler;
error = handler(fd, cmd, arg, filp);
......
......@@ -81,11 +81,11 @@ sys32_sigsuspend(int history0, int history1, old_sigset_t mask, struct pt_regs r
sigset_t saveset;
mask &= _BLOCKABLE;
spin_lock_irq(&current->sigmask_lock);
spin_lock_irq(&current->sig->siglock);
saveset = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sigmask_lock);
spin_unlock_irq(&current->sig->siglock);
regs.rax = -EINTR;
while (1) {
......@@ -170,10 +170,7 @@ ia32_restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsign
asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \
if (pre != cur) loadsegment(seg,pre); }
/* Reload fs and gs if they have changed in the signal handler.
This does not handle long fs/gs base changes in the handler, but does not clobber
them at least in the normal case. */
/* Reload fs and gs if they have changed in the signal handler. */
{
unsigned short gs;
......@@ -181,11 +178,18 @@ ia32_restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsign
load_gs_index(gs);
}
RELOAD_SEG(fs);
RELOAD_SEG(ds);
RELOAD_SEG(es);
COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
COPY(dx); COPY(cx); COPY(ip);
/* Don't touch extended registers */
err |= __get_user(regs->cs, &sc->cs);
regs->cs |= 2;
err |= __get_user(regs->ss, &sc->ss);
regs->ss |= 2;
{
unsigned int tmpflags;
err |= __get_user(tmpflags, &sc->eflags);
......@@ -231,10 +235,10 @@ asmlinkage int sys32_sigreturn(struct pt_regs regs)
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sigmask_lock);
spin_lock_irq(&current->sig->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sigmask_lock);
spin_unlock_irq(&current->sig->siglock);
if (ia32_restore_sigcontext(&regs, &frame->sc, &eax))
goto badframe;
......@@ -258,10 +262,10 @@ asmlinkage int sys32_rt_sigreturn(struct pt_regs regs)
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sigmask_lock);
spin_lock_irq(&current->sig->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sigmask_lock);
spin_unlock_irq(&current->sig->siglock);
if (ia32_restore_sigcontext(&regs, &frame->uc.uc_mcontext, &eax))
goto badframe;
......@@ -299,6 +303,10 @@ ia32_setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate,
err |= __put_user(tmp, (unsigned int *)&sc->gs);
__asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp));
err |= __put_user(tmp, (unsigned int *)&sc->fs);
__asm__("movl %%ds,%0" : "=r"(tmp): "0"(tmp));
err |= __put_user(tmp, (unsigned int *)&sc->ds);
__asm__("movl %%es,%0" : "=r"(tmp): "0"(tmp));
err |= __put_user(tmp, (unsigned int *)&sc->es);
err |= __put_user((u32)regs->rdi, &sc->edi);
err |= __put_user((u32)regs->rsi, &sc->esi);
......@@ -308,6 +316,8 @@ ia32_setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate,
err |= __put_user((u32)regs->rdx, &sc->edx);
err |= __put_user((u32)regs->rcx, &sc->ecx);
err |= __put_user((u32)regs->rax, &sc->eax);
err |= __put_user((u32)regs->cs, &sc->cs);
err |= __put_user((u32)regs->ss, &sc->ss);
err |= __put_user(current->thread.trap_no, &sc->trapno);
err |= __put_user(current->thread.error_code, &sc->err);
err |= __put_user((u32)regs->rip, &sc->eip);
......@@ -406,8 +416,13 @@ void ia32_setup_frame(int sig, struct k_sigaction *ka,
regs->rsp = (unsigned long) frame;
regs->rip = (unsigned long) ka->sa.sa_handler;
asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
regs->cs = __USER32_CS;
regs->ss = __USER32_DS;
set_fs(USER_DS);
// XXX: cs
regs->eflags &= ~TF_MASK;
#if DEBUG_SIG
......@@ -479,8 +494,13 @@ void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->rsp = (unsigned long) frame;
regs->rip = (unsigned long) ka->sa.sa_handler;
asm volatile("movl %0,%%ds" :: "r" (__USER32_DS));
asm volatile("movl %0,%%es" :: "r" (__USER32_DS));
regs->cs = __USER32_CS;
regs->ss = __USER32_DS;
set_fs(USER_DS);
// XXX: cs
regs->eflags &= ~TF_MASK;
#if DEBUG_SIG
......
......@@ -171,7 +171,7 @@ ia32_sys_call_table:
.quad sys_umount /* new_umount */
.quad ni_syscall /* old lock syscall holder */
.quad sys32_ioctl
.quad sys32_fcntl /* 55 */
.quad sys32_fcntl64 /* 55 */
.quad ni_syscall /* old mpx syscall holder */
.quad sys_setpgid
.quad ni_syscall /* old ulimit syscall holder */
......@@ -319,15 +319,15 @@ ia32_sys_call_table:
.quad sys_getgid /* 200 */
.quad sys_geteuid
.quad sys_getegid
.quad sys32_setreuid
.quad sys32_setregid
.quad sys32_getgroups /* 205 */
.quad sys32_setgroups
.quad sys_setreuid
.quad sys_setregid
.quad sys_getgroups /* 205 */
.quad sys_setgroups
.quad sys_fchown
.quad sys32_setresuid
.quad sys32_getresuid
.quad sys32_setresgid /* 210 */
.quad sys32_getresgid
.quad sys_setresuid
.quad sys_getresuid
.quad sys_setresgid /* 210 */
.quad sys_getresgid
.quad sys_chown
.quad sys_setuid
.quad sys_setgid
......@@ -356,9 +356,19 @@ ia32_sys_call_table:
.quad sys_fremovexattr
.quad sys_tkill /* 238 */
.quad sys_sendfile64
.quad sys_futex
.quad sys_futex /* 240 */
.quad sys32_sched_setaffinity
.quad sys32_sched_getaffinity
.quad sys_set_thread_area
.quad sys_get_thread_area
.quad sys32_io_setup
.quad sys_io_destroy
.quad sys_io_getevents
.quad sys_io_submit
.quad sys_io_cancel
.quad sys_ni_syscall /* 250 alloc_huge_pages */
.quad sys_ni_syscall /* free_huge_pages */
.quad sys_ni_syscall /* exit_group */
ia32_syscall_end:
.rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
.quad ni_syscall
......
......@@ -152,8 +152,8 @@ struct shm_info32 {
};
struct ipc_kludge {
struct msgbuf *msgp;
int msgtyp;
u32 msgp;
s32 msgtyp;
};
......@@ -268,14 +268,19 @@ semctl32 (int first, int second, int third, void *uptr)
return err;
}
#define MAXBUF (64*1024)
static int
do_sys32_msgsnd (int first, int second, int third, void *uptr)
{
struct msgbuf *p = kmalloc(second + sizeof(struct msgbuf) + 4, GFP_USER);
struct msgbuf *p;
struct msgbuf32 *up = (struct msgbuf32 *)uptr;
mm_segment_t old_fs;
int err;
if (second >= MAXBUF-sizeof(struct msgbuf))
return -EINVAL;
p = kmalloc(second + sizeof(struct msgbuf), GFP_USER);
if (!p)
return -ENOMEM;
err = get_user(p->mtype, &up->mtype);
......@@ -312,13 +317,15 @@ do_sys32_msgrcv (int first, int second, int msgtyp, int third, int version, void
uptr = (void *)A(ipck.msgp);
msgtyp = ipck.msgtyp;
}
if (second >= MAXBUF-sizeof(struct msgbuf))
return -EINVAL;
err = -ENOMEM;
p = kmalloc(second + sizeof(struct msgbuf) + 4, GFP_USER);
p = kmalloc(second + sizeof(struct msgbuf), GFP_USER);
if (!p)
goto out;
old_fs = get_fs();
set_fs(KERNEL_DS);
err = sys_msgrcv(first, p, second + 4, msgtyp, third);
err = sys_msgrcv(first, p, second, msgtyp, third);
set_fs(old_fs);
if (err < 0)
goto free_then_out;
......
......@@ -24,6 +24,7 @@
#include <asm/i387.h>
#include <asm/fpu32.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#define R32(l,q) \
case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break
......@@ -35,20 +36,32 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 val)
switch (regno) {
case offsetof(struct user32, regs.fs):
if (val && (val & 3) != 3) return -EIO;
child->thread.fs = val;
break;
case offsetof(struct user32, regs.gs):
if (val && (val & 3) != 3) return -EIO;
child->thread.gs = val;
break;
case offsetof(struct user32, regs.ds):
if (val && (val & 3) != 3) return -EIO;
child->thread.ds = val;
break;
case offsetof(struct user32, regs.es):
if (val && (val & 3) != 3) return -EIO;
child->thread.es = val;
break;
R32(cs, cs);
R32(ss, ss);
case offsetof(struct user32, regs.ss):
if ((val & 3) != 3) return -EIO;
stack[offsetof(struct pt_regs, ss)/8] = val;
break;
case offsetof(struct user32, regs.cs):
if ((val & 3) != 3) return -EIO;
stack[offsetof(struct pt_regs, cs)/8] = val;
break;
R32(ebx, rbx);
R32(ecx, rcx);
R32(edx, rdx);
......
......@@ -57,6 +57,7 @@
#include <linux/ipc.h>
#include <linux/rwsem.h>
#include <linux/init.h>
#include <linux/aio_abi.h>
#include <asm/mman.h>
#include <asm/types.h>
#include <asm/uaccess.h>
......@@ -1288,37 +1289,71 @@ static inline int put_flock(struct flock *kfl, struct flock32 *ufl)
extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg);
asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
static inline int get_flock64(struct ia32_flock64 *fl32, struct flock *fl64)
{
switch (cmd) {
case F_GETLK:
case F_SETLK:
case F_SETLKW:
{
struct flock f;
mm_segment_t old_fs;
long ret;
if (get_flock(&f, (struct flock32 *)arg))
return -EFAULT;
old_fs = get_fs(); set_fs (KERNEL_DS);
ret = sys_fcntl(fd, cmd, (unsigned long)&f);
set_fs (old_fs);
if (ret) return ret;
if (put_flock(&f, (struct flock32 *)arg))
return -EFAULT;
return 0;
if (access_ok(fl32, sizeof(struct ia32_flock64), VERIFY_WRITE)) {
int ret = __get_user(fl64->l_type, &fl32->l_type);
ret |= __get_user(fl64->l_whence, &fl32->l_whence);
ret |= __get_user(fl64->l_start, &fl32->l_start);
ret |= __get_user(fl64->l_len, &fl32->l_len);
ret |= __get_user(fl64->l_pid, &fl32->l_pid);
return ret;
}
default:
return sys_fcntl(fd, cmd, (unsigned long)arg);
return -EFAULT;
}
static inline int put_flock64(struct ia32_flock64 *fl32, struct flock *fl64)
{
if (access_ok(fl32, sizeof(struct ia32_flock64), VERIFY_WRITE)) {
int ret = __put_user(fl64->l_type, &fl32->l_type);
ret |= __put_user(fl64->l_whence, &fl32->l_whence);
ret |= __put_user(fl64->l_start, &fl32->l_start);
ret |= __put_user(fl64->l_len, &fl32->l_len);
ret |= __put_user(fl64->l_pid, &fl32->l_pid);
return ret;
}
return -EFAULT;
}
asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)
{
if (cmd >= F_GETLK64 && cmd <= F_SETLKW64)
return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg);
return sys32_fcntl(fd, cmd, arg);
struct flock fl64;
mm_segment_t oldfs = get_fs();
int ret = 0, origcmd;
unsigned long origarg;
origcmd = cmd;
origarg = arg;
switch (cmd) {
case F_GETLK:
case F_SETLK:
case F_SETLKW:
ret = get_flock(&fl64, (struct flock32 *)arg);
arg = (unsigned long) &fl64;
set_fs(KERNEL_DS);
break;
case F_GETLK64:
cmd = F_GETLK;
goto cnv64;
case F_SETLK64:
cmd = F_SETLK;
goto cnv64;
case F_SETLKW64:
cmd = F_SETLKW;
cnv64:
ret = get_flock64((struct ia32_flock64 *)arg, &fl64);
arg = (unsigned long)&fl64;
set_fs(KERNEL_DS);
break;
}
if (!ret)
ret = sys_fcntl(fd, cmd, arg);
set_fs(oldfs);
if (origcmd == F_GETLK && !ret)
ret = put_flock(&fl64, (struct flock32 *)origarg);
else if (cmd == F_GETLK && !ret)
ret = put_flock64((struct ia32_flock64 *)origarg, &fl64);
return ret;
}
int sys32_ni_syscall(int call)
......@@ -1362,42 +1397,6 @@ sys32_utime(char * filename, struct utimbuf32 *times)
return ret;
}
/*
* Ooo, nasty. We need here to frob 32-bit unsigned longs to
* 64-bit unsigned longs.
*/
static inline int
get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset)
{
if (ufdset) {
unsigned long odd;
if (verify_area(VERIFY_READ, ufdset, n*sizeof(u32)))
return -EFAULT;
odd = n & 1UL;
n &= ~1UL;
while (n) {
unsigned long h, l;
__get_user(l, ufdset);
__get_user(h, ufdset+1);
ufdset += 2;
*fdset++ = h << 32 | l;
n -= 2;
}
if (odd)
__get_user(*fdset, ufdset);
} else {
/* Tricky, must clear full unsigned long in the
* kernel fdset at the end, this makes sure that
* actually happens.
*/
memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32));
}
return 0;
}
extern asmlinkage long sys_sysfs(int option, unsigned long arg1,
unsigned long arg2);
......@@ -1704,136 +1703,6 @@ sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo)
return ret;
}
extern asmlinkage long sys_setreuid(uid_t ruid, uid_t euid);
asmlinkage long sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid)
{
uid_t sruid, seuid;
sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid);
seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid);
return sys_setreuid(sruid, seuid);
}
extern asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
asmlinkage long
sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid,
__kernel_uid_t32 suid)
{
uid_t sruid, seuid, ssuid;
sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid);
seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid);
ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid);
return sys_setresuid(sruid, seuid, ssuid);
}
extern asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
asmlinkage long
sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid,
__kernel_uid_t32 *suid)
{
uid_t a, b, c;
int ret;
mm_segment_t old_fs = get_fs();
set_fs (KERNEL_DS);
ret = sys_getresuid(&a, &b, &c);
set_fs (old_fs);
if (put_user (a, ruid) || put_user (b, euid) || put_user (c, suid))
return -EFAULT;
return ret;
}
extern asmlinkage long sys_setregid(gid_t rgid, gid_t egid);
asmlinkage long
sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid)
{
gid_t srgid, segid;
srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid);
segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid);
return sys_setregid(srgid, segid);
}
extern asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
asmlinkage long
sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid,
__kernel_gid_t32 sgid)
{
gid_t srgid, segid, ssgid;
srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid);
segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid);
ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid);
return sys_setresgid(srgid, segid, ssgid);
}
extern asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
asmlinkage long
sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid,
__kernel_gid_t32 *sgid)
{
gid_t a, b, c;
int ret;
mm_segment_t old_fs = get_fs();
set_fs (KERNEL_DS);
ret = sys_getresgid(&a, &b, &c);
set_fs (old_fs);
if (!ret) {
ret = put_user (a, rgid);
ret |= put_user (b, egid);
ret |= put_user (c, sgid);
}
return ret;
}
extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist);
asmlinkage long
sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist)
{
gid_t gl[NGROUPS];
int ret, i;
mm_segment_t old_fs = get_fs ();
set_fs (KERNEL_DS);
ret = sys_getgroups(gidsetsize, gl);
set_fs (old_fs);
if (gidsetsize && ret > 0 && ret <= NGROUPS)
for (i = 0; i < ret; i++, grouplist++)
if (put_user (gl[i], grouplist))
return -EFAULT;
return ret;
}
extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist);
asmlinkage long
sys32_setgroups(int gidsetsize, __kernel_gid_t32 *grouplist)
{
gid_t gl[NGROUPS];
int ret, i;
mm_segment_t old_fs = get_fs ();
if ((unsigned) gidsetsize > NGROUPS)
return -EINVAL;
for (i = 0; i < gidsetsize; i++, grouplist++)
if (get_user (gl[i], grouplist))
return -EFAULT;
set_fs (KERNEL_DS);
ret = sys_setgroups(gidsetsize, gl);
set_fs (old_fs);
return ret;
}
extern void check_pending(int signum);
asmlinkage long sys_utimes(char *, struct timeval *);
......@@ -1943,7 +1812,7 @@ sys32_newuname(struct new_utsname * name)
int ret = sys_newuname(name);
if (current->personality == PER_LINUX32 && !ret) {
ret = copy_to_user(name->machine, "i386\0\0", 8);
ret = copy_to_user(name->machine, "i686\0\0", 6);
}
return ret;
}
......@@ -2164,7 +2033,7 @@ int sys32_uname(struct old_utsname * name)
err=copy_to_user(name, &system_utsname, sizeof (*name));
up_read(&uts_sem);
if (current->personality == PER_LINUX32)
err |= copy_to_user(&name->machine, "i386", 5);
err |= copy_to_user(&name->machine, "i686", 5);
return err?-EFAULT:0;
}
......@@ -2270,16 +2139,17 @@ int sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs)
asmlinkage int sys32_fork(struct pt_regs regs)
{
struct task_struct *p;
p = do_fork(SIGCHLD, regs.rsp, &regs, 0);
p = do_fork(SIGCHLD, regs.rsp, &regs, 0, NULL);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
}
asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs)
{
struct task_struct *p;
int *user_tid = (int *)regs.rdx;
if (!newsp)
newsp = regs.rsp;
p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, user_tid);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
}
......@@ -2296,7 +2166,7 @@ asmlinkage int sys32_clone(unsigned int clone_flags, unsigned int newsp, struct
asmlinkage int sys32_vfork(struct pt_regs regs)
{
struct task_struct *p;
p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0);
p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, &regs, 0, NULL);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
}
......@@ -2695,10 +2565,26 @@ int sys32_sched_getaffinity(pid_t pid, unsigned int len,
return err;
}
extern long sys_io_setup(unsigned nr_reqs, aio_context_t *ctx);
long sys32_io_setup(unsigned nr_reqs, u32 *ctx32p)
{
long ret;
aio_context_t ctx64;
mm_segment_t oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sys_io_setup(nr_reqs, &ctx64);
set_fs(oldfs);
/* truncating is ok because it's a user address */
if (!ret)
ret = put_user((u32)ctx64, ctx32p);
return ret;
}
struct exec_domain ia32_exec_domain = {
name: "linux/x86",
pers_low: PER_LINUX32,
pers_high: PER_LINUX32,
.name = "linux/x86",
.pers_low = PER_LINUX32,
.pers_high = PER_LINUX32,
};
static int __init ia32_init (void)
......
......@@ -249,7 +249,17 @@
#define __NR_ia32_futex 240
#define __NR_ia32_sched_setaffinity 241
#define __NR_ia32_sched_getaffinity 242
#define __NR_ia32_set_thread_area 243
#define __NR_ia32_get_thread_area 244
#define __NR_ia32_io_setup 245
#define __NR_ia32_io_destroy 246
#define __NR_ia32_io_getevents 247
#define __NR_ia32_io_submit 248
#define __NR_ia32_io_cancel 249
#define __NR_ia32_alloc_hugepages 250
#define __NR_ia32_free_hugepages 251
#define __NR_ia32_exit_group 252
#define IA32_NR_syscalls 243 /* must be > than biggest syscall! */
#define IA32_NR_syscalls 260 /* must be > than biggest syscall! */
#endif /* _ASM_X86_64_IA32_UNISTD_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment