18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 38c2ecf20Sopenharmony_ci * Copyright 2003 PathScale, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Licensed under the GPL 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 118c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 128c2ecf20Sopenharmony_ci#include <asm/prctl.h> /* XXX This should get the constants from libc */ 138c2ecf20Sopenharmony_ci#include <os.h> 148c2ecf20Sopenharmony_ci#include <registers.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cilong arch_prctl(struct task_struct *task, int option, 178c2ecf20Sopenharmony_ci unsigned long __user *arg2) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci unsigned long *ptr = arg2, tmp; 208c2ecf20Sopenharmony_ci long ret; 218c2ecf20Sopenharmony_ci int pid = task->mm->context.id.u.pid; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* 248c2ecf20Sopenharmony_ci * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to 258c2ecf20Sopenharmony_ci * be safe), we need to call arch_prctl on the host because 268c2ecf20Sopenharmony_ci * setting %fs may result in something else happening (like a 278c2ecf20Sopenharmony_ci * GDT or thread.fs being set instead). So, we let the host 288c2ecf20Sopenharmony_ci * fiddle the registers and thread struct and restore the 298c2ecf20Sopenharmony_ci * registers afterwards. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * So, the saved registers are stored to the process (this 328c2ecf20Sopenharmony_ci * needed because a stub may have been the last thing to run), 338c2ecf20Sopenharmony_ci * arch_prctl is run on the host, then the registers are read 348c2ecf20Sopenharmony_ci * back. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci switch (option) { 378c2ecf20Sopenharmony_ci case ARCH_SET_FS: 388c2ecf20Sopenharmony_ci case ARCH_SET_GS: 398c2ecf20Sopenharmony_ci ret = restore_pid_registers(pid, ¤t->thread.regs.regs); 408c2ecf20Sopenharmony_ci if (ret) 418c2ecf20Sopenharmony_ci return ret; 428c2ecf20Sopenharmony_ci break; 438c2ecf20Sopenharmony_ci case ARCH_GET_FS: 448c2ecf20Sopenharmony_ci case ARCH_GET_GS: 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * With these two, we read to a local pointer and 478c2ecf20Sopenharmony_ci * put_user it to the userspace pointer that we were 488c2ecf20Sopenharmony_ci * given. If addr isn't valid (because it hasn't been 498c2ecf20Sopenharmony_ci * faulted in or is just bogus), we want put_user to 508c2ecf20Sopenharmony_ci * fault it in (or return -EFAULT) instead of having 518c2ecf20Sopenharmony_ci * the host return -EFAULT. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci ptr = &tmp; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci ret = os_arch_prctl(pid, option, ptr); 578c2ecf20Sopenharmony_ci if (ret) 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci switch (option) { 618c2ecf20Sopenharmony_ci case ARCH_SET_FS: 628c2ecf20Sopenharmony_ci current->thread.arch.fs = (unsigned long) ptr; 638c2ecf20Sopenharmony_ci ret = save_registers(pid, ¤t->thread.regs.regs); 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case ARCH_SET_GS: 668c2ecf20Sopenharmony_ci ret = save_registers(pid, ¤t->thread.regs.regs); 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci case ARCH_GET_FS: 698c2ecf20Sopenharmony_ci ret = put_user(tmp, arg2); 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci case ARCH_GET_GS: 728c2ecf20Sopenharmony_ci ret = put_user(tmp, arg2); 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return arch_prctl(current, option, (unsigned long __user *) arg2); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_civoid arch_switch_to(struct task_struct *to) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci if ((to->thread.arch.fs == 0) || (to->mm == NULL)) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs); 908c2ecf20Sopenharmony_ci} 91