patch-2.3.99-pre6 linux/arch/ia64/ia32/sys_ia32.c
Next file: linux/arch/ia64/kdb/Makefile
Previous file: linux/arch/ia64/ia32/ia32_traps.c
Back to the patch index
Back to the overall index
- Lines: 1397
- Date:
Fri Apr 21 15:21:24 2000
- Orig file:
v2.3.99-pre5/linux/arch/ia64/ia32/sys_ia32.c
- Orig date:
Tue Apr 11 15:09:12 2000
diff -u --recursive --new-file v2.3.99-pre5/linux/arch/ia64/ia32/sys_ia32.c linux/arch/ia64/ia32/sys_ia32.c
@@ -58,32 +58,6 @@
#define A(__x) ((unsigned long)(__x))
#define AA(__x) ((unsigned long)(__x))
-/*
- * This is trivial, and on the face of it looks like it
- * could equally well be done in user mode.
- *
- * Not so, for quite unobvious reasons - register pressure.
- * In user mode vfork() cannot have a stack frame, and if
- * done by calling the "clone()" system call directly, you
- * do not have enough call-clobbered registers to hold all
- * the information you need.
- */
-asmlinkage int sys32_vfork(
-int dummy0,
-int dummy1,
-int dummy2,
-int dummy3,
-int dummy4,
-int dummy5,
-int dummy6,
-int dummy7,
-int stack)
-{
- struct pt_regs *regs = (struct pt_regs *)&stack;
-
- return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->r12, regs);
-}
-
static int
nargs(unsigned int arg, char **ap)
{
@@ -842,82 +816,6 @@
return sys32_select(a.n, a.inp, a.outp, a.exp, a.tvp);
}
-struct rusage32 {
- struct timeval32 ru_utime;
- struct timeval32 ru_stime;
- int ru_maxrss;
- int ru_ixrss;
- int ru_idrss;
- int ru_isrss;
- int ru_minflt;
- int ru_majflt;
- int ru_nswap;
- int ru_inblock;
- int ru_oublock;
- int ru_msgsnd;
- int ru_msgrcv;
- int ru_nsignals;
- int ru_nvcsw;
- int ru_nivcsw;
-};
-
-static int
-put_rusage (struct rusage32 *ru, struct rusage *r)
-{
- int err;
-
- err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec);
- err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec);
- err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec);
- err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec);
- err |= __put_user (r->ru_maxrss, &ru->ru_maxrss);
- err |= __put_user (r->ru_ixrss, &ru->ru_ixrss);
- err |= __put_user (r->ru_idrss, &ru->ru_idrss);
- err |= __put_user (r->ru_isrss, &ru->ru_isrss);
- err |= __put_user (r->ru_minflt, &ru->ru_minflt);
- err |= __put_user (r->ru_majflt, &ru->ru_majflt);
- err |= __put_user (r->ru_nswap, &ru->ru_nswap);
- err |= __put_user (r->ru_inblock, &ru->ru_inblock);
- err |= __put_user (r->ru_oublock, &ru->ru_oublock);
- err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd);
- err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv);
- err |= __put_user (r->ru_nsignals, &ru->ru_nsignals);
- err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw);
- err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw);
- return err;
-}
-
-extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr,
- int options, struct rusage * ru);
-
-asmlinkage int
-sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options,
- struct rusage32 *ru)
-{
- if (!ru)
- return sys_wait4(pid, stat_addr, options, NULL);
- else {
- struct rusage r;
- int ret;
- unsigned int status;
- mm_segment_t old_fs = get_fs();
-
- set_fs (KERNEL_DS);
- ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r);
- set_fs (old_fs);
- if (put_rusage (ru, &r)) return -EFAULT;
- if (stat_addr && put_user (status, stat_addr))
- return -EFAULT;
- return ret;
- }
-}
-
-asmlinkage int
-sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options)
-{
- return sys32_wait4(pid, stat_addr, options, NULL);
-}
-
struct timespec32 {
int tv_sec;
int tv_nsec;
@@ -1586,367 +1484,904 @@
{
union semun fourth;
u32 pad;
- int err = -EINVAL;
+ int err, err2;
+ struct semid64_ds s;
+ struct semid_ds32 *usp;
+ mm_segment_t old_fs;
if (!uptr)
- goto out;
+ return -EINVAL;
err = -EFAULT;
if (get_user (pad, (u32 *)uptr))
- goto out;
+ return err;
if(third == SETVAL)
fourth.val = (int)pad;
else
fourth.__pad = (void *)A(pad);
- if (IPCOP_MASK (third) &
- (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) |
- IPCOP_MASK (GETVAL) | IPCOP_MASK (GETPID) |
- IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) |
- IPCOP_MASK (GETALL) | IPCOP_MASK (SETALL) |
- IPCOP_MASK (IPC_RMID))) {
+ switch (third) {
+
+ case IPC_INFO:
+ case IPC_RMID:
+ case IPC_SET:
+ case SEM_INFO:
+ case GETVAL:
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case GETALL:
+ case SETVAL:
+ case SETALL:
err = sys_semctl (first, second, third, fourth);
- } else {
- struct semid_ds s;
- struct semid_ds32 *usp = (struct semid_ds32 *)A(pad);
- mm_segment_t old_fs;
- int need_back_translation;
-
- if (third == IPC_SET) {
- err = get_user (s.sem_perm.uid, &usp->sem_perm.uid);
- err |= __get_user(s.sem_perm.gid, &usp->sem_perm.gid);
- err |= __get_user(s.sem_perm.mode, &usp->sem_perm.mode);
- if (err)
- goto out;
- fourth.__pad = &s;
- }
- need_back_translation =
- (IPCOP_MASK (third) &
- (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0;
- if (need_back_translation)
- fourth.__pad = &s;
+ break;
+
+ case IPC_STAT:
+ case SEM_STAT:
+ usp = (struct semid_ds32 *)A(pad);
+ fourth.__pad = &s;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_semctl (first, second, third, fourth);
set_fs (old_fs);
- if (need_back_translation) {
- int err2 = put_user(s.sem_perm.key, &usp->sem_perm.key);
- err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid);
- err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid);
- err2 |= __put_user(s.sem_perm.cuid,
- &usp->sem_perm.cuid);
- err2 |= __put_user (s.sem_perm.cgid,
- &usp->sem_perm.cgid);
- err2 |= __put_user (s.sem_perm.mode,
- &usp->sem_perm.mode);
- err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq);
- err2 |= __put_user (s.sem_otime, &usp->sem_otime);
- err2 |= __put_user (s.sem_ctime, &usp->sem_ctime);
- err2 |= __put_user (s.sem_nsems, &usp->sem_nsems);
- if (err2) err = -EFAULT;
- }
+ err2 = put_user(s.sem_perm.key, &usp->sem_perm.key);
+ err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid);
+ err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid);
+ err2 |= __put_user(s.sem_perm.cuid,
+ &usp->sem_perm.cuid);
+ err2 |= __put_user (s.sem_perm.cgid,
+ &usp->sem_perm.cgid);
+ err2 |= __put_user (s.sem_perm.mode,
+ &usp->sem_perm.mode);
+ err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq);
+ err2 |= __put_user (s.sem_otime, &usp->sem_otime);
+ err2 |= __put_user (s.sem_ctime, &usp->sem_ctime);
+ err2 |= __put_user (s.sem_nsems, &usp->sem_nsems);
+ if (err2)
+ err = -EFAULT;
+ break;
+
}
-out:
+
return err;
}
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 msgbuf32 *up = (struct msgbuf32 *)uptr;
- mm_segment_t old_fs;
- int err;
+ struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf)
+ + 4, GFP_USER);
+ struct msgbuf32 *up = (struct msgbuf32 *)uptr;
+ mm_segment_t old_fs;
+ int err;
+
+ if (!p)
+ return -ENOMEM;
+ err = get_user (p->mtype, &up->mtype);
+ err |= __copy_from_user (p->mtext, &up->mtext, second);
+ if (err)
+ goto out;
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_msgsnd (first, p, second, third);
+ set_fs (old_fs);
+out:
+ kfree (p);
+ return err;
+}
+
+static int
+do_sys32_msgrcv (int first, int second, int msgtyp, int third,
+ int version, void *uptr)
+{
+ struct msgbuf32 *up;
+ struct msgbuf *p;
+ mm_segment_t old_fs;
+ int err;
+
+ if (!version) {
+ struct ipc_kludge *uipck = (struct ipc_kludge *)uptr;
+ struct ipc_kludge ipck;
+
+ err = -EINVAL;
+ if (!uptr)
+ goto out;
+ err = -EFAULT;
+ if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge)))
+ goto out;
+ uptr = (void *)A(ipck.msgp);
+ msgtyp = ipck.msgtyp;
+ }
+ err = -ENOMEM;
+ p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER);
+ if (!p)
+ goto out;
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_msgrcv (first, p, second + 4, msgtyp, third);
+ set_fs (old_fs);
+ if (err < 0)
+ goto free_then_out;
+ up = (struct msgbuf32 *)uptr;
+ if (put_user (p->mtype, &up->mtype) ||
+ __copy_to_user (&up->mtext, p->mtext, err))
+ err = -EFAULT;
+free_then_out:
+ kfree (p);
+out:
+ return err;
+}
+
+static int
+do_sys32_msgctl (int first, int second, void *uptr)
+{
+ int err, err2;
+ struct msqid_ds m;
+ struct msqid64_ds m64;
+ struct msqid_ds32 *up = (struct msqid_ds32 *)uptr;
+ mm_segment_t old_fs;
+
+ switch (second) {
+
+ case IPC_INFO:
+ case IPC_RMID:
+ case MSG_INFO:
+ err = sys_msgctl (first, second, (struct msqid_ds *)uptr);
+ break;
+
+ case IPC_SET:
+ err = get_user (m.msg_perm.uid, &up->msg_perm.uid);
+ err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid);
+ err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode);
+ err |= __get_user (m.msg_qbytes, &up->msg_qbytes);
+ if (err)
+ break;
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_msgctl (first, second, &m);
+ set_fs (old_fs);
+ break;
+
+ case IPC_STAT:
+ case MSG_STAT:
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_msgctl (first, second, &m64);
+ set_fs (old_fs);
+ err2 = put_user (m64.msg_perm.key, &up->msg_perm.key);
+ err2 |= __put_user(m64.msg_perm.uid, &up->msg_perm.uid);
+ err2 |= __put_user(m64.msg_perm.gid, &up->msg_perm.gid);
+ err2 |= __put_user(m64.msg_perm.cuid, &up->msg_perm.cuid);
+ err2 |= __put_user(m64.msg_perm.cgid, &up->msg_perm.cgid);
+ err2 |= __put_user(m64.msg_perm.mode, &up->msg_perm.mode);
+ err2 |= __put_user(m64.msg_perm.seq, &up->msg_perm.seq);
+ err2 |= __put_user(m64.msg_stime, &up->msg_stime);
+ err2 |= __put_user(m64.msg_rtime, &up->msg_rtime);
+ err2 |= __put_user(m64.msg_ctime, &up->msg_ctime);
+ err2 |= __put_user(m64.msg_cbytes, &up->msg_cbytes);
+ err2 |= __put_user(m64.msg_qnum, &up->msg_qnum);
+ err2 |= __put_user(m64.msg_qbytes, &up->msg_qbytes);
+ err2 |= __put_user(m64.msg_lspid, &up->msg_lspid);
+ err2 |= __put_user(m64.msg_lrpid, &up->msg_lrpid);
+ if (err2)
+ err = -EFAULT;
+ break;
+
+ }
+
+ return err;
+}
+
+static int
+do_sys32_shmat (int first, int second, int third, int version, void *uptr)
+{
+ unsigned long raddr;
+ u32 *uaddr = (u32 *)A((u32)third);
+ int err = -EINVAL;
+
+ if (version == 1)
+ return err;
+ err = sys_shmat (first, uptr, second, &raddr);
+ if (err)
+ return err;
+ err = put_user (raddr, uaddr);
+ return err;
+}
+
+static int
+do_sys32_shmctl (int first, int second, void *uptr)
+{
+ int err = -EFAULT, err2;
+ struct shmid_ds s;
+ struct shmid64_ds s64;
+ struct shmid_ds32 *up = (struct shmid_ds32 *)uptr;
+ mm_segment_t old_fs;
+ struct shm_info32 {
+ int used_ids;
+ u32 shm_tot, shm_rss, shm_swp;
+ u32 swap_attempts, swap_successes;
+ } *uip = (struct shm_info32 *)uptr;
+ struct shm_info si;
+
+ switch (second) {
+
+ case IPC_INFO:
+ case IPC_RMID:
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ err = sys_shmctl (first, second, (struct shmid_ds *)uptr);
+ break;
+ case IPC_SET:
+ err = get_user (s.shm_perm.uid, &up->shm_perm.uid);
+ err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid);
+ err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode);
+ if (err)
+ break;
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_shmctl (first, second, &s);
+ set_fs (old_fs);
+ break;
+
+ case IPC_STAT:
+ case SHM_STAT:
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_shmctl (first, second, &s64);
+ set_fs (old_fs);
+ if (err < 0)
+ break;
+ err2 = put_user (s64.shm_perm.key, &up->shm_perm.key);
+ err2 |= __put_user (s64.shm_perm.uid, &up->shm_perm.uid);
+ err2 |= __put_user (s64.shm_perm.gid, &up->shm_perm.gid);
+ err2 |= __put_user (s64.shm_perm.cuid,
+ &up->shm_perm.cuid);
+ err2 |= __put_user (s64.shm_perm.cgid,
+ &up->shm_perm.cgid);
+ err2 |= __put_user (s64.shm_perm.mode,
+ &up->shm_perm.mode);
+ err2 |= __put_user (s64.shm_perm.seq, &up->shm_perm.seq);
+ err2 |= __put_user (s64.shm_atime, &up->shm_atime);
+ err2 |= __put_user (s64.shm_dtime, &up->shm_dtime);
+ err2 |= __put_user (s64.shm_ctime, &up->shm_ctime);
+ err2 |= __put_user (s64.shm_segsz, &up->shm_segsz);
+ err2 |= __put_user (s64.shm_nattch, &up->shm_nattch);
+ err2 |= __put_user (s64.shm_cpid, &up->shm_cpid);
+ err2 |= __put_user (s64.shm_lpid, &up->shm_lpid);
+ if (err2)
+ err = -EFAULT;
+ break;
+
+ case SHM_INFO:
+ old_fs = get_fs ();
+ set_fs (KERNEL_DS);
+ err = sys_shmctl (first, second, &si);
+ set_fs (old_fs);
+ if (err < 0)
+ break;
+ err2 = put_user (si.used_ids, &uip->used_ids);
+ err2 |= __put_user (si.shm_tot, &uip->shm_tot);
+ err2 |= __put_user (si.shm_rss, &uip->shm_rss);
+ err2 |= __put_user (si.shm_swp, &uip->shm_swp);
+ err2 |= __put_user (si.swap_attempts,
+ &uip->swap_attempts);
+ err2 |= __put_user (si.swap_successes,
+ &uip->swap_successes);
+ if (err2)
+ err = -EFAULT;
+ break;
+
+ }
+ return err;
+}
+
+asmlinkage int
+sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
+{
+ int version, err;
+
+ lock_kernel();
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ switch (call) {
+
+ case SEMOP:
+ /* struct sembuf is the same on 32 and 64bit :)) */
+ err = sys_semop (first, (struct sembuf *)AA(ptr),
+ second);
+ break;
+ case SEMGET:
+ err = sys_semget (first, second, third);
+ break;
+ case SEMCTL:
+ err = do_sys32_semctl (first, second, third,
+ (void *)AA(ptr));
+ break;
+
+ case MSGSND:
+ err = do_sys32_msgsnd (first, second, third,
+ (void *)AA(ptr));
+ break;
+ case MSGRCV:
+ err = do_sys32_msgrcv (first, second, fifth, third,
+ version, (void *)AA(ptr));
+ break;
+ case MSGGET:
+ err = sys_msgget ((key_t) first, second);
+ break;
+ case MSGCTL:
+ err = do_sys32_msgctl (first, second, (void *)AA(ptr));
+ break;
+
+ case SHMAT:
+ err = do_sys32_shmat (first, second, third,
+ version, (void *)AA(ptr));
+ break;
+ case SHMDT:
+ err = sys_shmdt ((char *)AA(ptr));
+ break;
+ case SHMGET:
+ err = sys_shmget (first, second, third);
+ break;
+ case SHMCTL:
+ err = do_sys32_shmctl (first, second, (void *)AA(ptr));
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ unlock_kernel();
+ return err;
+}
+
+/*
+ * sys_time() can be implemented in user-level using
+ * sys_gettimeofday(). IA64 did this but i386 Linux did not
+ * so we have to implement this system call here.
+ */
+asmlinkage long sys32_time(int * tloc)
+{
+ int i;
+
+ /* SMP: This is fairly trivial. We grab CURRENT_TIME and
+ stuff it to user space. No side effects */
+ i = CURRENT_TIME;
+ if (tloc) {
+ if (put_user(i,tloc))
+ i = -EFAULT;
+ }
+ return i;
+}
+
+struct rusage32 {
+ struct timeval32 ru_utime;
+ struct timeval32 ru_stime;
+ int ru_maxrss;
+ int ru_ixrss;
+ int ru_idrss;
+ int ru_isrss;
+ int ru_minflt;
+ int ru_majflt;
+ int ru_nswap;
+ int ru_inblock;
+ int ru_oublock;
+ int ru_msgsnd;
+ int ru_msgrcv;
+ int ru_nsignals;
+ int ru_nvcsw;
+ int ru_nivcsw;
+};
+
+static int
+put_rusage (struct rusage32 *ru, struct rusage *r)
+{
+ int err;
+
+ err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec);
+ err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec);
+ err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec);
+ err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec);
+ err |= __put_user (r->ru_maxrss, &ru->ru_maxrss);
+ err |= __put_user (r->ru_ixrss, &ru->ru_ixrss);
+ err |= __put_user (r->ru_idrss, &ru->ru_idrss);
+ err |= __put_user (r->ru_isrss, &ru->ru_isrss);
+ err |= __put_user (r->ru_minflt, &ru->ru_minflt);
+ err |= __put_user (r->ru_majflt, &ru->ru_majflt);
+ err |= __put_user (r->ru_nswap, &ru->ru_nswap);
+ err |= __put_user (r->ru_inblock, &ru->ru_inblock);
+ err |= __put_user (r->ru_oublock, &ru->ru_oublock);
+ err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd);
+ err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv);
+ err |= __put_user (r->ru_nsignals, &ru->ru_nsignals);
+ err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw);
+ err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw);
+ return err;
+}
+
+extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr,
+ int options, struct rusage * ru);
+
+asmlinkage int
+sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options,
+ struct rusage32 *ru)
+{
+ if (!ru)
+ return sys_wait4(pid, stat_addr, options, NULL);
+ else {
+ struct rusage r;
+ int ret;
+ unsigned int status;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs (KERNEL_DS);
+ ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r);
+ set_fs (old_fs);
+ if (put_rusage (ru, &r)) return -EFAULT;
+ if (stat_addr && put_user (status, stat_addr))
+ return -EFAULT;
+ return ret;
+ }
+}
+
+asmlinkage int
+sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options)
+{
+ return sys32_wait4(pid, stat_addr, options, NULL);
+}
+
+
+extern asmlinkage int
+sys_getrusage(int who, struct rusage *ru);
+
+asmlinkage int
+sys32_getrusage(int who, struct rusage32 *ru)
+{
+ struct rusage r;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs (KERNEL_DS);
+ ret = sys_getrusage(who, &r);
+ set_fs (old_fs);
+ if (put_rusage (ru, &r)) return -EFAULT;
+ return ret;
+}
+
+struct tms32 {
+ __kernel_clock_t32 tms_utime;
+ __kernel_clock_t32 tms_stime;
+ __kernel_clock_t32 tms_cutime;
+ __kernel_clock_t32 tms_cstime;
+};
+
+extern asmlinkage long sys_times(struct tms * tbuf);
+
+asmlinkage long
+sys32_times(struct tms32 *tbuf)
+{
+ struct tms t;
+ long ret;
+ mm_segment_t old_fs = get_fs ();
+ int err;
+
+ set_fs (KERNEL_DS);
+ ret = sys_times(tbuf ? &t : NULL);
+ set_fs (old_fs);
+ if (tbuf) {
+ err = put_user (t.tms_utime, &tbuf->tms_utime);
+ err |= __put_user (t.tms_stime, &tbuf->tms_stime);
+ err |= __put_user (t.tms_cutime, &tbuf->tms_cutime);
+ err |= __put_user (t.tms_cstime, &tbuf->tms_cstime);
+ if (err)
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+unsigned int
+ia32_peek (struct pt_regs *regs, struct task_struct *child, unsigned long addr, unsigned int *val)
+{
+ size_t copied;
+ unsigned int ret;
+
+ copied = access_process_vm(child, addr, val, sizeof(*val), 0);
+ return(copied != sizeof(ret) ? -EIO : 0);
+}
+
+unsigned int
+ia32_poke (struct pt_regs *regs, struct task_struct *child, unsigned long addr, unsigned int val)
+{
+
+ if (access_process_vm(child, addr, &val, sizeof(val), 1) != sizeof(val))
+ return -EIO;
+ return 0;
+}
+
+/*
+ * The order in which registers are stored in the ptrace regs structure
+ */
+#define PT_EBX 0
+#define PT_ECX 1
+#define PT_EDX 2
+#define PT_ESI 3
+#define PT_EDI 4
+#define PT_EBP 5
+#define PT_EAX 6
+#define PT_DS 7
+#define PT_ES 8
+#define PT_FS 9
+#define PT_GS 10
+#define PT_ORIG_EAX 11
+#define PT_EIP 12
+#define PT_CS 13
+#define PT_EFL 14
+#define PT_UESP 15
+#define PT_SS 16
+
+unsigned int
+getreg(struct task_struct *child, int regno)
+{
+ struct pt_regs *child_regs;
+
+ child_regs = ia64_task_regs(child);
+ switch (regno / sizeof(int)) {
+
+ case PT_EBX:
+ return(child_regs->r11);
+ case PT_ECX:
+ return(child_regs->r9);
+ case PT_EDX:
+ return(child_regs->r10);
+ case PT_ESI:
+ return(child_regs->r14);
+ case PT_EDI:
+ return(child_regs->r15);
+ case PT_EBP:
+ return(child_regs->r13);
+ case PT_EAX:
+ case PT_ORIG_EAX:
+ return(child_regs->r8);
+ case PT_EIP:
+ return(child_regs->cr_iip);
+ case PT_UESP:
+ return(child_regs->r12);
+ case PT_EFL:
+ return(child->thread.eflag);
+ case PT_DS:
+ case PT_ES:
+ case PT_FS:
+ case PT_GS:
+ case PT_SS:
+ return((unsigned int)__USER_DS);
+ case PT_CS:
+ return((unsigned int)__USER_CS);
+ default:
+ printk("getregs:unknown register %d\n", regno);
+ break;
+
+ }
+ return(0);
+}
+
+void
+putreg(struct task_struct *child, int regno, unsigned int value)
+{
+ struct pt_regs *child_regs;
+
+ child_regs = ia64_task_regs(child);
+ switch (regno / sizeof(int)) {
+
+ case PT_EBX:
+ child_regs->r11 = value;
+ break;
+ case PT_ECX:
+ child_regs->r9 = value;
+ break;
+ case PT_EDX:
+ child_regs->r10 = value;
+ break;
+ case PT_ESI:
+ child_regs->r14 = value;
+ break;
+ case PT_EDI:
+ child_regs->r15 = value;
+ break;
+ case PT_EBP:
+ child_regs->r13 = value;
+ break;
+ case PT_EAX:
+ case PT_ORIG_EAX:
+ child_regs->r8 = value;
+ break;
+ case PT_EIP:
+ child_regs->cr_iip = value;
+ break;
+ case PT_UESP:
+ child_regs->r12 = value;
+ break;
+ case PT_EFL:
+ child->thread.eflag = value;
+ break;
+ case PT_DS:
+ case PT_ES:
+ case PT_FS:
+ case PT_GS:
+ case PT_SS:
+ if (value != __USER_DS)
+ printk("setregs:try to set invalid segment register %d = %x\n", regno, value);
+ break;
+ case PT_CS:
+ if (value != __USER_CS)
+ printk("setregs:try to set invalid segment register %d = %x\n", regno, value);
+ break;
+ default:
+ printk("getregs:unknown register %d\n", regno);
+ break;
+
+ }
+}
+
+static inline void
+ia32f2ia64f(void *dst, void *src)
+{
+
+ __asm__ ("ldfe f6=[%1] ;;\n\t"
+ "stf.spill [%0]=f6"
+ :
+ : "r"(dst), "r"(src));
+ return;
+}
+
+static inline void
+ia64f2ia32f(void *dst, void *src)
+{
- if (!p)
- return -ENOMEM;
- err = get_user (p->mtype, &up->mtype);
- err |= __copy_from_user (p->mtext, &up->mtext, second);
- if (err)
- goto out;
- old_fs = get_fs ();
- set_fs (KERNEL_DS);
- err = sys_msgsnd (first, p, second, third);
- set_fs (old_fs);
-out:
- kfree (p);
- return err;
+ __asm__ ("ldf.fill f6=[%1] ;;\n\t"
+ "stfe [%0]=f6"
+ :
+ : "r"(dst), "r"(src));
+ return;
}
-static int
-do_sys32_msgrcv (int first, int second, int msgtyp, int third,
- int version, void *uptr)
+void
+put_fpreg(int regno, struct _fpreg_ia32 *reg, struct pt_regs *ptp, struct switch_stack *swp, int tos)
{
- struct msgbuf32 *up;
- struct msgbuf *p;
- mm_segment_t old_fs;
- int err;
+ struct _fpreg_ia32 *f;
+ char buf[32];
- if (!version) {
- struct ipc_kludge *uipck = (struct ipc_kludge *)uptr;
- struct ipc_kludge ipck;
+ f = (struct _fpreg_ia32 *)(((unsigned long)buf + 15) & ~15);
+ if ((regno += tos) >= 8)
+ regno -= 8;
+ switch (regno) {
+
+ case 0:
+ ia64f2ia32f(f, &ptp->f8);
+ break;
+ case 1:
+ ia64f2ia32f(f, &ptp->f9);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ ia64f2ia32f(f, &swp->f10 + (regno - 2));
+ break;
- err = -EINVAL;
- if (!uptr)
- goto out;
- err = -EFAULT;
- if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge)))
- goto out;
- uptr = (void *)A(ipck.msgp);
- msgtyp = ipck.msgtyp;
}
- err = -ENOMEM;
- p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER);
- if (!p)
- goto out;
- old_fs = get_fs ();
- set_fs (KERNEL_DS);
- err = sys_msgrcv (first, p, second + 4, msgtyp, third);
- set_fs (old_fs);
- if (err < 0)
- goto free_then_out;
- up = (struct msgbuf32 *)uptr;
- if (put_user (p->mtype, &up->mtype) ||
- __copy_to_user (&up->mtext, p->mtext, err))
- err = -EFAULT;
-free_then_out:
- kfree (p);
-out:
- return err;
+ __copy_to_user(reg, f, sizeof(*reg));
+ return;
}
-static int
-do_sys32_msgctl (int first, int second, void *uptr)
+void
+get_fpreg(int regno, struct _fpreg_ia32 *reg, struct pt_regs *ptp, struct switch_stack *swp, int tos)
{
- int err;
- if (IPCOP_MASK (second) &
- (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (MSG_INFO) |
- IPCOP_MASK (IPC_RMID))) {
- err = sys_msgctl (first, second, (struct msqid_ds *)uptr);
- } else {
- struct msqid_ds m;
- struct msqid_ds32 *up = (struct msqid_ds32 *)uptr;
- mm_segment_t old_fs;
-
- if (second == IPC_SET) {
- err = get_user (m.msg_perm.uid, &up->msg_perm.uid);
- err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid);
- err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode);
- err |= __get_user (m.msg_qbytes, &up->msg_qbytes);
- if (err)
- goto out;
- }
- old_fs = get_fs ();
- set_fs (KERNEL_DS);
- err = sys_msgctl (first, second, &m);
- set_fs (old_fs);
- if (IPCOP_MASK (second) &
- (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) {
- int err2 = put_user (m.msg_perm.key, &up->msg_perm.key);
- err2 |= __put_user(m.msg_perm.uid, &up->msg_perm.uid);
- err2 |= __put_user(m.msg_perm.gid, &up->msg_perm.gid);
- err2 |= __put_user(m.msg_perm.cuid, &up->msg_perm.cuid);
- err2 |= __put_user(m.msg_perm.cgid, &up->msg_perm.cgid);
- err2 |= __put_user(m.msg_perm.mode, &up->msg_perm.mode);
- err2 |= __put_user(m.msg_perm.seq, &up->msg_perm.seq);
- err2 |= __put_user(m.msg_stime, &up->msg_stime);
- err2 |= __put_user(m.msg_rtime, &up->msg_rtime);
- err2 |= __put_user(m.msg_ctime, &up->msg_ctime);
- err2 |= __put_user(m.msg_cbytes, &up->msg_cbytes);
- err2 |= __put_user(m.msg_qnum, &up->msg_qnum);
- err2 |= __put_user(m.msg_qbytes, &up->msg_qbytes);
- err2 |= __put_user(m.msg_lspid, &up->msg_lspid);
- err2 |= __put_user(m.msg_lrpid, &up->msg_lrpid);
- if (err2)
- err = -EFAULT;
- }
- }
+ if ((regno += tos) >= 8)
+ regno -= 8;
+ switch (regno) {
-out:
- return err;
+ case 0:
+ __copy_from_user(&ptp->f8, reg, sizeof(*reg));
+ break;
+ case 1:
+ __copy_from_user(&ptp->f9, reg, sizeof(*reg));
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ __copy_from_user(&swp->f10 + (regno - 2), reg, sizeof(*reg));
+ break;
+
+ }
+ return;
}
-static int
-do_sys32_shmat (int first, int second, int third, int version, void *uptr)
+int
+save_ia32_fpstate(struct task_struct *tsk, struct _fpstate_ia32 *save)
{
- unsigned long raddr;
- u32 *uaddr = (u32 *)A((u32)third);
- int err = -EINVAL;
-
- if (version == 1)
- goto out;
- err = sys_shmat (first, uptr, second, &raddr);
- if (err)
- goto out;
- err = put_user (raddr, uaddr);
-out:
- return err;
+ struct switch_stack *swp;
+ struct pt_regs *ptp;
+ int i, tos;
+
+ if (!access_ok(VERIFY_WRITE, save, sizeof(*save)))
+ return(-EIO);
+ __put_user(tsk->thread.fcr, &save->cw);
+ __put_user(tsk->thread.fsr, &save->sw);
+ __put_user(tsk->thread.fsr >> 32, &save->tag);
+ __put_user(tsk->thread.fir, &save->ipoff);
+ __put_user(__USER_CS, &save->cssel);
+ __put_user(tsk->thread.fdr, &save->dataoff);
+ __put_user(__USER_DS, &save->datasel);
+ /*
+ * Stack frames start with 16-bytes of temp space
+ */
+ swp = (struct switch_stack *)(tsk->thread.ksp + 16);
+ ptp = ia64_task_regs(tsk);
+ tos = (tsk->thread.fsr >> 11) & 3;
+ for (i = 0; i < 8; i++)
+ put_fpreg(i, &save->_st[i], ptp, swp, tos);
+ return(0);
}
-static int
-do_sys32_shmctl (int first, int second, void *uptr)
+int
+restore_ia32_fpstate(struct task_struct *tsk, struct _fpstate_ia32 *save)
{
- int err;
-
- if (IPCOP_MASK (second) &
- (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SHM_LOCK)
- | IPCOP_MASK (SHM_UNLOCK) | IPCOP_MASK (IPC_RMID))) {
- err = sys_shmctl (first, second, (struct shmid_ds *)uptr);
- } else {
- struct shmid_ds s;
- struct shmid_ds32 *up = (struct shmid_ds32 *)uptr;
- mm_segment_t old_fs;
-
- if (second == IPC_SET) {
- err = get_user (s.shm_perm.uid, &up->shm_perm.uid);
- err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid);
- err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode);
- if (err)
- goto out;
- }
- old_fs = get_fs ();
- set_fs (KERNEL_DS);
- err = sys_shmctl (first, second, &s);
- set_fs (old_fs);
- if (err < 0)
- goto out;
-
- /* Mask it even in this case so it becomes a CSE. */
- if (second == SHM_INFO) {
- struct shm_info32 {
- int used_ids;
- u32 shm_tot, shm_rss, shm_swp;
- u32 swap_attempts, swap_successes;
- } *uip = (struct shm_info32 *)uptr;
- struct shm_info *kp = (struct shm_info *)&s;
- int err2 = put_user (kp->used_ids, &uip->used_ids);
- err2 |= __put_user (kp->shm_tot, &uip->shm_tot);
- err2 |= __put_user (kp->shm_rss, &uip->shm_rss);
- err2 |= __put_user (kp->shm_swp, &uip->shm_swp);
- err2 |= __put_user (kp->swap_attempts,
- &uip->swap_attempts);
- err2 |= __put_user (kp->swap_successes,
- &uip->swap_successes);
- if (err2)
- err = -EFAULT;
- } else if (IPCOP_MASK (second) &
- (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) {
- int err2 = put_user (s.shm_perm.key, &up->shm_perm.key);
- err2 |= __put_user (s.shm_perm.uid, &up->shm_perm.uid);
- err2 |= __put_user (s.shm_perm.gid, &up->shm_perm.gid);
- err2 |= __put_user (s.shm_perm.cuid,
- &up->shm_perm.cuid);
- err2 |= __put_user (s.shm_perm.cgid,
- &up->shm_perm.cgid);
- err2 |= __put_user (s.shm_perm.mode,
- &up->shm_perm.mode);
- err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq);
- err2 |= __put_user (s.shm_atime, &up->shm_atime);
- err2 |= __put_user (s.shm_dtime, &up->shm_dtime);
- err2 |= __put_user (s.shm_ctime, &up->shm_ctime);
- err2 |= __put_user (s.shm_segsz, &up->shm_segsz);
- err2 |= __put_user (s.shm_nattch, &up->shm_nattch);
- err2 |= __put_user (s.shm_cpid, &up->shm_cpid);
- err2 |= __put_user (s.shm_lpid, &up->shm_lpid);
- if (err2)
- err = -EFAULT;
- }
- }
-out:
- return err;
+ struct switch_stack *swp;
+ struct pt_regs *ptp;
+ int i, tos;
+ int fsrlo, fsrhi;
+
+ if (!access_ok(VERIFY_READ, save, sizeof(*save)))
+ return(-EIO);
+ __get_user(tsk->thread.fcr, (unsigned int *)&save->cw);
+ __get_user(fsrlo, (unsigned int *)&save->sw);
+ __get_user(fsrhi, (unsigned int *)&save->tag);
+ tsk->thread.fsr = ((long)fsrhi << 32) | (long)fsrlo;
+ __get_user(tsk->thread.fir, (unsigned int *)&save->ipoff);
+ __get_user(tsk->thread.fdr, (unsigned int *)&save->dataoff);
+ /*
+ * Stack frames start with 16-bytes of temp space
+ */
+ swp = (struct switch_stack *)(tsk->thread.ksp + 16);
+ ptp = ia64_task_regs(tsk);
+ tos = (tsk->thread.fsr >> 11) & 3;
+ for (i = 0; i < 8; i++)
+ get_fpreg(i, &save->_st[i], ptp, swp, tos);
+ return(0);
}
-asmlinkage int
-sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
+asmlinkage long sys_ptrace(long, pid_t, unsigned long, unsigned long, long, long, long, long, long);
+
+/*
+ * Note that the IA32 version of `ptrace' calls the IA64 routine for
+ * many of the requests. This will only work for requests that do
+ * not need access to the calling processes `pt_regs' which is located
+ * at the address of `stack'. Once we call the IA64 `sys_ptrace' then
+ * the address of `stack' will not be the address of the `pt_regs'.
+ */
+asmlinkage long
+sys32_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
+ long arg4, long arg5, long arg6, long arg7, long stack)
{
- int version, err;
+ struct pt_regs *regs = (struct pt_regs *) &stack;
+ struct task_struct *child;
+ long i, ret;
+ unsigned int value;
lock_kernel();
- version = call >> 16; /* hack for backward compatibility */
- call &= 0xffff;
+ if (request == PTRACE_TRACEME) {
+ ret = sys_ptrace(request, pid, addr, data,
+ arg4, arg5, arg6, arg7, stack);
+ goto out;
+ }
- if (call <= SEMCTL)
- switch (call) {
- case SEMOP:
- /* struct sembuf is the same on 32 and 64bit :)) */
- err = sys_semop (first, (struct sembuf *)AA(ptr),
- second);
- goto out;
- case SEMGET:
- err = sys_semget (first, second, third);
- goto out;
- case SEMCTL:
- err = do_sys32_semctl (first, second, third,
- (void *)AA(ptr));
- goto out;
- default:
- err = -EINVAL;
- goto out;
- };
- if (call <= MSGCTL)
- switch (call) {
- case MSGSND:
- err = do_sys32_msgsnd (first, second, third,
- (void *)AA(ptr));
- goto out;
- case MSGRCV:
- err = do_sys32_msgrcv (first, second, fifth, third,
- version, (void *)AA(ptr));
- goto out;
- case MSGGET:
- err = sys_msgget ((key_t) first, second);
- goto out;
- case MSGCTL:
- err = do_sys32_msgctl (first, second, (void *)AA(ptr));
- goto out;
- default:
- err = -EINVAL;
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ read_unlock(&tasklist_lock);
+ if (!child)
+ goto out;
+ ret = -EPERM;
+ if (pid == 1) /* no messing around with init! */
+ goto out;
+
+ if (request == PTRACE_ATTACH) {
+ ret = sys_ptrace(request, pid, addr, data,
+ arg4, arg5, arg6, arg7, stack);
+ goto out;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
goto out;
+ }
+ if (child->p_pptr != current)
+ goto out;
+
+ switch (request) {
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA: /* read word at location addr */
+ ret = ia32_peek(regs, child, addr, &value);
+ if (ret == 0)
+ ret = put_user(value, (unsigned int *)data);
+ else
+ ret = -EIO;
+ goto out;
+
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA: /* write the word at location addr */
+ ret = ia32_poke(regs, child, addr, (unsigned int)data);
+ goto out;
+
+ case PTRACE_PEEKUSR: /* read word at addr in USER area */
+ ret = 0;
+ break;
+
+ case PTRACE_POKEUSR: /* write word at addr in USER area */
+ ret = 0;
+ break;
+
+ case IA32_PTRACE_GETREGS:
+ if (!access_ok(VERIFY_WRITE, (int *)data, 17*sizeof(int))) {
+ ret = -EIO;
+ break;
}
- if (call <= SHMCTL)
- switch (call) {
- case SHMAT:
- err = do_sys32_shmat (first, second, third,
- version, (void *)AA(ptr));
- goto out;
- case SHMDT:
- err = sys_shmdt ((char *)AA(ptr));
- goto out;
- case SHMGET:
- err = sys_shmget (first, second, third);
- goto out;
- case SHMCTL:
- err = do_sys32_shmctl (first, second, (void *)AA(ptr));
- goto out;
- default:
- err = -EINVAL;
- goto out;
+ for ( i = 0; i < 17*sizeof(int); i += sizeof(int) ) {
+ __put_user(getreg(child, i),(unsigned int *) data);
+ data += sizeof(int);
}
+ ret = 0;
+ break;
- err = -EINVAL;
+ case IA32_PTRACE_SETREGS:
+ {
+ unsigned int tmp;
+ if (!access_ok(VERIFY_READ, (int *)data, 17*sizeof(int))) {
+ ret = -EIO;
+ break;
+ }
+ for ( i = 0; i < 17*sizeof(int); i += sizeof(int) ) {
+ __get_user(tmp, (unsigned int *) data);
+ putreg(child, i, tmp);
+ data += sizeof(int);
+ }
+ ret = 0;
+ break;
+ }
-out:
- unlock_kernel();
- return err;
-}
+ case IA32_PTRACE_GETFPREGS:
+ ret = save_ia32_fpstate(child, (struct _fpstate_ia32 *)data);
+ break;
-/*
- * sys_time() can be implemented in user-level using
- * sys_gettimeofday(). IA64 did this but i386 Linux did not
- * so we have to implement this system call here.
- */
-asmlinkage long sys32_time(int * tloc)
-{
- int i;
+ case IA32_PTRACE_SETFPREGS:
+ ret = restore_ia32_fpstate(child, (struct _fpstate_ia32 *)data);
+ break;
+
+ case PTRACE_SYSCALL: /* continue, stop after next syscall */
+ case PTRACE_CONT: /* restart after signal. */
+ case PTRACE_KILL:
+ case PTRACE_SINGLESTEP: /* execute chile for one instruction */
+ case PTRACE_DETACH: /* detach a process */
+ unlock_kernel();
+ ret = sys_ptrace(request, pid, addr, data,
+ arg4, arg5, arg6, arg7, stack);
+ return(ret);
+
+ default:
+ ret = -EIO;
+ break;
- /* SMP: This is fairly trivial. We grab CURRENT_TIME and
- stuff it to user space. No side effects */
- i = CURRENT_TIME;
- if (tloc) {
- if (put_user(i,tloc))
- i = -EFAULT;
}
- return i;
+ out:
+ unlock_kernel();
+ return ret;
}
#ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */
@@ -2719,37 +3154,6 @@
return ret;
}
-struct tms32 {
- __kernel_clock_t32 tms_utime;
- __kernel_clock_t32 tms_stime;
- __kernel_clock_t32 tms_cutime;
- __kernel_clock_t32 tms_cstime;
-};
-
-extern asmlinkage long sys_times(struct tms * tbuf);
-
-asmlinkage long
-sys32_times(struct tms32 *tbuf)
-{
- struct tms t;
- long ret;
- mm_segment_t old_fs = get_fs ();
- int err;
-
- set_fs (KERNEL_DS);
- ret = sys_times(tbuf ? &t : NULL);
- set_fs (old_fs);
- if (tbuf) {
- err = put_user (t.tms_utime, &tbuf->tms_utime);
- err |= __put_user (t.tms_stime, &tbuf->tms_stime);
- err |= __put_user (t.tms_cutime, &tbuf->tms_cutime);
- err |= __put_user (t.tms_cstime, &tbuf->tms_cstime);
- if (err)
- ret = -EFAULT;
- }
- return ret;
-}
-
extern asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist);
asmlinkage int
@@ -2789,23 +3193,6 @@
return ret;
}
-extern asmlinkage int
-sys_getrusage(int who, struct rusage *ru);
-
-asmlinkage int
-sys32_getrusage(int who, struct rusage32 *ru)
-{
- struct rusage r;
- int ret;
- mm_segment_t old_fs = get_fs();
-
- set_fs (KERNEL_DS);
- ret = sys_getrusage(who, &r);
- set_fs (old_fs);
- if (put_rusage (ru, &r)) return -EFAULT;
- return ret;
-}
-
/* XXX These as well... */
extern __inline__ struct socket *
@@ -4355,4 +4742,3 @@
return ret;
}
#endif // NOTYET
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)