linux/arch/e2k/kernel/process.c

7654 lines
204 KiB
C

/*
* arch/e2k/kernel/process.c
*
* This file handles the arch-dependent parts of process handling
*
* Copyright 2001 Salavat S. Guiliazov (atic@mcst.ru)
*/
#include <linux/compat.h>
#include <linux/context_tracking.h>
#include <linux/types.h>
#include <linux/jhash.h>
#include <linux/tick.h>
#include <linux/ftrace.h>
#include <linux/rmap.h>
#include <linux/elf.h>
#include <linux/kthread.h>
#include <linux/mempolicy.h>
#include <linux/migrate.h>
#include <linux/mm_inline.h>
#include <linux/swap.h>
#include <linux/syscalls.h>
#include <linux/pm.h>
#include <linux/cpuidle.h>
#include <asm/atomic.h>
#include <asm/cpu.h>
#include <asm/process.h>
#include <asm/a.out.h>
#include <asm/mmu_context.h>
#include <asm/pgalloc.h>
#include <asm/regs_state.h>
#include <asm/boot_init.h>
#include <asm/e2k_debug.h>
#include <asm/sge.h>
#include <asm/ucontext.h>
#ifdef CONFIG_MONITORS
#include <asm/monitors.h>
#endif /* CONFIG_MONITORS */
#ifdef CONFIG_PROTECTED_MODE
#include <asm/3p.h>
#include <asm/e2k_ptypes.h>
#include <asm/prot_loader.h>
#endif /* CONFIG_PROTECTED_MODE */
#undef DEBUG_PROCESS_MODE
#undef DebugP
#define DEBUG_PROCESS_MODE 0 /* processes */
#define DebugP(...) DebugPrint(DEBUG_PROCESS_MODE ,##__VA_ARGS__)
#undef DEBUG_TASK_MODE
#undef DebugT
#define DEBUG_TASK_MODE 0 /* tasks */
#define DebugT(...) DebugPrint(DEBUG_TASK_MODE ,##__VA_ARGS__)
#undef DEBUG_QUEUED_TASK_MODE
#undef DebugQT
#define DEBUG_QUEUED_TASK_MODE 0 /* queue task and release */
#define DebugQT(...) DebugPrint(DEBUG_QUEUED_TASK_MODE ,##__VA_ARGS__)
#undef DEBUG_QUEUED_STACK_MODE
#undef DebugQS
#define DEBUG_QUEUED_STACK_MODE 0 /* queue stck and release */
#define DebugQS(...) DebugPrint(DEBUG_QUEUED_STACK_MODE ,##__VA_ARGS__)
#undef DEBUG_EXECVE_MODE
#undef DebugEX
#define DEBUG_EXECVE_MODE 0 /* execve and exit */
#define DebugEX(...) DebugPrint(DEBUG_EXECVE_MODE ,##__VA_ARGS__)
#undef DEBUG_DATA_STACK_MODE
#undef DebugDS
#define DEBUG_DATA_STACK_MODE 0 /* user data stack */
#define DebugDS(...) DebugPrint(DEBUG_DATA_STACK_MODE ,##__VA_ARGS__)
#undef DEBUG_EXPAND_STACK_MODE
#undef DebugES
#define DEBUG_EXPAND_STACK_MODE 0 /* expand stack */
#define DebugES(...) DebugPrint(DEBUG_EXPAND_STACK_MODE ,##__VA_ARGS__)
#undef DEBUG_EXPAND_STACK_GDB
#undef DebugES_GDB
#define DEBUG_EXPAND_STACK_GDB 0 /* expand stack for gdb*/
#define DebugES_GDB(...) DebugPrint(DEBUG_EXPAND_STACK_GDB ,##__VA_ARGS__)
#undef DEBUG_CU_MODE
#undef DebugCU
#define DEBUG_CU_MODE 0 /* compilation unit */
#define DebugCU(...) DebugPrint(DEBUG_CU_MODE ,##__VA_ARGS__)
#undef DEBUG_US_MODE
#undef DebugUS
#define DEBUG_US_MODE 0 /* user stacks */
#define DebugUS(...) DebugPrint(DEBUG_US_MODE ,##__VA_ARGS__)
#undef DEBUG_KS_MODE
#undef DebugKS
#define DEBUG_KS_MODE 0 /* kernel stacks */
#define DebugKS(...) DebugPrint(DEBUG_KS_MODE ,##__VA_ARGS__)
#undef DEBUG_ST_MODE
#undef DebugST
#define DEBUG_ST_MODE 0 /* all stacks manipulation */
#define DebugST(...) DebugPrint(DEBUG_ST_MODE ,##__VA_ARGS__)
#undef DEBUG_SD_MODE
#undef DebugSD
#define DEBUG_SD_MODE 0 /* go stack down */
#define DebugSD(...) DebugPrint(DEBUG_SD_MODE ,##__VA_ARGS__)
#undef DEBUG_CT_MODE
#undef DebugCT
#define DEBUG_CT_MODE 0 /* copy thread */
#define DebugCT(...) DebugPrint(DEBUG_CT_MODE ,##__VA_ARGS__)
#undef DEBUG_CL_MODE
#undef DebugCL
#define DEBUG_CL_MODE 0 /* clone thread */
#define DebugCL(...) DebugPrint(DEBUG_CL_MODE ,##__VA_ARGS__)
#undef DEBUG_FORK_MODE
#undef DebugF
#define DEBUG_FORK_MODE 0 /* fork process */
#define DebugF(...) DebugPrint(DEBUG_FORK_MODE ,##__VA_ARGS__)
#undef DebugNUMA
#undef DEBUG_NUMA_MODE
#define DEBUG_NUMA_MODE 0 /* NUMA */
#define DebugNUMA(...) DebugPrint(DEBUG_NUMA_MODE ,##__VA_ARGS__)
#define DEBUG_32 0 /* processes */
#define DebugP_32(...) DebugPrint(DEBUG_32 ,##__VA_ARGS__)
#undef DEBUG_HS_MODE
#define DEBUG_HS_MODE 0 /* Hard Stack Clone and Alloc */
#define DebugHS(...) DebugPrint(DEBUG_HS_MODE ,##__VA_ARGS__)
#undef DEBUG_HS_FAIL_MODE
#undef DebugHSF
#define DEBUG_HS_FAIL_MODE 0 /* Hard Stack Clone and Alloc */
/* failure */
#define DebugHSF(...) DebugPrint(DEBUG_HS_FAIL_MODE ,##__VA_ARGS__)
#define DEBUG_FTRACE_MODE 0
#if DEBUG_FTRACE_MODE
# define DebugFTRACE(...) pr_info(__VA_ARGS__)
#else
# define DebugFTRACE(...)
#endif
#define DEBUG_CTX_MODE 0 /* setcontext/swapcontext */
#if DEBUG_CTX_MODE
#define DebugCTX(...) DebugPrint(DEBUG_CTX_MODE ,##__VA_ARGS__)
#else
#define DebugCTX(...)
#endif
#undef DEBUG_SPRs_MODE
#define DEBUG_SPRs_MODE 0 /* stack pointers registers */
#undef DEBUG_CpuR_MODE
#define DEBUG_CpuR_MODE 0 /* CPU registers */
#undef DEBUG_SWREGS_MODE
#define DEBUG_SWREGS_MODE 0 /* switch registers */
enum {
HW_STACK_TYPE_PS,
HW_STACK_TYPE_PCS
};
unsigned long idle_halt;
EXPORT_SYMBOL(idle_halt);
unsigned long idle_nomwait;
EXPORT_SYMBOL(idle_nomwait);
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
memcpy(dst, src, sizeof(*dst));
return 0;
}
char *debug_process_name = NULL;
int debug_process_name_len = 0;
static int __init debug_process_name_setup(char *str)
{
debug_process_name = str;
debug_process_name_len = strlen(debug_process_name);
return 1;
}
__setup("procdebug=", debug_process_name_setup);
extern void schedule_tail(struct task_struct *prev);
extern struct machdep machine;
typedef void (*start_fn)(u64 __start);
#ifdef CONFIG_PROTECTED_MODE
extern void * rtl_GetNextInitFunction(rtl_Unit_t *unit_p, void **entry_pp);
#endif /* CONFIG_PROTECTED_MODE */
static void release_hw_stacks(struct thread_info *);
/*
* SLAB cache for task_struct structures (task) & thread_info (ti)
*/
struct kmem_cache __nodedata *task_cachep = NULL;
struct kmem_cache __nodedata *thread_cachep = NULL;
#ifdef CONFIG_NUMA
struct kmem_cache __nodedata *node_policy_cache = NULL;
#endif /* CONFIG_NUMA */
static DEFINE_PER_CPU(struct list_head, hw_stacks_to_free_list);
static DEFINE_PER_CPU(struct task_struct *, kfree_hw_stacks_task);
/*
* User hardware stack mode
*/
int uhws_mode = UHWS_MODE_PSEUDO;
static int __init uhws_setup(char *str)
{
if (!strcmp(str, "cont"))
uhws_mode = UHWS_MODE_CONT;
else if (!strcmp(str, "pseudo"))
uhws_mode = UHWS_MODE_PSEUDO;
else
pr_err("Unknown user hardware stack mode\n");
return 1;
}
__setup("uhws=", uhws_setup);
const char *arch_vma_name(struct vm_area_struct *vma)
{
if (vma->vm_flags & VM_HW_STACK_PS)
return "[procedure stack]";
else if (vma->vm_flags & VM_HW_STACK_PCS)
return "[chain stack]";
return NULL;
}
void queue_hw_stack_to_free(struct task_struct *task)
{
struct thread_info *ti = task_thread_info(task);
struct task_struct *daemon;
unsigned long flags;
int cpu;
DebugQS("started on CPU %d for task 0x%p %d (%s)\n",
raw_smp_processor_id(), task, task->pid, task->comm);
local_irq_save(flags);
/*
* We've already taken a reference to the task_struct
*/
cpu = smp_processor_id();
list_add_tail(&ti->hw_stacks_to_free,
&per_cpu(hw_stacks_to_free_list, cpu));
local_irq_restore(flags);
daemon = per_cpu(kfree_hw_stacks_task, cpu);
if (daemon && daemon->state != TASK_RUNNING)
wake_up_process(daemon);
}
static void free_queued_hw_stacks(int cpu)
{
struct list_head *head;
struct thread_info *ti;
head = &__get_cpu_var(hw_stacks_to_free_list);
raw_local_irq_disable();
while (!list_empty(head)) {
ti = list_entry(
head->next, struct thread_info, hw_stacks_to_free);
BUG_ON(!ti || !ti->task);
list_del_init(&ti->hw_stacks_to_free);
raw_local_irq_enable();
DebugQS("on CPU %d will free hw stacks for task 0x%p %d (%s)\n",
cpu, ti->task, ti->task->pid, ti->task->comm);
release_hw_stacks(ti);
put_task_struct(ti->task);
raw_local_irq_disable();
BUG_ON(cpu != raw_smp_processor_id());
}
raw_local_irq_enable();
}
static int kfree_hw_stacksd(void *data)
{
int cpu = (int) (unsigned long) data;
while (kthread_should_stop() == 0) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
__set_current_state(TASK_RUNNING);
free_queued_hw_stacks(cpu);
}
return 0;
}
static int create_kfree_hw_stacks_tasks()
{
int cpu;
for_each_possible_cpu(cpu) {
per_cpu(kfree_hw_stacks_task, cpu) =
kthread_create_on_cpu(kfree_hw_stacksd,
(void *) cpu, cpu,
"kfree_hw_stacksd/%u");
BUG_ON(IS_ERR(per_cpu(kfree_hw_stacks_task, cpu)));
kthread_unpark(per_cpu(kfree_hw_stacks_task, cpu));
}
return 0;
}
arch_initcall(create_kfree_hw_stacks_tasks);
void __init
task_caches_init(void)
{
int cpu;
#ifdef CONFIG_NUMA
int nid;
#endif /* ! CONFIG_NUMA */
task_cachep = kmem_cache_create("task_struct",
sizeof(struct task_struct), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!task_cachep)
panic("Cannot create task structures SLAB cache");
thread_cachep = kmem_cache_create("thread_info",
sizeof(struct thread_info), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!thread_cachep)
panic("Cannot create thread info structures SLAB cache");
#ifdef CONFIG_NUMA
node_policy_cache = kmem_cache_create("node_mempolicy",
sizeof(struct mempolicy), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!node_policy_cache)
panic("Cannot create memory policy structures SLAB cache");
for_each_node_has_dup_kernel(nid) {
*node_task_cachep(nid) = task_cachep;
*node_thread_cachep(nid) = thread_cachep;
*the_node_policy_cache(nid) = node_policy_cache;
}
#endif /* ! CONFIG_NUMA */
for_each_possible_cpu(cpu)
INIT_LIST_HEAD(&per_cpu(hw_stacks_to_free_list, cpu));
}
void *alloc_kernel_c_stack()
{
int node = tsk_fork_get_node(current);
void *addr;
addr = alloc_pages_exact_nid(node, KERNEL_C_STACK_SIZE,
GFP_KERNEL | __GFP_NOWARN);
if (!addr)
addr = vmalloc_node(KERNEL_C_STACK_SIZE, node);
return addr;
}
void free_kernel_c_stack(void *addr)
{
if (is_vmalloc_addr(addr))
vfree(addr);
else
free_pages_exact(addr, KERNEL_C_STACK_SIZE);
}
static void *alloc_kernel_p_stack(struct thread_info *ti)
{
int node = tsk_fork_get_node(current);
void *addr;
addr = alloc_pages_exact_nid(node, KERNEL_P_STACK_SIZE,
GFP_KERNEL | __GFP_NOWARN);
if (!addr)
addr = vmalloc_node(KERNEL_P_STACK_SIZE, node);
return addr;
}
static void free_kernel_p_stack(void *addr)
{
if (is_vmalloc_addr(addr))
vfree(addr);
else
free_pages_exact(addr, KERNEL_P_STACK_SIZE);
}
static void *alloc_kernel_pc_stack(struct thread_info *ti)
{
int node = tsk_fork_get_node(current);
void *addr;
addr = alloc_pages_exact_nid(node, KERNEL_PC_STACK_SIZE,
GFP_KERNEL | __GFP_NOWARN);
if (!addr)
addr = vmalloc_node(KERNEL_PC_STACK_SIZE, node);
return addr;
}
static void free_kernel_pc_stack(void *addr)
{
if (is_vmalloc_addr(addr))
vfree(addr);
else
free_pages_exact(addr, KERNEL_PC_STACK_SIZE);
}
static void free_user_stack(void *stack_base, e2k_size_t max_stack_size,
e2k_size_t kernel_part_size)
{
int retval;
DebugUS("started: stack base 0x%llx max stack size 0x%lx, kernel part size 0x%lx\n",
(u64) stack_base, max_stack_size, kernel_part_size);
set_ts_flag(TS_KERNEL_SYSCALL);
retval = vm_munmap((unsigned long) stack_base,
max_stack_size + kernel_part_size);
clear_ts_flag(TS_KERNEL_SYSCALL);
DebugUS("do_munmap() returned %d\n", retval);
if (retval) {
pr_err("Could not do munmap: error %d\n", retval);
BUG();
}
DebugUS("freed stack base 0x%llx stack size 0x%lx\n",
(u64) stack_base, max_stack_size + kernel_part_size);
}
static void *alloc_user_hard_stack(size_t stack_size, size_t present_offset,
size_t present_size, size_t kernel_size,
unsigned long user_stacks_base, unsigned long user_stacks_end,
int type)
{
e2k_addr_t stack_addr;
struct vm_area_struct *vma;
struct thread_info *ti = current_thread_info();
e2k_addr_t u_ps_base, u_pcs_base;
unsigned long ti_status;
int ret = 0;
BUG_ON(!IS_ALIGNED(kernel_size, PAGE_SIZE) ||
!IS_ALIGNED(stack_size, PAGE_SIZE) ||
!IS_ALIGNED(present_size, PAGE_SIZE));
BUG_ON(!current->mm);
if (!UHWS_PSEUDO_MODE)
stack_size += kernel_size;
BUG_ON(!present_size);
present_size += kernel_size;
if (present_size > stack_size) {
DebugHS("alloc_user_hard_stack(): present_size 0x%lx > stack_size 0x%lx\n",
present_size, stack_size);
return NULL;
}
DebugHS("started: stack size 0x%lx, including present 0x%lx (user 0x%lx/kernel 0x%lx)\n",
stack_size, present_size, present_size - kernel_size,
kernel_size);
/*
* In the case of pseudo discontinuous user hardware stacks one
* shouldn't reuse already freed memory of user hardware stacks,
* otherwise there will be a problem with longjmp (we won't be
* able to find needed area unambiguously).
*/
if (UHWS_PSEUDO_MODE) {
if (ti->cur_ps) {
u_ps_base = (e2k_addr_t)ti->cur_ps->base;
user_stacks_base = max(user_stacks_base, u_ps_base);
}
if (ti->cur_pcs) {
u_pcs_base = (e2k_addr_t)ti->cur_pcs->base;
user_stacks_base = max(user_stacks_base, u_pcs_base);
}
}
/*
* Hardware stacks are essentially divided into 4 parts:
* 1) Allocated area below current window;
* 2) Current resident window (as described by PSP and PCSP registers);
* 3) Allocated but unused area above current window;
* 4) Unallocated area.
*
* They have the following flags set:
* 1) VM_READ | VM_WRITE
* 2) VM_READ | VM_WRITE | VM_DONTCOPY | VM_DONTMIGRATE | VM_HW_STACK
* 3) VM_READ | VM_WRITE | VM_DONTCOPY
* 4) VM_DONTCOPY | VM_DONTMIGRATE | VM_DONTEXPAND
*
* Initially only 2) and 4) areas exist so we can set
* (VM_DONTCOPY | VM_DONTMIGRATE) on the whole vma in
* vm_mmap() below.
*
* We also set VM_DONTEXPAND initially to remove VM_LOCKED
* from 4) area in case it is set in current->mm->def_flags,
* but remove it later for 2) area.
*
* And the whole stack area has VM_PRIVILEGED flag set to
* protect it from all user modifications with except for
* mlock/munlock.
*
* Also the whole stack area has VM_HW_STACK_P(C)S flag set.
*/
ti_status = (type == HW_STACK_TYPE_PS) ?
TS_MMAP_HW_STACK_PS : TS_MMAP_HW_STACK_PCS;
current_thread_info()->status |= ti_status;
stack_addr = vm_mmap(NULL, user_stacks_base, stack_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
0);
current_thread_info()->status &= ~ti_status;
if (IS_ERR_VALUE(stack_addr)) {
DebugHSF("mmap() returned error %ld\n", (long) stack_addr);
WARN_ONCE(stack_addr != -ENOMEM, "vm_mmap failed with %lld\n",
stack_addr);
return NULL;
}
DebugHS("vm_mmap() returned stack addr 0x%lx\n", stack_addr);
if (stack_addr < user_stacks_base ||
stack_addr + stack_size > user_stacks_end) {
DebugHSF("bad stack base or end\n");
goto out_unmap;
}
down_write(&current->mm->mmap_sem);
vma = find_vma(current->mm, stack_addr);
if (vma->vm_start != stack_addr ||
vma->vm_end != stack_addr + stack_size) {
up_write(&current->mm->mmap_sem);
pr_err("Invalid VMA structure start addr 0x%lx or end 0x%lx (should be <= 0x%lx & >= 0x%lx)\n",
vma->vm_start, vma->vm_end,
stack_addr, stack_addr + stack_size);
BUG();
}
up_write(&current->mm->mmap_sem);
BUG_ON((vma->vm_flags & VM_LOCKED) ||
(vma->vm_flags & (VM_DONTCOPY | VM_DONTMIGRATE) !=
(VM_DONTCOPY | VM_DONTMIGRATE)));
if (present_size) {
/*
* One should not populate the resident part of new hardware
* stack area, if this area is allocated while hardware stack
* expanding. A part of the resident window will be populated
* by remapping of old hardware stack area, other part will be
* populated manually.
*/
bool populate = (present_offset) ? false : true;
if ((ret = update_vm_area_flags(stack_addr + present_offset,
present_size, VM_HW_STACK, VM_DONTEXPAND))) {
DebugHSF("update_vm_area_flags returned %d\n", ret);
goto out_unmap;
}
set_ts_flag(TS_KERNEL_SYSCALL);
DebugHS("set PROT_READ | PROT_WRITE from 0x%lx to 0x%lx\n",
stack_addr, stack_addr + present_offset + present_size);
if ((ret = sys_mprotect(stack_addr,
present_offset + present_size,
PROT_READ | PROT_WRITE))) {
DebugHSF("sys_mprotect returned %d\n", ret);
goto out_unmap;
}
clear_ts_flag(TS_KERNEL_SYSCALL);
DebugHS("mlock from 0x%lx to 0x%lx\n",
stack_addr + present_offset,
stack_addr + present_offset + present_size);
if ((ret = mlock_hw_stack(stack_addr + present_offset,
present_size, populate))) {
DebugHSF("mlock returned %d\n", ret);
goto out_unmap;
}
if (!present_offset)
goto out;
if ((ret = update_vm_area_flags(stack_addr, present_offset, 0,
VM_DONTCOPY | VM_DONTMIGRATE |
VM_DONTEXPAND))) {
DebugHSF("update_vm_area_flags returned %d\n", ret);
goto out_unmap;
}
}
if (present_offset) {
/*
* One shouldn't populate this area, because it will be
* populated by remapping of old hardware stack area.
*/
if (current->mm->def_flags & VM_LOCKED) {
DebugHS("mlock from 0x%lx to 0x%lx\n",
stack_addr, stack_addr + present_offset);
if ((ret = mlock_hw_stack(stack_addr,
present_offset, false))) {
DebugHSF("mlock returned %d\n", ret);
goto out_unmap;
}
}
}
out:
DebugHS("finished and returns stack base 0x%lx\n", stack_addr);
return (void *) stack_addr;
out_unmap:
set_ts_flag(TS_KERNEL_SYSCALL);
vm_munmap(stack_addr, stack_size);
clear_ts_flag(TS_KERNEL_SYSCALL);
return NULL;
}
struct hw_stack_area *alloc_user_p_stack(size_t stack_area_size,
size_t present_offset,
size_t present_size)
{
struct hw_stack_area *ps;
ps = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (!ps)
return NULL;
ps->base = alloc_user_hard_stack(stack_area_size, present_offset,
present_size, KERNEL_P_STACK_SIZE,
USER_P_STACKS_BASE,
USER_P_STACKS_BASE + USER_P_STACKS_MAX_SIZE,
HW_STACK_TYPE_PS);
if (!ps->base) {
kfree(ps);
return NULL;
}
ps->size = stack_area_size - KERNEL_P_STACK_SIZE;
ps->offset = present_offset;
ps->top = present_offset + present_size;
return ps;
}
static void *alloc_user_p_stack_cont(size_t max_stack_size, size_t present_size)
{
return alloc_user_hard_stack(max_stack_size, 0, present_size,
KERNEL_P_STACK_SIZE, USER_P_STACKS_BASE,
USER_P_STACKS_BASE + USER_P_STACKS_MAX_SIZE,
HW_STACK_TYPE_PS);
}
struct hw_stack_area *alloc_user_pc_stack(size_t stack_area_size,
size_t present_offset,
size_t present_size)
{
struct hw_stack_area *pcs;
pcs = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (!pcs)
return NULL;
pcs->base = alloc_user_hard_stack(stack_area_size, present_offset,
present_size, KERNEL_PC_STACK_SIZE, USER_PC_STACKS_BASE,
USER_PC_STACKS_BASE + USER_PC_STACKS_MAX_SIZE,
HW_STACK_TYPE_PCS);
if (!pcs->base) {
kfree(pcs);
return NULL;
}
pcs->size = stack_area_size - KERNEL_PC_STACK_SIZE;
pcs->offset = present_offset;
pcs->top = present_offset + present_size;
return pcs;
}
static void *alloc_user_pc_stack_cont(size_t max_stack_size,
size_t present_size)
{
return alloc_user_hard_stack(max_stack_size, 0, present_size,
KERNEL_PC_STACK_SIZE, USER_PC_STACKS_BASE,
USER_PC_STACKS_BASE + USER_PC_STACKS_MAX_SIZE,
HW_STACK_TYPE_PCS);
}
static void free_user_p_stack(struct hw_stack_area *ps, int free_desc)
{
if (ps) {
size_t present_size = ps->top - ps->offset +
KERNEL_P_STACK_SIZE;
int ret;
BUG_ON(!ps->base);
DebugHS("munlock 0x%lx bytes from 0x%lx\n",
present_size, ps->base + ps->offset);
ret = munlock_hw_stack((unsigned long) ps->base + ps->offset,
present_size);
BUG_ON(ret);
free_user_stack(ps->base, ps->size, KERNEL_P_STACK_SIZE);
if (free_desc)
kfree(ps);
}
}
static void free_user_p_stack_cont(void *stack_base, size_t max_stack_size,
unsigned long present_offset, size_t present_top)
{
size_t present_size = present_top - present_offset +
KERNEL_P_STACK_SIZE;
int ret;
DebugHS("munlock from 0x%p size 0x%lx\n",
stack_base + present_offset, present_size);
ret = munlock_hw_stack((unsigned long) stack_base + present_offset,
present_size);
BUG_ON(ret);
return free_user_stack(stack_base, max_stack_size, KERNEL_P_STACK_SIZE);
}
static void free_user_pc_stack(struct hw_stack_area *pcs, int free_desc)
{
if (pcs) {
size_t present_size = pcs->top - pcs->offset +
KERNEL_PC_STACK_SIZE;
int ret;
BUG_ON(!pcs->base);
DebugHS("munlock 0x%lx bytes from 0x%lx\n",
present_size, pcs->base + pcs->offset);
ret = munlock_hw_stack((unsigned long) pcs->base + pcs->offset,
present_size);
BUG_ON(ret);
free_user_stack(pcs->base, pcs->size, KERNEL_PC_STACK_SIZE);
if (free_desc)
kfree(pcs);
}
}
static void free_user_pc_stack_cont(void *stack_base, size_t max_stack_size,
unsigned long present_offset, size_t present_top)
{
size_t present_size = present_top - present_offset +
KERNEL_PC_STACK_SIZE;
int ret;
DebugHS("munlock from 0x%lx size 0x%lx\n",
stack_base + present_offset, present_size);
ret = munlock_hw_stack((unsigned long) stack_base + present_offset,
present_size);
BUG_ON(ret);
return free_user_stack(stack_base, max_stack_size,
KERNEL_PC_STACK_SIZE);
}
static void free_user_p_stack_areas(struct list_head *ps_list)
{
struct hw_stack_area *user_p_stack;
struct hw_stack_area *n;
list_for_each_entry_safe(user_p_stack, n, ps_list, list_entry) {
list_del(&user_p_stack->list_entry);
free_user_p_stack(user_p_stack, true);
}
}
static void free_user_pc_stack_areas(struct list_head *pcs_list)
{
struct hw_stack_area *user_pc_stack;
struct hw_stack_area *n;
list_for_each_entry_safe(user_pc_stack, n, pcs_list, list_entry) {
list_del(&user_pc_stack->list_entry);
free_user_pc_stack(user_pc_stack, true);
}
}
void free_user_old_pc_stack_areas(struct list_head *old_u_pcs_list)
{
struct hw_stack_area *user_old_pc_stack;
struct hw_stack_area *n;
list_for_each_entry_safe(user_old_pc_stack, n, old_u_pcs_list,
list_entry) {
list_del(&user_old_pc_stack->list_entry);
kfree(user_old_pc_stack);
}
}
struct task_struct *alloc_task_struct_node(int node)
{
struct kmem_cache *p;
int tmp;
tmp = ts_get_node(current_thread_info());
if (tmp != NUMA_NO_NODE)
node = tmp;
if (!node_has_online_mem(node)) {
DebugT("node #%d has not memory, so alloc on default node\n",
node);
node = NUMA_NO_NODE;
}
p = kmem_cache_alloc_node(task_cachep, GFP_KERNEL, node);
DebugT("NODE #%d CPU #%d allocated task struct 0x%p on node %d\n",
numa_node_id(), smp_processor_id(), p, node);
return (struct task_struct *) p;
}
void free_task_struct(struct task_struct *task)
{
DebugP("started for task 0x%p (%s)\n", task, task->comm);
kmem_cache_free(task_cachep, task);
}
static void initialize_thread_info(struct thread_info *ti)
{
/*
* If an error occurs during copy_process(), we will check
* these fields and free user hardware stacks if needed.
*/
if (UHWS_PSEUDO_MODE) {
ti->cur_ps = NULL;
ti->cur_pcs = NULL;
} else {
ti->ps_base = NULL;
ti->pcs_base = NULL;
}
}
struct thread_info *alloc_thread_info_node(struct task_struct *task, int node)
{
struct kmem_cache *ti;
int tmp;
tmp = ts_get_node(current_thread_info());
if (tmp != NUMA_NO_NODE)
node = tmp;
if (!node_has_online_mem(node)) {
DebugT("node #%d has not memory, so alloc on default node\n",
node);
node = NUMA_NO_NODE;
}
ti = kmem_cache_alloc_node(thread_cachep, GFP_KERNEL, node);
if (ti)
initialize_thread_info((struct thread_info *) ti);
DebugT("NODE #%d CPU #%d allocated thread info 0x%p on node %d\n",
numa_node_id(), smp_processor_id(), ti, node);
return (struct thread_info *) ti;
}
void free_thread_info(struct thread_info *ti)
{
DebugP("started for task %s\n", ti->task->comm);
BUG_ON(test_ti_status_flag(ti, TS_MAPPED_HW_STACKS));
BUG_ON(UHWS_PSEUDO_MODE && (ti->cur_ps || ti->cur_pcs));
/*
* On e2k kernel data stack is not located together with
* thread_info cause it is too big.
*/
if (ti->k_stk_base) {
DebugEX("free kernel C stack from 0x%lx\n", ti->k_stk_base);
free_kernel_c_stack((void *) ti->k_stk_base);
ti->k_stk_base = 0;
}
kmem_cache_free(thread_cachep, ti);
}
static u64 _get_user_main_c_stack(e2k_addr_t sp, e2k_size_t max_stack_size,
e2k_size_t init_size, e2k_addr_t *stack_top)
{
e2k_addr_t stack_start, stack_end;
struct vm_area_struct *vma;
DebugDS("started: sp 0x%lx max stack "
"size 0x%lx init size 0x%lx\n",
sp, max_stack_size, init_size);
down_write(&current->mm->mmap_sem);
vma = find_vma(current->mm, sp);
DebugDS("find_vma() returned VMA 0x%p "
"start 0x%lx end 0x%lx\n",
vma, vma->vm_start, vma->vm_end);
BUG_ON(!(vma->vm_flags & VM_GROWSDOWN));
stack_end = vma->vm_end;
*stack_top = stack_end;
#ifdef CONFIG_ALLOC_MAX_STACK
stack_start = stack_end - max_stack_size;
#else
stack_start = stack_end - init_size;
# ifdef CONFIG_MAKE_ALL_PAGES_VALID
/* Leave the guard page out of stack
* (see comment before expand_user_data_stack()) */
if (stack_start > vma->vm_start + PAGE_SIZE)
stack_start = vma->vm_start + PAGE_SIZE;
# endif
#endif
#ifdef CONFIG_MAKE_ALL_PAGES_VALID
if (make_vma_pages_valid(vma, stack_start - PAGE_SIZE, stack_end)) {
DebugDS("make valid failed\n");
return 0;
}
#endif
DebugDS("start 0x%lx, end 0x%lx, init 0x%lx\n",
stack_start, stack_end, init_size);
/* Leave the guard page out of stack
* (see comment before expand_user_data_stack()) */
if (stack_start - PAGE_SIZE < vma->vm_start) {
DebugDS("will expand stack space "
"from 0x%lx to 0x%lx\n",
stack_start, vma->vm_start);
if (expand_stack(vma, stack_start - PAGE_SIZE)) {
up_write(&current->mm->mmap_sem);
DebugDS("expand_stack() "
"failed\n");
return 0;
}
}
up_write(&current->mm->mmap_sem);
DebugDS("returns stack base 0x%lx\n",
stack_start);
return stack_start;
}
static u64 get_user_main_c_stack(e2k_addr_t sp, e2k_addr_t *stack_top)
{
BUILD_BUG_ON(USER_MAIN_C_STACK_INIT_SIZE > USER_MAIN_C_STACK_SIZE);
return _get_user_main_c_stack(sp, USER_MAIN_C_STACK_SIZE,
USER_MAIN_C_STACK_INIT_SIZE,
stack_top);
}
/*
* This function allocates user's memory for needs of Compilation Unit Table.
*/
static int alloc_cu_table(e2k_addr_t cut_base_addr, e2k_size_t cut_size)
{
e2k_addr_t cut_start;
DebugCU("started: cut base 0x%lx, size 0x%lx\n",
cut_base_addr, cut_size);
set_ts_flag(TS_KERNEL_SYSCALL);
cut_start = vm_mmap(NULL, cut_base_addr, cut_size,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0);
clear_ts_flag(TS_KERNEL_SYSCALL);
DebugCU("vm_mmap() returned %ld\n", (long) cut_start);
return cut_start ? 0 : -ENOMEM;
}
static int
prepare_next_p_stack_area_before_switch(struct hw_stack_area *cur_u_ps,
struct hw_stack_area *prev_u_ps)
{
e2k_addr_t from;
e2k_size_t sz;
int retval;
/*
* One should munlock a part of resident area of the previous hardware
* stack area, that should become not resident, to remap already not
* locked pages.
*/
if (!(current->mm->def_flags & VM_LOCKED)) {
from = (e2k_addr_t)(prev_u_ps->base + prev_u_ps->offset);
sz = USER_P_STACK_BYTE_INCR;
retval = munlock_hw_stack(from, sz);
DebugHS("munlock 0x%lx from 0x%lx\n", sz, from);
if (retval) {
DebugHS("munlock_hw_stack() returned error\n");
return retval;
}
}
from = (e2k_addr_t)prev_u_ps->base;
sz = prev_u_ps->size + KERNEL_P_STACK_SIZE;
DebugHS("remap 0x%lx bytes from 0x%lx to 0x%lx\n",
sz, from, cur_u_ps->base);
retval = remap_user_hard_stack(cur_u_ps->base, (void *)from, sz);
if (retval) {
DebugHS("remap_user_hard_stack() returned error\n");
return retval;
}
/*
* While allocating new user hardware stack area, area for the resident
* part was mlocked, but was not populated. A part of new resident area
* was populated by remapping of the previous user hardware stack area.
* Other part of new resident area should be populated manually.
*/
from = (e2k_addr_t)cur_u_ps->base + cur_u_ps->top +
KERNEL_P_STACK_SIZE - USER_P_STACK_BYTE_INCR;
sz = USER_P_STACK_BYTE_INCR;
DebugHS("populate 0x%lx bytes from 0x%lx\n", sz, from);
retval = __mm_populate(from, sz, 0);
if (retval)
DebugHS("__mm_populate() returned error\n");
return retval;
}
static void
prepare_prev_p_stack_area_after_switch(struct hw_stack_area *prev_u_ps)
{
list_del(&prev_u_ps->list_entry);
DebugHS("user Procedure stack area 0x%p was deleted from user Procedure stack areas list\n",
prev_u_ps);
free_user_p_stack(prev_u_ps, false);
}
int switch_to_next_p_stack_area(void)
{
thread_info_t *ti = current_thread_info();
struct hw_stack_area *cur_u_ps;
struct hw_stack_area *prev_u_ps;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
long flags;
int retval;
cur_u_ps = ti->cur_ps;
prev_u_ps = list_entry(cur_u_ps->list_entry.prev,
struct hw_stack_area, list_entry);
BUG_ON(cur_u_ps == prev_u_ps);
DebugHS("cur_u_ps 0x%p prev_u_ps 0x%p\n",
cur_u_ps, prev_u_ps);
retval = prepare_next_p_stack_area_before_switch(cur_u_ps, prev_u_ps);
if (retval) {
DebugHS("prepare_next_p_stack_area_before_switch() returned error\n");
return retval;
}
raw_all_irq_save(flags);
E2K_FLUSHR;
psp_hi = READ_PSP_HI_REG();
psp_lo = READ_PSP_LO_REG();
psp_hi.PSP_hi_ind = psp_hi.PSP_hi_ind - USER_P_STACK_BYTE_INCR;
psp_hi.PSP_hi_size = cur_u_ps->top - cur_u_ps->offset +
KERNEL_P_STACK_SIZE;
psp_lo.PSP_lo_base = (e2k_addr_t)cur_u_ps->base + cur_u_ps->offset;
WRITE_PSP_REG(psp_hi, psp_lo);
raw_all_irq_restore(flags);
DebugHS("new PS state: base 0x%llx ind 0x%x size 0x%x\n",
psp_lo.PSP_lo_base, psp_hi.PSP_hi_ind, psp_hi.PSP_hi_size);
prepare_prev_p_stack_area_after_switch(prev_u_ps);
return retval;
}
static int
prepare_next_pc_stack_area_before_switch(struct hw_stack_area *cur_u_pcs,
struct hw_stack_area *prev_u_pcs)
{
e2k_addr_t from;
e2k_size_t sz;
int retval;
/*
* One should munlock a part of resident area of the previous hardware
* stack area, that should become not resident, to remap already not
* locked pages.
*/
if (!(current->mm->def_flags & VM_LOCKED)) {
from = (e2k_addr_t)(prev_u_pcs->base + prev_u_pcs->offset);
sz = USER_PC_STACK_BYTE_INCR;
retval = munlock_hw_stack(from, sz);
DebugHS("munlock 0x%lx from 0x%lx\n", sz, from);
if (retval) {
DebugHS("munlock_hw_stack() returned error\n");
return retval;
}
}
from = (e2k_addr_t)prev_u_pcs->base;
sz = prev_u_pcs->size + KERNEL_PC_STACK_SIZE;
DebugHS("remap 0x%lx bytes from 0x%lx to 0x%lx\n",
sz, from, cur_u_pcs->base);
retval = remap_user_hard_stack(cur_u_pcs->base, (void *)from, sz);
if (retval) {
DebugHS("remap_user_hard_stack() returned error\n");
return retval;
}
/*
* While allocating new user hardware stack area, area for the resident
* part was mlocked, but was not populated. A part of new resident area
* was populated by remapping of the previous user hardware stack area.
* Other part of new resident area should be populated manually.
*/
from = (e2k_addr_t)cur_u_pcs->base + cur_u_pcs->top +
KERNEL_PC_STACK_SIZE - USER_PC_STACK_BYTE_INCR;
sz = USER_PC_STACK_BYTE_INCR;
DebugHS("populate 0x%lx bytes from 0x%lx\n", sz, from);
retval = __mm_populate(from, sz, 0);
if (retval)
DebugHS("__mm_populate() returned error\n");
return retval;
}
static void
prepare_prev_pc_stack_area_after_switch(struct hw_stack_area *prev_u_pcs)
{
struct thread_info *ti = current_thread_info();
list_move_tail(&prev_u_pcs->list_entry, &ti->old_u_pcs_list);
DebugHS("user Procedure chain stack area 0x%p was deleted from user Procedure chain stack areas list\n",
prev_u_pcs);
free_user_pc_stack(prev_u_pcs, false);
}
int switch_to_next_pc_stack_area(void)
{
thread_info_t *ti = current_thread_info();
struct hw_stack_area *cur_u_pcs;
struct hw_stack_area *prev_u_pcs;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
long flags;
int retval;
cur_u_pcs = ti->cur_pcs;
prev_u_pcs = list_entry(cur_u_pcs->list_entry.prev,
struct hw_stack_area, list_entry);
BUG_ON(cur_u_pcs == prev_u_pcs);
DebugHS("cur_u_pcs 0x%p prev_u_pcs 0x%p\n",
cur_u_pcs, prev_u_pcs);
retval = prepare_next_pc_stack_area_before_switch(
cur_u_pcs, prev_u_pcs);
if (retval) {
DebugHS("prepare_next_pc_stack_area_before_switch() returned error\n");
return retval;
}
raw_all_irq_save(flags);
E2K_FLUSHC;
pcsp_hi = READ_PCSP_HI_REG();
pcsp_lo = READ_PCSP_LO_REG();
pcsp_hi.PCSP_hi_ind = pcsp_hi.PCSP_hi_ind - USER_PC_STACK_BYTE_INCR;
pcsp_hi.PCSP_hi_size = cur_u_pcs->top - cur_u_pcs->offset +
KERNEL_PC_STACK_SIZE;
pcsp_lo.PCSP_lo_base = (e2k_addr_t)cur_u_pcs->base + cur_u_pcs->offset;
WRITE_PCSP_REG(pcsp_hi, pcsp_lo);
raw_all_irq_restore(flags);
DebugHS("new PCS state: base 0x%llx ind 0x%x size 0x%x\n",
pcsp_lo.PCSP_lo_base, pcsp_hi.PCSP_hi_ind,
pcsp_hi.PCSP_hi_size);
prepare_prev_pc_stack_area_after_switch(prev_u_pcs);
return retval;
}
static inline int
clone_hard_stack_pte_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *dst_vma,
pmd_t *dst_pmd, pmd_t *src_pmd, e2k_addr_t address,
e2k_addr_t end, long copy_size)
{
pte_t *src_pte;
pte_t *dst_pte;
pte_t *orig_src_pte;
pte_t *orig_dst_pte;
spinlock_t *src_ptl;
spinlock_t *dst_ptl;
int ret = 0;
long rss = 0;
DebugHS("started: start 0x%lx "
"end 0x%lx real size to copy 0x%lx "
"dst_pmd 0x%p == 0x%lx src_pmd 0x%p == 0x%lx\n",
address, end, copy_size,
dst_pmd, pmd_val(*dst_pmd), src_pmd, pmd_val(*src_pmd));
dst_pte = pte_alloc_map_lock(dst, dst_pmd, address, &dst_ptl);
if (!dst_pte) {
DebugHS("could "
"not alloc PTE page for addr 0x%lx\n",
address);
return -ENOMEM;
}
src_pte = pte_offset_map(src_pmd, address);
src_ptl = pte_lockptr(src, src_pmd);
spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
orig_src_pte = src_pte;
orig_dst_pte = dst_pte;
arch_enter_lazy_mmu_mode();
do {
pte_t pte;
struct page *ptepage;
struct page *new_page;
DebugHS("will copy pte 0x%p == "
"0x%lx from address 0x%lx to 0x%lx\n",
src_pte, pte_val(*src_pte), address, end);
pte = *src_pte;
if (pte_none(pte)) {
printk("clone_hard_stack_pte_range() could not be "
"empty pte from addr 0x%lx to 0x%lx\n",
address, (address + PAGE_SIZE) & PAGE_MASK);
ret = -ENOMEM;
break;
}
if (!pte_present(pte)) {
printk("clone_hard_stack_pte_range() pte 0x%p "
"== 0x%lx not present: could not be swaped\n",
src_pte, pte_val(*src_pte));
ret = -ENOMEM;
break;
}
new_page = alloc_page_vma(GFP_ATOMIC, dst_vma, address);
if (!new_page) {
DebugHS("could allocate "
"page for addr 0x%lx\n",
address);
ret = -ENOMEM;
break;
}
/*
* Because we dropped the lock, we should re-check the
* entry, as somebody else could delete or swap it..
*/
if (pte_none(pte)) {
DebugHS("pte 0x%p = "
"0x%lx deleted, process is killing probably\n",
src_pte, pte_val(*src_pte));
ret = -ENOMEM;
break;
}
if (!pte_present(pte)) {
printk("clone_hard_stack_pte_range() pte 0x%p "
"== 0x%lx was swaped, when it should be "
"locked\n",
src_pte, pte_val(*src_pte));
ret = -ENOMEM;
break;
}
ptepage = pte_page(pte);
set_pte_at(dst, address, dst_pte, mk_clone_pte(new_page, pte));
page_add_new_anon_rmap(new_page, dst_vma, address);
++rss;
DebugHS("copies and sets PTE 0x%p "
"to new page 0x%lx (was 0x%lx) for address 0x%lx\n",
dst_pte, pte_val(*dst_pte), pte_val(pte), address);
if (copy_size > 0) {
DebugHS("will copy page "
"contents for address 0x%lx\n",
address);
copy_user_highpage(new_page, ptepage, address, dst_vma);
}
if (copy_size > PAGE_SIZE)
copy_size -= PAGE_SIZE;
else
copy_size = 0;
} while (dst_pte++, src_pte++, address += PAGE_SIZE, address != end);
arch_leave_lazy_mmu_mode();
spin_unlock(src_ptl);
pte_unmap(orig_src_pte);
add_mm_counter(dst, MM_ANONPAGES, rss);
pte_unmap_unlock(orig_dst_pte, dst_ptl);
DebugHS("finished and returns %d\n",
ret);
return ret;
}
static inline int
clone_hard_stack_pmd_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *dst_vma,
pud_t *dst_pud, pud_t *src_pud, e2k_addr_t address,
e2k_addr_t end, long copy_size)
{
e2k_addr_t next;
pmd_t *src_pmd;
pmd_t *dst_pmd;
int ret = 0;
DebugHS("started: start 0x%lx "
"end 0x%lx real size to copy 0x%lx "
"dst_pud 0x%p == 0x%lx src_pud 0x%p == 0x%lx\n",
address, end, copy_size,
dst_pud, pud_val(*dst_pud), src_pud, pud_val(*src_pud));
dst_pmd = pmd_alloc(dst, dst_pud, address);
if (!dst_pud) {
DebugP("could "
"not alloc PMD for addr 0x%lx\n",
address);
return -ENOMEM;
}
src_pmd = pmd_offset(src_pud, address);
do {
DebugHS("will copy pmd 0x%p == "
"0x%lx from address 0x%lx to 0x%lx\n",
src_pmd, pmd_val(*src_pmd), address, end);
next = pmd_addr_end(address, end);
if (pmd_none_or_clear_bad(src_pmd)) {
DebugHS("will skip empty "
"pte range from addr 0x%lx to 0x%lx\n",
address, next);
copy_size -= (next - address);
if (copy_size <= 0)
copy_size = 0;
continue;
}
DebugHS("will clone pte range "
"from address 0x%lx to 0x%lx size to copy 0x%lx\n",
address, next, copy_size);
ret = clone_hard_stack_pte_range(dst, src, dst_vma, dst_pmd,
src_pmd, address, next, copy_size);
if (ret != 0)
break;
} while (dst_pmd++, src_pmd++, address = next, address != end);
DebugHS("finished and returns %d\n",
ret);
return ret;
}
static inline int
clone_hard_stack_pud_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *dst_vma,
pgd_t *dst_pgd, pgd_t *src_pgd, e2k_addr_t address, e2k_addr_t end,
long copy_size)
{
e2k_addr_t next;
pud_t *src_pud;
pud_t *dst_pud;
int ret = 0;
DebugHS("started: start 0x%lx "
"end 0x%lx real size to copy 0x%lx "
"dst_pgd 0x%p == 0x%lx src_pgd 0x%p == 0x%lx\n",
address, end, copy_size,
dst_pgd, pgd_val(*dst_pgd), src_pgd, pgd_val(*src_pgd));
dst_pud = pud_alloc(dst, dst_pgd, address);
if (!dst_pud) {
DebugP("could not "
"alloc PUD for addr 0x%lx\n",
address);
return -ENOMEM;
}
src_pud = pud_offset(src_pgd, address);
do {
DebugHS("will copy pud 0x%p == "
"0x%lx from address 0x%lx to 0x%lx\n",
src_pud, pud_val(*src_pud), address, end);
next = pud_addr_end(address, end);
if (pud_none_or_clear_bad(src_pud)) {
DebugHS("will skip empty "
"or bad pmd range from addr 0x%lx to 0x%lx\n",
address, next);
copy_size -= (next - address);
if (copy_size <= 0)
copy_size = 0;
continue;
}
DebugHS("will clone pmd range "
"from address 0x%lx to 0x%lx size to copy 0x%lx\n",
address, next, copy_size);
ret = clone_hard_stack_pmd_range(dst, src, dst_vma, dst_pud,
src_pud, address, next, copy_size);
if (ret != 0)
break;
} while (dst_pud++, src_pud++, address = next, address != end);
DebugHS("finished and returns %d\n",
ret);
return ret;
}
static inline int
clone_hard_stack_pgd_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *dst_vma,
e2k_addr_t stack_start, e2k_addr_t stack_end, long copy_size)
{
e2k_addr_t address = stack_start;
e2k_addr_t end = stack_end;
e2k_addr_t next;
pgd_t *src_pgd;
pgd_t *dst_pgd;
int ret = 0;
DebugHS("started: start 0x%lx "
"end 0x%lx real size to copy 0x%lx\n",
stack_start, stack_end, copy_size);
dst_pgd = pgd_offset(dst, address);
src_pgd = pgd_offset(src, address);
do {
DebugHS("will copy pgd 0x%p == "
"0x%lx from address 0x%lx and size 0x%lx "
"to pgd 0x%p == 0x%lx\n",
src_pgd, pgd_val(*src_pgd), address,
stack_end - stack_start, dst_pgd, pgd_val(*dst_pgd));
next = pgd_addr_end(address, end);
if (pgd_none_or_clear_bad(src_pgd)) {
DebugHS("will skip bad "
"or none pud range from addr 0x%lx to 0x%lx\n",
address, next);
copy_size -= (next - address);
if (copy_size < 0)
copy_size = 0;
continue;
}
DebugHS("will clone pud range "
"from address 0x%lx to 0x%lx real size 0x%lx\n",
address, next, copy_size);
ret = clone_hard_stack_pud_range(dst, src, dst_vma, dst_pgd,
src_pgd, address, next, copy_size);
if (ret != 0)
break;
} while (dst_pgd++, src_pgd++, address = next, address != end);
DebugHS("finished and returns %d\n",
ret);
return ret;
}
/*
* The function clones user hardware stack from the parent task to new
* (child) task, which has been created before by fork() system call.
*/
static int
clone_user_hard_stack(struct mm_struct *dst, struct mm_struct *src,
e2k_addr_t stack_base, long stack_len, e2k_addr_t stack_end_addr,
unsigned long clone_flags)
{
struct vm_area_struct *src_vma, *dst_vma;
e2k_addr_t stack_end;
int retval = 0;
int vma_count = 0;
DebugHS("will start alloc VMA structure\n");
vma_count = 0;
down_read(&src->mmap_sem);
do {
src_vma = find_vma(src, stack_base);
if (src_vma == NULL) {
if (vma_count == 0) {
DebugP("Could not find VM area\n");
retval = -EINVAL;
goto fail;
}
break;
}
DebugHS("found VMA 0x%p start 0x%lx, end 0x%lx, flags 0x%lx\n",
src_vma, src_vma->vm_start, src_vma->vm_end,
src_vma->vm_flags);
vma_count ++;
if (!(src_vma->vm_flags & VM_DONTCOPY)) {
up_read(&src->mmap_sem);
pr_err("Invalid flags 0x%lx of hardware stack VMA: start 0x%lx, end 0x%lx (should have VM_DONTCOPY flag)\n",
src_vma->vm_flags, src_vma->vm_start,
src_vma->vm_end);
print_mmap(current);
BUG();
}
if (!(src_vma->vm_flags & VM_LOCKED) &&
stack_len != 0) {
up_read(&src->mmap_sem);
pr_err("Invalid flags 0x%lx of hardware stack VMA: start 0x%lx, end 0x%lx, stack (locked) size 0x%lx (should have VM_LOCKED flag)\n",
src_vma->vm_flags, src_vma->vm_start,
src_vma->vm_end, stack_len);
BUG();
}
stack_end = (src_vma->vm_end < stack_end_addr) ?
src_vma->vm_end :
stack_end_addr;
dst_vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
if (!dst_vma) {
DebugHS("Could not allocate VM area\n");
retval = -ENOMEM;
goto fail;
}
DebugHS("copy VMA old 0x%p to new 0x%p\n",
src_vma, dst_vma);
*dst_vma = *src_vma;
if (dst_vma->vm_end > stack_end_addr)
dst_vma->vm_end = stack_end_addr;
if (dst_vma->vm_start < stack_base)
dst_vma->vm_start = stack_base;
dst_vma->vm_mm = dst;
dst_vma->vm_prev = NULL;
dst_vma->vm_next = NULL;
dst_vma->anon_vma = NULL;
INIT_LIST_HEAD(&dst_vma->anon_vma_chain);
DebugHS("src VMA 0x%p anon-vma 0x%p == 0x%p\n",
src_vma, &src_vma->anon_vma, src_vma->anon_vma);
retval = vma_dup_policy(src_vma, dst_vma);
if (retval) {
DebugP("mpol_copy() failed and returns error %d\n",
retval);
goto fail_free_vma;
}
retval = insert_vm_struct(dst, dst_vma);
if (retval) {
goto fail_free_mempolicy;
break;
}
vm_stat_account(dst, src_vma->vm_flags, src_vma->vm_file,
vma_pages(src_vma));
retval = anon_vma_prepare(dst_vma);
if (retval)
break;
DebugHS("dst VMA 0x%p anon-vma 0x%p == 0x%p\n",
dst_vma, &(dst_vma->anon_vma), dst_vma->anon_vma);
if ((src_vma->vm_flags & VM_LOCKED) &&
(src_vma->vm_flags & (VM_READ| VM_WRITE | VM_EXEC))) {
DebugHS("will copy from parent stack 0x%lx to child user, size 0x%lx byte(s)\n",
stack_base, stack_len);
retval = clone_hard_stack_pgd_range(dst, src,
dst_vma, stack_base, stack_end,
stack_len);
DebugHS("clone_hard_stack_pgd_range returned %d\n",
retval);
if (retval)
break;
}
DebugHS("succeeded\n");
stack_len -= (stack_end - stack_base);
if (stack_len < 0)
stack_len = 0;
stack_base = stack_end;
} while (stack_base < stack_end_addr);
up_read(&src->mmap_sem);
return retval;
fail_free_mempolicy:
mpol_put(vma_policy(dst_vma));
fail_free_vma:
kmem_cache_free(vm_area_cachep, dst_vma);
fail:
up_read(&src->mmap_sem);
DebugP("failed and returns error %d\n",
retval);
return retval;
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static inline int __fix_return_values(
unsigned long dst, unsigned long size, unsigned long delta)
{
int last_not_copied_index = -1;
int index;
for (index = current->curr_ret_stack; index >= 0; index--) {
unsigned long fp = current->ret_stack[index].fp + delta;
e2k_mem_crs_t *frame;
if (fp >= dst + size) {
last_not_copied_index = index;
DebugFTRACE("%d: skipping fp 0x%lx, limit 0x%lx - 0x%lx (entry %pS)\n",
current->pid, fp, dst, dst + size,
current->ret_stack[index].ret);
continue;
}
if (fp < dst) {
DebugFTRACE("%d: fp 0x%lx, limit 0x%lx - 0x%lx (did not restore %pS at original fp %lx)\n",
current->pid, fp, dst, dst + size,
current->ret_stack[index].ret,
fp - delta);
break;
}
frame = (e2k_mem_crs_t *) fp;
DebugFTRACE("%d: replacing %pS with %pS at 0x%lx (user: %s)\n",
current->pid, AW(frame->cr0_hi),
current->ret_stack[index].ret, (u64) frame,
current->mm ? "true" : "false");
if (current->mm)
__put_user(current->ret_stack[index].ret,
&AW(frame->cr0_hi));
else
AW(frame->cr0_hi) = current->ret_stack[index].ret;
--index;
}
return last_not_copied_index;
}
static void fix_return_values_in_chain_stack(struct mm_struct *mm,
unsigned long dst, unsigned long src, unsigned long size,
e2k_mem_crs_t *crs)
{
unsigned long flags;
struct mm_struct *oldmm;
int was_mm_switch = 0, index, cpu;
if (current->curr_ret_stack < 0 || !current->ret_stack)
/* Tracing buffer is empty or the graph tracing never
* was enabled for the current task, nothing to do here... */
return;
DebugFTRACE("%d: ftrace: fixing chain stack\n", current->pid);
oldmm = current->active_mm;
if (mm != oldmm) {
raw_all_irq_save(flags);
cpu = raw_smp_processor_id();
current->active_mm = mm;
do_switch_mm(oldmm, mm, cpu);
was_mm_switch = 1;
}
index = __fix_return_values(dst, size, dst - src);
if (was_mm_switch) {
current->active_mm = oldmm;
do_switch_mm(mm, oldmm, cpu);
raw_all_irq_restore(flags);
}
/*
* Last copied chain stack frame is stored in registers,
* check if it should be restored too.
*/
if (index >= 0 && current->ret_stack[index].fp == src + size) {
DebugFTRACE("%d: replacing %pS with %pS at 0x%lx in registers\n",
current->pid, AW(crs->cr0_hi),
current->ret_stack[index].ret, dst + size);
AW(crs->cr0_hi) = current->ret_stack[index].ret;
}
}
#endif
/*
* The function clones all user hardware stacks(PS & PCS)
* from the parrent task to the new (child) task.
*/
static inline int
do_clone_all_user_hard_stacks(struct task_struct *new_task,
e2k_addr_t ps_base, long ps_len, e2k_addr_t ps_end_addr,
e2k_addr_t pcs_base, long pcs_len, e2k_addr_t pcs_end_addr,
unsigned long clone_flags, e2k_mem_crs_t *new_crs)
{
struct mm_struct *mm_from = current->mm;
struct mm_struct *mm_to = new_task->mm;
int retval;
DebugUS("started to clone stacks from task 0x%p to task 0x%p\n",
current, new_task);
DebugUS("will copy procedure stack from base 0x%lx, size 0x%lx, end addr 0x%lx\n",
ps_base, ps_len, ps_end_addr);
retval = clone_user_hard_stack(mm_to, mm_from,
ps_base, ps_len, ps_end_addr, clone_flags);
DebugUS("clone_user_hard_stack() returned %d\n", retval);
if (retval)
return retval;
DebugUS("will copy chain stack from base 0x%lx, size 0x%lx, end addr 0x%lx\n",
pcs_base, pcs_len, pcs_end_addr);
retval = clone_user_hard_stack(mm_to, mm_from,
pcs_base, pcs_len, pcs_end_addr, clone_flags);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
if (!retval)
fix_return_values_in_chain_stack(mm_to,
pcs_base, pcs_base, pcs_len, new_crs);
#endif
DebugUS("finished and returns %d\n", retval);
return retval;
}
/*
* The function creates user hardware stacks(PS & PCS) excluding or
* including kernel part of the hardware stacks of current task
*/
static int
create_user_hard_stacks(thread_info_t *thread_info, e2k_stacks_t *stacks,
e2k_size_t user_psp_size, e2k_size_t user_psp_init_size,
e2k_size_t user_pcsp_size, e2k_size_t user_pcsp_init_size)
{
struct hw_stack_area *user_psp_stk;
struct hw_stack_area *user_pcsp_stk;
void *user_psp_stk_cont;
void *user_pcsp_stk_cont;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
DebugUS("started\n");
user_psp_size = get_max_psp_size(user_psp_size);
user_pcsp_size = get_max_pcsp_size(user_pcsp_size);
if (user_psp_init_size > user_psp_size) {
user_psp_init_size = user_psp_size;
}
DebugUS("will allocate user Procedure stack\n");
if (UHWS_PSEUDO_MODE) {
user_psp_stk = alloc_user_p_stack(user_psp_size, 0,
user_psp_init_size);
if (!user_psp_stk) {
DebugHS("Could not allocate user Procedure stack\n");
return -ENOMEM;
}
list_add_tail(&user_psp_stk->list_entry, &thread_info->ps_list);
thread_info->cur_ps = user_psp_stk;
DebugUS("user Procedure stack area 0x%p was added to user Procedure stack areas list\n",
user_psp_stk);
} else {
user_psp_stk_cont = alloc_user_p_stack_cont(user_psp_size,
user_psp_init_size);
if (!user_psp_stk_cont) {
DebugHS("Could not allocate user Procedure stack\n");
return -ENOMEM;
}
}
DebugUS("allocated user Procedure stack at 0x%p, size 0x%lx, init size 0x%lx, kernel part size 0x%lx\n",
(UHWS_PSEUDO_MODE) ? user_psp_stk->base : user_psp_stk_cont,
user_psp_size, user_psp_init_size, KERNEL_P_STACK_SIZE);
DebugUS("will allocate user Procedure Chain stack\n");
if (UHWS_PSEUDO_MODE) {
user_pcsp_stk = alloc_user_pc_stack(user_pcsp_size, 0,
user_pcsp_init_size);
if (!user_pcsp_stk) {
DebugHS("Could not allocate user Procedure Chain stack\n");
free_user_p_stack(user_psp_stk, true);
return -ENOMEM;
}
list_add_tail(&user_pcsp_stk->list_entry,
&thread_info->pcs_list);
thread_info->cur_pcs = user_pcsp_stk;
DebugUS("user Procedure Chain stack area 0x%p was added to user Procedure stack areas list\n",
user_pcsp_stk);
} else {
user_pcsp_stk_cont = alloc_user_pc_stack_cont(user_pcsp_size,
user_pcsp_init_size);
if (!user_pcsp_stk_cont) {
DebugHS("Could not allocate user Procedure Chain stack\n");
free_user_p_stack_cont(user_psp_stk_cont,
user_psp_size, 0,
user_psp_init_size);
return -ENOMEM;
}
}
DebugUS("allocated user Procedure Chain stack at 0x%p, size 0x%lx, init size 0x%lx, kernel part size 0x%lx\n",
(UHWS_PSEUDO_MODE) ? user_pcsp_stk->base : user_pcsp_stk_cont,
user_pcsp_size, user_pcsp_init_size, KERNEL_PC_STACK_SIZE);
psp_lo.PSP_lo_half = 0;
psp_hi.PSP_hi_half = 0;
if (UHWS_PSEUDO_MODE) {
AS_STRUCT(psp_lo).base = (e2k_addr_t)user_psp_stk->base;
AS_STRUCT(psp_hi).size = user_psp_stk->top;
} else {
AS_STRUCT(psp_lo).base = (e2k_addr_t)user_psp_stk_cont;
AS_STRUCT(psp_hi).size = user_psp_init_size;
thread_info->ps_base = user_psp_stk_cont;
thread_info->ps_size = user_psp_size;
thread_info->ps_offset = 0;
thread_info->ps_top = user_psp_init_size;
}
stacks->psp_lo = psp_lo;
stacks->psp_hi = psp_hi;
pcsp_lo.PCSP_lo_half = 0;
pcsp_hi.PCSP_hi_half = 0;
if (UHWS_PSEUDO_MODE) {
AS_STRUCT(pcsp_lo).base = (e2k_addr_t)user_pcsp_stk->base;
AS_STRUCT(pcsp_hi).size = user_pcsp_stk->top;
} else {
AS_STRUCT(pcsp_lo).base = (e2k_addr_t)user_pcsp_stk_cont;
AS_STRUCT(pcsp_hi).size = user_pcsp_init_size;
thread_info->pcs_base = user_pcsp_stk_cont;
thread_info->pcs_size = user_pcsp_size;
thread_info->pcs_offset = 0;
thread_info->pcs_top = user_pcsp_init_size;
}
stacks->pcsp_lo = pcsp_lo;
stacks->pcsp_hi = pcsp_hi;
DebugUS("succeeded\n");
return 0;
}
/*
* The function creates all user hardware stacks(PS & PCS) including and
* kernel part of the hardware stacks of current task
*/
static int
create_all_user_hard_stacks(struct thread_info *ti, e2k_stacks_t *stacks)
{
int ret;
DebugUS("will allocate user Procedure and Chain stacks\n");
ret = create_user_hard_stacks(ti, stacks,
(UHWS_PSEUDO_MODE) ?
USER_P_STACK_AREA_SIZE : USER_P_STACK_SIZE,
USER_P_STACK_INIT_SIZE,
(UHWS_PSEUDO_MODE) ?
USER_PC_STACK_AREA_SIZE : USER_PC_STACK_SIZE,
USER_PC_STACK_INIT_SIZE);
DebugUS("returned %d\n", ret);
return ret;
}
void show_regs(struct pt_regs * regs)
{
DebugP("show_regs entered.\n");
print_task_pt_regs(regs);
DebugP("show_regs exited.\n");
}
static int check_wchan(struct task_struct *p, e2k_mem_crs_t *frame,
unsigned long frame_address, void *arg1, void *arg2, void *arg3)
{
unsigned long *p_ip = arg1;
unsigned long ip;
ip = AS(frame->cr0_hi).ip << 3;
if (!in_sched_functions(ip)) {
*p_ip = ip;
return 1;
}
return 0;
}
unsigned long get_wchan(struct task_struct *p)
{
unsigned long ip = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
parse_chain_stack(p, check_wchan, &ip, NULL, NULL);
return ip;
}
/**
* free_hw_stack_mappings - free user hw stacks which were mapped to kernel
* @ti: the task's thread_info
*/
static void free_hw_stack_mappings(struct thread_info *ti)
{
if (ti->mapped_p_stack) {
free_vm_area(ti->mapped_p_stack);
ti->mapped_p_stack = NULL;
}
if (ti->mapped_pc_stack) {
free_vm_area(ti->mapped_pc_stack);
ti->mapped_pc_stack = NULL;
}
}
/**
* unmap_hw_stack_mappings - unmap user hw stacks mappings which were mapped to
* kernel
* @ti: the task's thread_info
*/
static void unmap_hw_stack_mappings(struct thread_info *ti)
{
/*
* Now we don't need the old mappings. Cache flush is not needed on e2k,
* and TLB flush is done on as-needed basis. This is possible because
* normally hardware stacks are accessed only by their owner, and in
* exceptional cases (parse_chain_stack(), print_stack()) we'll do a
* manual TLB flush.
*/
all_irq_disable();
bitmap_fill(ti->need_tlb_flush, NR_CPUS);
__clear_bit(smp_processor_id(), ti->need_tlb_flush);
__flush_tlb_all();
all_irq_enable();
unmap_kernel_range_noflush((u64) ti->mapped_pc_stack->addr,
KERNEL_PC_STACK_SIZE);
unmap_kernel_range_noflush((u64) ti->mapped_p_stack->addr,
KERNEL_P_STACK_SIZE);
}
/**
* free_hw_stack_pages - free user hw stacks pages which were mapped to kernel
* @ti: the task's thread_info
*/
static void free_hw_stack_pages(struct thread_info *ti)
{
int i;
clear_ti_status_flag(ti,
TS_MAPPED_HW_STACKS | TS_MAPPED_HW_STACKS_INVALID);
for (i = 0; i < KERNEL_PC_STACK_PAGES; i++) {
struct page *page = ti->mapped_pc_pages[i];
put_page(page);
}
for (i = 0; i < KERNEL_P_STACK_PAGES; i++) {
struct page *page = ti->mapped_p_pages[i];
put_page(page);
}
}
enum {
FKE_FIRST_FRAME,
FKE_SEARCHING,
FKE_FOUND
};
static int __find_kernel_entry(struct task_struct *task,
e2k_mem_crs_t *frame, unsigned long frame_address,
void *arg1, void *arg2, void *arg3)
{
unsigned long *cs = arg1, *ps = arg2;
int *state = arg3;
/*
* Skip the first frame which contains find_kernel_entry()'s ip
* since it has already been accounted for in *cs and *ps.
*/
if (*state == FKE_FIRST_FRAME) {
*state = FKE_SEARCHING;
return 0;
}
if (!AS(frame->cr1_lo).pm) {
/*
* Found!
*/
*state = FKE_FOUND;
return 1;
}
*cs -= SZ_OF_CR;
*ps -= AS(frame->cr1_lo).wbs * EXT_4_NR_SZ;
return 0;
}
static int find_kernel_entry(unsigned long *cs, unsigned long *ps)
{
e2k_pshtp_t pshtp;
e2k_pcshtp_t pcshtp;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
unsigned long flags;
int state;
raw_all_irq_save(flags);
psp_lo = READ_PSP_LO_REG();
psp_hi = READ_PSP_HI_REG();
pcsp_lo = READ_PCSP_LO_REG();
pcsp_hi = READ_PCSP_HI_REG();
pshtp = READ_PSHTP_REG();
pcshtp = READ_PCSHTP_REG();
raw_all_irq_restore(flags);
*cs = AS(pcsp_lo).base + AS(pcsp_hi).ind + PCSHTP_SIGN_EXTEND(pcshtp);
*ps = AS(psp_lo).base + AS(psp_hi).ind + GET_PSHTP_INDEX(pshtp);
state = FKE_FIRST_FRAME;
parse_chain_stack(NULL, __find_kernel_entry, cs, ps, &state);
return (state == FKE_FOUND) ? 0 : -ESRCH;
}
static long get_hw_stack_pages(unsigned long start, unsigned long nr_pages,
struct page **pages)
{
long i;
struct mm_struct *mm = current->mm;
unsigned int foll_flags = FOLL_GET | FOLL_WRITE |
FOLL_TOUCH | FOLL_NUMA;
down_read(&mm->mmap_sem);
i = 0;
do {
struct vm_area_struct *vma;
vma = find_vma(mm, start);
BUG_ON(!vma || (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
!(VM_WRITE & vma->vm_flags));
do {
struct page *page;
page = follow_page(vma, start, foll_flags);
BUG_ON(!page || IS_ERR(page));
pages[i] = page;
flush_anon_page(vma, page, start);
flush_dcache_page(page);
++i;
start += PAGE_SIZE;
--nr_pages;
} while (nr_pages && start < vma->vm_end);
} while (nr_pages);
up_read(&mm->mmap_sem);
return i;
}
void switch_to_kernel_hardware_stacks()
{
struct thread_info *ti = current_thread_info();
struct page **pages;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
unsigned long flags, cs, ps;
int ret;
if (test_ts_flag(TS_MAPPED_HW_STACKS_INVALID)) {
BUG_ON(!test_ts_flag(TS_MAPPED_HW_STACKS));
unmap_hw_stack_mappings(ti);
free_hw_stack_pages(ti);
}
if (test_ts_flag(TS_MAPPED_HW_STACKS))
return;
if (AS(READ_PCSP_LO_REG()).base >= PAGE_OFFSET)
/* On kernel stacks already, which means
* that we failed somewhere in do_sys_execve()
* before switching to user stacks. */
return;
ret = find_kernel_entry(&cs, &ps);
if (ret) {
/* makecontext_trampoline()->do_exit() case */
cs = AS(READ_PCSP_LO_REG()).base;
ps = AS(READ_PSP_LO_REG()).base;
}
cs = round_down(cs, PAGE_SIZE);
ps = round_down(ps, PAGE_SIZE);
/*
* Get the pages which hold current kernel stacks.
* They _must_ be present so we panic() on error.
*/
ret = get_hw_stack_pages(cs, KERNEL_PC_STACK_PAGES,
ti->mapped_pc_pages);
if (unlikely(ret != KERNEL_PC_STACK_PAGES))
panic("Could not remap chain stack from 0x%lx (ret %d)\n",
cs, ret);
ret = get_hw_stack_pages(ps, KERNEL_P_STACK_PAGES,
ti->mapped_p_pages);
if (unlikely(ret != KERNEL_P_STACK_PAGES))
panic("Could not remap procedure stack from 0x%lx (ret %d)\n",
ps, ret);
/*
* Actually map the pages...
*/
pages = ti->mapped_pc_pages;
ret = map_vm_area(ti->mapped_pc_stack, PAGE_KERNEL_PCS, &pages);
BUG_ON(ret);
pages = ti->mapped_p_pages;
ret = map_vm_area(ti->mapped_p_stack, PAGE_KERNEL_PS, &pages);
BUG_ON(ret);
set_ts_flag(TS_MAPPED_HW_STACKS);
/*
* ... and switch to the new location.
*/
raw_all_irq_save(flags);
E2K_FLUSHCPU;
psp_lo = READ_PSP_LO_REG();
psp_hi = READ_PSP_HI_REG();
pcsp_lo = READ_PCSP_LO_REG();
pcsp_hi = READ_PCSP_HI_REG();
AS(pcsp_hi).ind -= cs - AS(pcsp_lo).base;
AS(psp_hi).ind -= ps - AS(psp_lo).base;
AS(pcsp_lo).base = (unsigned long) ti->mapped_pc_stack->addr;
AS(psp_lo).base = (unsigned long) ti->mapped_p_stack->addr;
AS(pcsp_hi).size = KERNEL_PC_STACK_SIZE;
AS(psp_hi).size = KERNEL_P_STACK_SIZE;
WRITE_PSP_LO_REG(psp_lo);
WRITE_PSP_HI_REG(psp_hi);
WRITE_PCSP_LO_REG(pcsp_lo);
WRITE_PCSP_HI_REG(pcsp_hi);
raw_all_irq_restore(flags);
DebugEX("new chain stack base 0x%llx, size 0x%x, ind 0x%x\n",
AS(pcsp_lo).base, AS(pcsp_hi).size, AS(pcsp_hi).ind);
DebugEX("new procedure stack base 0x%llx, size 0x%x, ind 0x%x\n",
AS(psp_lo).base, AS(psp_hi).size, AS(psp_hi).ind);
#if defined CONFIG_FUNCTION_GRAPH_TRACER
if (current->curr_ret_stack >= 0) {
unsigned long pc_delta =
(unsigned long) ti->mapped_pc_stack->addr - cs;
int i;
DebugFTRACE("%d: fixing stack in do_exit\n", current->pid);
for (i = current->curr_ret_stack; i >= 0; i--) {
DebugFTRACE("%d: correcting entry %pS fp %lx->%lx\n",
current->pid, current->ret_stack[i].ret,
current->ret_stack[i].fp,
current->ret_stack[i].fp + pc_delta);
current->ret_stack[i].fp += pc_delta;
}
}
#endif
}
static int free_user_hardware_stacks(void)
{
thread_info_t *ti = current_thread_info();
if (UHWS_PSEUDO_MODE) {
free_user_old_pc_stack_areas(&ti->old_u_pcs_list);
DebugEX("freed user old PCS list head 0x%p\n",
&ti->old_u_pcs_list);
}
if (atomic_read(&current->mm->mm_users) <= 1) {
DebugEX("last thread: do not free stacks - mmput will release all mm\n");
/*
* Do not free address space, free descriptors only.
*/
if (UHWS_PSEUDO_MODE) {
struct hw_stack_area *user_p_stack, *user_pc_stack, *n;
list_for_each_entry_safe(user_p_stack, n,
&ti->ps_list, list_entry) {
kfree(user_p_stack);
}
INIT_LIST_HEAD(&ti->ps_list);
ti->cur_ps = NULL;
list_for_each_entry_safe(user_pc_stack, n,
&ti->pcs_list, list_entry) {
kfree(user_pc_stack);
}
INIT_LIST_HEAD(&ti->pcs_list);
ti->cur_pcs = NULL;
}
return 0;
}
DebugEX("thread #%d do free stacks\n",
atomic_read(&current->mm->mm_users));
BUG_ON((unsigned long) GET_PS_BASE(ti) >= TASK_SIZE ||
(unsigned long) GET_PCS_BASE(ti) >= TASK_SIZE);
if (UHWS_PSEUDO_MODE) {
free_user_p_stack_areas(&ti->ps_list);
ti->cur_ps = NULL;
DebugEX("freed user PS list head 0x%p, kernel part size 0x%lx\n",
&ti->ps_list, KERNEL_P_STACK_SIZE);
free_user_pc_stack_areas(&ti->pcs_list);
ti->cur_pcs = NULL;
DebugEX("freed user PCS list head 0x%p, kernel part size 0x%lx\n",
&ti->pcs_list, KERNEL_PC_STACK_SIZE);
} else {
if (ti->ps_base) {
free_user_p_stack_cont(ti->ps_base, ti->ps_size,
ti->ps_offset, ti->ps_size);
ti->ps_base = NULL;
DebugEX("freed user PS from base 0x%p, size 0x%lx, kernel part size 0x%lx\n",
ti->ps_base, ti->ps_size,
KERNEL_P_STACK_SIZE);
}
if (ti->pcs_base) {
free_user_pc_stack_cont(ti->pcs_base, ti->pcs_size,
ti->pcs_offset, ti->pcs_top);
ti->pcs_base = NULL;
DebugEX("freed user PCS from base 0x%p, size 0x%lx, kernel part size 0x%lx\n",
ti->pcs_base, ti->pcs_size,
KERNEL_PC_STACK_SIZE);
}
}
return 0;
}
static inline void
goto_new_user_hard_stk(e2k_stacks_t *stacks)
{
unsigned long flags;
DebugEX("will switch stacks\n");
raw_all_irq_save(flags);
/*
* Wait for SPILL/FILL to issue all memory accesses
*/
E2K_FLUSH_WAIT;
/*
* Optimization to do not flush chain stack.
*
* Old stacks are not needed anymore, do not flush procedure
* registers and chain registers - only strip sizes
*/
STRIP_PSHTP_WINDOW();
STRIP_PCSHTP_WINDOW();
/*
* There might be a FILL operation still going right now.
* Wait for it's completion before going further - otherwise
* the next FILL on the new PSP/PCSP registers will race
* with the previous one.
*
* The first and the second FILL operations will use different
* addresses because we will change PSP/PCSP registers, and
* thus loads/stores from these two FILLs can race with each
* other leading to bad register file (containing values from
* both stacks).
*/
E2K_WAIT(_ma_c);
/*
* Since we are switching to user stacks their sizes
* have been stripped already, so use RAW_* writes.
*/
RAW_WRITE_PSP_REG(stacks->psp_hi, stacks->psp_lo);
RAW_WRITE_PCSP_REG(stacks->pcsp_hi, stacks->pcsp_lo);
/*
* We have switched to user stacks which are not expanded.
*/
clear_ts_flag(TS_HW_STACKS_EXPANDED);
e2k_reset_sge();
raw_all_irq_restore(flags);
}
#define printk printk_fixed_args
#define panic panic_fixed_args
notrace noinline __interrupt
void do_switch_to_user_func(start_fn start_func)
{
e2k_cr0_hi_t cr_ip;
e2k_cr1_lo_t cr_wd_psr;
e2k_cr1_hi_t cr_ussz;
e2k_cuir_t cuir = {{ 0 }};
e2k_psr_t psr;
#ifdef CONFIG_PROTECTED_MODE
e2k_usd_lo_t usd_lo;
e2k_usd_hi_t usd_hi;
e2k_pusd_lo_t pusd_lo;
e2k_pusd_hi_t pusd_hi;
u64 u_sbr;
#endif /* CONFIG_PROTECTED_MODE */
DebugCU(" func = 0x%016lx\n", *((long *)start_func));
DebugP("entered: func 0x%p\n", start_func);
DebugSPRs("start");
AS_WORD(cr_ip) = E2K_GET_DSREG_NV(cr0.hi);
AS_WORD(cr_wd_psr) = E2K_GET_DSREG_NV(cr1.lo);
AS_WORD(cr_ussz) = E2K_GET_DSREG_NV(cr1.hi);
/*
* Go to down to sys_exec() procedure chain stack using PSCP info
* And get 'ussz' field of 'sys_exec()' function to restore
* user stack state before 'switch_to_user_func()' call
*/
AS_STRUCT(cr_ussz).ussz = current_thread_info()->u_stk_sz >> 4;
AS_WORD(psr) = 0;
AS_STRUCT(psr).sge = 1;
AS_STRUCT(psr).ie = 1; /* sti(); */
AS_STRUCT(psr).nmie = 1; /* nmi enable */
AS_STRUCT(psr).pm = 0; /* user mode */
AS_STRUCT(cr_wd_psr).psr = AS_WORD(psr);
AS_STRUCT(cr_ip).ip = (u64)start_func >> 3; /* start user IP */
/*
* Force CUD/GD/TSD update by the values stored in CUTE
* Entry #1 - for both 32bit and protected mode
*/
if ((current->thread.flags & E2K_FLAG_32BIT) ||
(current->thread.flags & E2K_FLAG_PROTECTED_MODE)) {
AS_STRUCT(cuir).index = USER_CODES_32_INDEX;
} else {
AS_STRUCT(cuir).index = USER_CODES_START_INDEX;
}
AS_STRUCT(cr_wd_psr).cuir = AS_WORD(cuir);
E2K_SET_DSREG_NV_NOIRQ(cr1.lo, AS_WORD(cr_wd_psr));
E2K_SET_DSREG_NV_NOIRQ(cr1.hi, AS_WORD(cr_ussz));
E2K_SET_DSREG_NV_NOIRQ(cr0.hi, AS_WORD(cr_ip));
#ifdef CONFIG_CLI_CHECK_TIME
sti_return();
#endif
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE) {
u_sbr = READ_USBR_REG_VALUE();
usd_hi = READ_USD_HI_REG();
usd_lo = READ_USD_LO_REG();
u_sbr = AS_STRUCT(usd_lo).base &
~E2K_PROTECTED_STACK_BASE_MASK;
WRITE_SBR_REG_VALUE(u_sbr);
AW(pusd_lo) = 0;
AW(pusd_hi) = 0;
AS_STRUCT(pusd_lo).base = AS_STRUCT(usd_lo).base &
E2K_PROTECTED_STACK_BASE_MASK;
AS_STRUCT(pusd_lo).base &= ~E2K_ALIGN_PUSTACK_MASK;
AS_STRUCT(pusd_lo).p = 1;
AS_STRUCT(pusd_hi).size = AS_STRUCT(usd_hi).size &
~E2K_ALIGN_PUSTACK_MASK;
AS_STRUCT(pusd_lo).psl = 2;
AS_STRUCT(pusd_lo).rw = RW_ENABLE;
WRITE_PUSD_REG(pusd_hi, pusd_lo);
ENABLE_US_CLW();
} else {
DISABLE_US_CLW();
}
#endif /* CONFIG_PROTECTED_MODE */
/*
* User function will be executed under PSR interrupts control
* and kernel should return interrupts mask control to PSR register
* (if it needs)
* Set UPSR register in the initial state for user process
*/
SET_USER_UPSR();
#ifdef CONFIG_KERNEL_TIMES_ACCOUNT
if (current_thread_info()->pt_regs) {
E2K_SAVE_CLOCK_REG(current_thread_info()->pt_regs->
scall_times->scall_done);
E2K_SAVE_CLOCK_REG(current_thread_info()->pt_regs->
scall_times->end);
}
#endif /* CONFIG_KERNEL_TIMES_ACCOUNT */
/* Prevent kernel pointers leakage to userspace.
* NOTE: this must be executed last. */
E2K_SET_DGREG_NV(16, 0);
E2K_SET_DGREG_NV(17, 0);
E2K_SET_DGREG_NV(18, 0);
E2K_SET_DGREG_NV(19, 0);
/* Prevent kernel information leakage */
#if E2K_MAXSR != 112
# error Must clear all registers here
#endif
#ifndef CONFIG_E2S_CPU_RF_BUG
E2K_CLEAR_RF_112();
#endif
}
#undef printk
#undef panic
extern void switch_to_user_func(long dummy, start_fn start_func);
#ifdef CONFIG_PROTECTED_MODE
extern void protected_switch_to_user_func(long r0, long r1,
start_fn start_func);
#endif
int create_cut_entry(int tcount,
unsigned long code_base, unsigned code_sz,
unsigned long glob_base, unsigned glob_sz)
{
struct mm_struct *mm = current->mm;
register e2k_cute_t *cute_p; /* register for workaround against */
/* gcc bug */
unsigned int cui;
#ifdef CONFIG_PROTECTED_MODE
int retval;
#endif
cui = atomic_add_return(1, &mm->context.cur_cui) - 1;
cute_p = (e2k_cute_t *) USER_CUT_AREA_BASE + cui;
DebugCU("cui = %d; tct = %d; code = 0x%lx: 0x%x; "
"data = 0x%lx : 0x%x\n", cui, tcount, code_base, code_sz,
glob_base, glob_sz);
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE) {
DebugCU("e2k_set_vmm_cui called for cui = %d; code 0x%lx : 0x%lx\n",
cui, code_base, code_base + code_sz);
retval = e2k_set_vmm_cui(mm, cui, code_base,
code_base + code_sz);
if (retval)
return retval;
}
#endif
//TODO 3.10 cute_p is user address, read it carefully
CUTE_CUD_BASE(cute_p) = code_base;
CUTE_CUD_SIZE(cute_p) =
ALIGN_MASK(code_sz,E2K_ALIGN_CODES_MASK);
CUTE_CUD_C(cute_p) = CUD_CFLAG_SET;
CUTE_GD_BASE(cute_p) = glob_base;
CUTE_GD_SIZE(cute_p) =
ALIGN_MASK(glob_sz, E2K_ALIGN_GLOBALS_MASK);
CUTE_TSD_BASE(cute_p) = atomic_add_return(tcount, &mm->context.tstart) -
tcount;
CUTE_TSD_SIZE(cute_p) = tcount;
return 0;
}
/**
* alloc_hw_stack_mappings - preallocates memory for hw stacks
* mappings to kernel
* @new_ti: the new task's thread_info
*
* We do not want to allocate memory in do_exit() path
* (in case an OOM happens) so we set everything ready
* beforehand.
*/
static int alloc_hw_stack_mappings(struct thread_info *new_ti)
{
struct vm_struct *p_area, *pc_area;
p_area = alloc_vm_area(KERNEL_P_STACK_SIZE, NULL);
if (!p_area)
goto out;
pc_area = alloc_vm_area(KERNEL_PC_STACK_SIZE, NULL);
if (!pc_area)
goto out_free_p;
new_ti->mapped_p_stack = p_area;
new_ti->mapped_pc_stack = pc_area;
return 0;
out_free_p:
free_vm_area(p_area);
out:
return -ENOMEM;
}
/**
* do_sys_execve - switch to the thread's context (stacks, CUT, etc)
* @entry: user function to call
* @sp: user stack's top
* @kernel: called by a kernel thread
*
* This is always called as the last step when exec()'ing a user binary.
*/
static long do_sys_execve(unsigned long entry, unsigned long sp, int kernel)
{
thread_info_t *const ti = current_thread_info();
struct mm_struct *mm = current->mm;
start_fn start;
int error;
struct hw_stack_area *k_psp_area = NULL, *k_pcsp_area = NULL;
void *k_psp_stk = NULL, *k_pcsp_stk = NULL;
e2k_stacks_t stacks;
e2k_usd_lo_t usd_lo;
e2k_usd_hi_t usd_hi;
e2k_addr_t stack_top;
u64 u_stk_base, u_stk_sz;
e2k_cutd_t cutd;
e2k_size_t size;
e2k_cute_t *cute_p;
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE)
sp &= ~E2K_ALIGN_PUSTACK_MASK;
else
#endif
sp &= ~E2K_ALIGN_USTACK_MASK;
start = (start_fn) entry;
u_stk_base = (u64) get_user_main_c_stack((e2k_addr_t) sp, &stack_top);
if (u_stk_base == 0) {
error = -ENOMEM;
DebugEX("is terminated: get_user_main_c_stack() failed and returned error %d\n",
error);
goto fatal_error;
}
u_stk_sz = sp - u_stk_base;
usd_hi = READ_USD_HI_REG();
usd_lo = READ_USD_LO_REG();
AS_STRUCT(usd_lo).base = sp;
AS_STRUCT(usd_lo).p = 0;
AS_STRUCT(usd_hi).size = u_stk_sz;
DebugEX("stack base 0x%lx size 0x%lx top 0x%lx\n",
u_stk_base, u_stk_sz, stack_top);
/*
* Free stack areas lists before switching to the new stacks.
* For user threads this is done by deactivate_mm().
*/
if (kernel) {
if (UHWS_PSEUDO_MODE) {
BUG_ON((unsigned long) ti->cur_ps->base < TASK_SIZE ||
(unsigned long) ti->cur_pcs->base < TASK_SIZE);
BUG_ON(!list_is_singular(&ti->ps_list) ||
!list_is_singular(&ti->pcs_list));
list_del(&ti->cur_ps->list_entry);
k_psp_area = ti->cur_ps;
ti->cur_ps = NULL;
list_del(&ti->cur_pcs->list_entry);
k_pcsp_area = ti->cur_pcs;
ti->cur_pcs = NULL;
} else {
BUG_ON((unsigned long) ti->ps_base < TASK_SIZE ||
(unsigned long) ti->pcs_base < TASK_SIZE);
k_psp_stk = ti->ps_base;
ti->ps_base = NULL;
k_pcsp_stk = ti->pcs_base;
ti->pcs_base = NULL;
}
} else {
if (UHWS_PSEUDO_MODE) {
BUG_ON(!list_empty(&ti->ps_list) ||
!list_empty(&ti->pcs_list));
BUG_ON(!list_empty(&ti->old_u_pcs_list));
BUG_ON(ti->cur_ps || ti->cur_pcs);
}
}
if (kernel) {
BUG_ON(ti->mapped_p_stack || ti->mapped_pc_stack);
error = alloc_hw_stack_mappings(ti);
if (error) {
DebugEX("Could not allocate stacks mappings\n");
goto fatal_error;
}
}
error = create_all_user_hard_stacks(ti, &stacks);
if (error) {
DebugEX("Could not create user hardware stacks\n");
goto fatal_error;
}
/*
* Here we go with CUT handling.
*/
/* Allocate memory for CUT table (2Mb) */
cute_p = (e2k_cute_t *) USER_CUT_AREA_BASE;
size = (e2k_size_t) USER_CUT_AREA_SIZE;
if (alloc_cu_table((e2k_addr_t) cute_p, size)) {
DebugEX("Can't create CU table.\n");
error = -ENOMEM;
goto fatal_error;
}
/*
* Fill CUT entry #0 with zeroes.
* #0 can not be legally used in both 32bit and 3p modes.
*/
CUTE_CUD_BASE(cute_p) = 0;
CUTE_CUD_SIZE(cute_p) = 0;
CUTE_CUD_C (cute_p) = 0;
CUTE_GD_BASE(cute_p) = 0;
CUTE_GD_SIZE(cute_p) = 0;
CUTE_TSD_BASE(cute_p) = 0;
CUTE_TSD_SIZE(cute_p) = 0;
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE) {
DebugEX("create_cut_entry for new protected loader\n");
error = create_cut_entry(mm->context.tcount,
mm->start_code, mm->end_code,
mm->start_data, mm->end_data);
}
else
#endif /* CONFIG_PROTECTED_MODE */
{
DebugEX("create_cut_entry for unprotected mode\n");
error = create_cut_entry(0,
0L, current->thread.flags & E2K_FLAG_32BIT ?
TASK32_SIZE : TASK_SIZE,
0L, current->thread.flags & E2K_FLAG_32BIT ?
TASK32_SIZE : TASK_SIZE);
}
if (error) {
DebugEX("Could not create CUT entry\n");
goto fatal_error;
}
/*
* Set CU descriptor (register) to point to the CUT base.
*/
cutd.CUTD_base = USER_CUT_AREA_BASE;
WRITE_CUTD_REG(cutd);
/*
* We don't return to hard_sys_calls() so call
* syscall_trace_leave() manually.
*/
if (unlikely(ti->flags & _TIF_WORK_SYSCALL_TRACE)) {
struct pt_regs *regs = current_pt_regs();
if (regs && user_mode(regs))
syscall_trace_leave(regs);
}
/*
* Set some special registers in accordance with
* E2K API specifications.
*/
INIT_SPECIAL_REGISTERS();
#ifdef CONFIG_GREGS_CONTEXT
INIT_G_REGS();
INIT_TI_GLOBAL_REGISTERS(current_thread_info());
#endif
#if defined CONFIG_FUNCTION_GRAPH_TRACER
/*
* We won't ever return from this function and we are on a new stack
* so remove all tracing entries.
*/
current->curr_ret_stack = -1;
#endif
/*
* Switch to the hardware stacks in the new user space.
* Note that we should not fail after this, otherwise
* deactivate_mm() won't switch to kernel hardware stacks
* (because it will not find kernel entry point), leading
* to all kinds of problems.
*/
goto_new_user_hard_stk(&stacks);
/*
* Free the old hardware stacks after we've switched to the new ones
*/
if (kernel) {
if (UHWS_PSEUDO_MODE) {
free_kernel_p_stack(k_psp_area->base);
free_kernel_pc_stack(k_pcsp_area->base);
kfree(k_psp_area);
kfree(k_pcsp_area);
} else {
free_kernel_p_stack(k_psp_stk);
free_kernel_pc_stack(k_pcsp_stk);
}
} else {
BUG_ON(!test_ts_flag(TS_MAPPED_HW_STACKS));
unmap_hw_stack_mappings(ti);
free_hw_stack_pages(ti);
}
ti->u_stk_base = u_stk_base;
ti->u_stk_sz = u_stk_sz;
ti->u_stk_top = stack_top;
ti->pt_regs = NULL; /* Could have been allocated by a signal handler */
#ifdef CONFIG_KERNEL_TIMES_ACCOUNT
ti->times_num = 0;
#endif /* CONFIG_KERNEL_TIMES_ACCOUNT */
raw_local_irq_disable();
user_enter();
/*
* And now just switch to user's data stack and function
*/
raw_all_irq_disable();
WRITE_SBR_REG_VALUE(stack_top & ~E2K_ALIGN_STACKS_BASE_MASK);
WRITE_USD_REG(usd_hi, usd_lo);
E2K_SET_DSREG(rpr.hi, 0);
E2K_SET_DSREG(rpr.lo, 0);
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE) {
unsigned long *p_base_lo, *p_base_hi;
unsigned long base_lo, base_hi;
init_sem_malloc(&mm->context.umpools);
/* new loader interface */
p_base_lo = (unsigned long *) mm->start_stack;
p_base_hi = p_base_lo + 1;
base_lo = *p_base_lo;
base_hi = *p_base_hi;
/* We may erase base descriptor from stack since
* no one will ever need it there. */
*p_base_lo = 0;
*p_base_hi = 0;
E2K_JUMP_WITH_ARGUMENTS(protected_switch_to_user_func,
3, base_lo, base_hi, start);
} else {
E2K_JUMP_WITH_ARGUMENTS(switch_to_user_func, 2, 0, start);
}
#else
E2K_JUMP_WITH_ARGUMENTS(switch_to_user_func, 2, 0, start);
#endif /* CONFIG_PROTECTED_MODE */
fatal_error:
if (kernel) {
if (UHWS_PSEUDO_MODE) {
if (k_psp_area) {
list_add(&k_psp_area->list_entry, &ti->ps_list);
ti->cur_ps = k_psp_area;
}
if (k_pcsp_area) {
list_add(&k_pcsp_area->list_entry,
&ti->pcs_list);
ti->cur_pcs = k_pcsp_area;
}
} else {
if (k_psp_stk)
ti->ps_base = k_psp_stk;
if (k_pcsp_stk)
ti->pcs_base = k_pcsp_stk;
}
}
DebugEX("fatal error %d: send KILL signal\n", error);
if (kernel)
/* Nowhere to return to, just exit */
do_exit(SIGKILL);
if (kernel)
/* Nowhere to return to, just exit */
do_exit(SIGKILL);
send_sig(SIGKILL, current, 0);
return error;
}
long e2k_sys_execve(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp)
{
int ret;
set_ts_flag(TS_USER_EXECVE);
ret = sys_execve(filename, argv, envp);
clear_ts_flag(TS_USER_EXECVE);
if (!ret)
ret = do_sys_execve(current_thread_info()->execve.entry,
current_thread_info()->execve.sp, false);
return ret;
}
#ifdef CONFIG_COMPAT
long compat_e2k_sys_execve(const char __user *filename,
const compat_uptr_t __user *argv,
const compat_uptr_t __user *envp)
{
int ret;
set_ts_flag(TS_USER_EXECVE);
ret = compat_sys_execve(filename, argv, envp);
clear_ts_flag(TS_USER_EXECVE);
if (!ret)
ret = do_sys_execve(current_thread_info()->execve.entry,
current_thread_info()->execve.sp, false);
return ret;
}
#endif
void start_thread(struct pt_regs *regs, unsigned long entry, unsigned long sp)
{
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
e2k_mem_crs_t *crs;
unsigned long *frame;
unsigned long flags;
bool flush_chain, flush_procedure;
DebugP("entry 0x%lx sp 0x%lx\n", entry, sp);
current_thread_info()->execve.entry = entry;
current_thread_info()->execve.sp = sp;
/*
* If called from user mode then do_sys_execve() will
* be called manually from ttable_entry().
*/
if (test_ts_flag(TS_USER_EXECVE))
return;
raw_all_irq_save(flags);
psp_lo = READ_PSP_LO_REG();
pcsp_lo = READ_PCSP_LO_REG();
psp_hi = READ_PSP_HI_REG();
pcsp_hi = READ_PCSP_HI_REG();
flush_chain = (AS(pcsp_hi).ind < 2 * SZ_OF_CR);
/* Assume a maximum of 4 do_sys_execve()'s parameters */
flush_procedure = (AS(psp_hi).ind < 2 * EXT_4_NR_SZ);
if (flush_chain)
E2K_FLUSHC;
if (flush_procedure)
E2K_FLUSHR;
if (flush_chain || flush_procedure)
E2K_FLUSH_WAIT;
/*
* Change IP of the last frame to do_sys_execve().
*/
crs = (e2k_mem_crs_t *) (AS(pcsp_lo).base + SZ_OF_CR);
AS(crs->cr0_hi).ip = (unsigned long) &do_sys_execve >> 3;
/*
* Put do_sys_execve()'s parameters into the procedure stack.
*/
frame = (unsigned long *) AS(psp_lo).base;
frame[0] = entry;
if (machine.iset_ver < E2K_ISET_V5) {
frame[1] = sp;
/* Skip frame[2] and frame[3] - they hold extended data */
} else {
frame[2] = sp;
/* Skip frame[1] and frame[3] - they hold extended data */
}
frame[4] = true;
raw_all_irq_restore(flags);
return;
}
EXPORT_SYMBOL(start_thread);
/*
* Idle related variables and functions
*/
unsigned long boot_option_idle_override = 0;
EXPORT_SYMBOL(boot_option_idle_override);
#ifdef CONFIG_SECONDARY_SPACE_SUPPORT
static void save_binco_regs(struct task_struct *new_task)
{
/* Save intel regs from processor. For binary compiler. */
if (!TASK_IS_BINCO(current))
return;
SAVE_INTEL_REGS((&new_task->thread.sw_regs));
#ifdef CONFIG_TC_STORAGE
E2K_FLUSH_ALL_TC;
new_task->thread.sw_regs.tcd = E2K_GET_TCD();
#endif /* CONFIG_TC_STORAGE */
}
#else /* !CONFIG_SECONDARY_SPACE_SUPPORT: */
#define save_binco_regs(new_task)
#endif /* CONFIG_SECONDARY_SPACE_SUPPORT */
static inline void
init_sw_regs(struct task_struct *new_task, e2k_stacks_t *new_stacks,
e2k_mem_crs_t *new_crs, bool kernel)
{
struct sw_regs *new_sw_regs = &new_task->thread.sw_regs;
struct thread_info *new_ti = task_thread_info(new_task);
/*
* Protect kernel part of hardware stacks from user.
*
* sw_regs contains the real value of psp.hi/pcsp.hi as
* opposed to pt_regs, so adjust "size" field explicitly.
*/
if (!kernel) {
new_stacks->psp_hi.PSP_hi_size -= KERNEL_P_STACK_SIZE;
new_stacks->pcsp_hi.PCSP_hi_size -= KERNEL_PC_STACK_SIZE;
clear_ti_status_flag(new_ti, TS_HW_STACKS_EXPANDED);
} else {
set_ti_status_flag(new_ti, TS_HW_STACKS_EXPANDED);
}
new_sw_regs->sbr = new_stacks->sbr;
new_sw_regs->usd_lo = new_stacks->usd_lo;
new_sw_regs->usd_hi = new_stacks->usd_hi;
new_sw_regs->psp_lo = new_stacks->psp_lo;
new_sw_regs->psp_hi = new_stacks->psp_hi;
new_sw_regs->pcsp_lo = new_stacks->pcsp_lo;
new_sw_regs->pcsp_hi = new_stacks->pcsp_hi;
new_sw_regs->cr0_lo = new_crs->cr0_lo;
new_sw_regs->cr0_hi = new_crs->cr0_hi;
new_sw_regs->cr_wd = new_crs->cr1_lo;
new_sw_regs->cr_ussz = new_crs->cr1_hi;
/*
* New process will start with interrupts disabled.
* They will be enabled in schedule_tail() (user's upsr
* is saved in pt_regs and does not need to be corrected).
*/
new_task->thread.sw_regs.upsr = E2K_KERNEL_UPSR_DISABLED;
AS_WORD(new_sw_regs->fpcr) = E2K_GET_SREG_NV(fpcr);
AS_WORD(new_sw_regs->fpsr) = E2K_GET_SREG_NV(fpsr);
AS_WORD(new_sw_regs->pfpfr) = E2K_GET_SREG_NV(pfpfr);
AS_WORD(new_sw_regs->cutd) = E2K_GET_DSREG_NV(cutd);
#ifdef CONFIG_GREGS_CONTEXT
SAVE_GLOBAL_REGISTERS(new_task, true);
#endif /* CONFIG_GREGS_CONTEXT */
save_binco_regs(new_task);
DebugSWregs("finish", &new_task->thread.sw_regs);
}
long __ret_from_fork(struct task_struct *prev)
{
BUG_ON(current->mm && sge_checking_enabled()
|| !current->mm && !sge_checking_enabled());
get_task_struct(current);
schedule_tail(prev);
return 0;
}
asmlinkage long e2k_sys_clone(unsigned long clone_flags, e2k_addr_t newsp,
struct pt_regs *regs, int __user *parent_tidptr,
int __user *child_tidptr, unsigned long tls)
{
struct task_struct *current_fork = current;
long rval;
DebugCL("CPU #%d started for task %s (pid %d) with new "
"SP 0x%lx\n",
smp_processor_id(), current->comm, current->pid, newsp);
DebugCpuR("sys_clone before");
regs->tls = tls;
rval = do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
/* we should be here if parent only */
if (current_fork != current)
/* we should return in ttable for new thread */
panic("sys_clone: current_fork != current");
DebugCL("sys_clone exited with value %ld\n", rval);
return rval;
}
asmlinkage pid_t sys_clone2(unsigned long clone_flags,
long stack_base,
unsigned long long stack_size,
struct pt_regs * regs,
int __user *parent_tidptr,
int __user *child_tidptr,
unsigned long tls)
{
long rval;
long flags = clone_flags;
struct task_struct *current_fork = current;
DebugCL("start.\n");
if (!access_ok(VERIFY_WRITE, stack_base, stack_size)) {
return -ENOMEM;
}
//if (!flags) flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND;
if (!flags) flags = SIGCHLD | CLONE_VM | CLONE_FS | CLONE_FILES;
regs->tls = tls;
rval = do_fork(flags, stack_base, stack_size,
parent_tidptr, child_tidptr);
/* we should be here if parent only */
if (current_fork != current)
/* we should return in ttable for new thread */
panic("sys_clone: current_fork != current");
DebugCL("sys_clone2 exited with rval %ld\n", rval);
return rval;
}
asmlinkage long sys_fork()
{
long rval;
unsigned long clone_flags = SIGCHLD;
DebugF("CPU #%d: started for task %s (pid %d)\n",
smp_processor_id(), current->comm, current->pid);
rval = do_fork(clone_flags, 0, 0, NULL, NULL);
DebugF("returned with child pid %ld\n", rval);
return rval;
}
asmlinkage long e2k_sys_vfork(struct pt_regs *regs)
{
long rval;
DebugP("entered.\n");
rval = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 0, NULL, NULL);
DebugP("exited rval=%ld current=%p, current->pid =%d\n",
rval, current, current->pid);
return rval;
}
/*
* Correct kernel pt_regs structure:
* It needs remain state of stack registers as for parent stacks:
* indexes and correct addresses and sizes of new process:
* size & base of data stack and base & size of hardware stacks
*/
static inline void
fix_kernel_pt_regs(thread_info_t *new_ti, pt_regs_t *regs,
e2k_size_t delta_sp, e2k_size_t delta_sz)
{
e2k_stacks_t *stacks = &regs->stacks;
e2k_mem_crs_t *crs = &regs->crs;
e2k_addr_t cur_base;
e2k_addr_t new_base;
e2k_size_t cur_size;
e2k_size_t new_size;
e2k_cr1_hi_t cr1_hi;
DebugKS("corrects SBR from 0x%lx "
"to 0x%lx\n",
stacks->sbr, new_ti->k_stk_base + new_ti->k_stk_sz);
stacks->sbr = new_ti->k_stk_base + new_ti->k_stk_sz;
cur_base = stacks->usd_lo.USD_lo_base;
cur_size = stacks->usd_hi.USD_hi_size;
new_base = cur_base + delta_sp;
new_size = cur_size + delta_sz;
DebugKS("corrects USD base from 0x%llx to 0x%lx, size from 0x%x to 0x%lx\n",
stacks->usd_lo.USD_lo_base, new_base,
stacks->usd_hi.USD_hi_size, new_size);
stacks->usd_lo.USD_lo_base = new_base;
stacks->usd_hi.USD_hi_size = new_size;
if (delta_sz == 0) {
/* Does not need correct cr1_hi.ussz field because of */
/* size of data stack was not changed */
return;
}
cr1_hi = crs->cr1_hi;
AS_STRUCT(cr1_hi).ussz += (delta_sz >> 4);
DebugKS("corrects CR1_hi ussz field "
"from 0x%x to 0x%x\n",
AS_STRUCT(crs->cr1_hi).ussz << 4,
AS_STRUCT(cr1_hi).ussz << 4);
crs->cr1_hi = cr1_hi;
/* Update all pointers */
if (regs->trap)
regs->trap = (void *) regs->trap + delta_sp;
#ifdef CONFIG_USE_AAU
if (regs->aau_context)
regs->aau_context = (void *) regs->aau_context + delta_sp;
#endif
}
static inline void
fix_kernel_stacks_state(pt_regs_t *regs, e2k_size_t delta_sz)
{
DebugKS("corrects kernel USD current "
"size from 0x%lx to 0x%lx\n",
regs->k_usd_size, regs->k_usd_size + delta_sz);
regs->k_usd_size += delta_sz;
}
/*
* fork() duplicates all user virtual space, including user local
* data stack and hardware stacks, but creates new kernel stacks for
* son process. These stacks have other addresses and can have other size.
* It needs correct all stacks pointers and registers into the
* thread/thread_info structure and pt_regs structures of new process
*/
static inline int
fix_all_kernel_stack_regs(thread_info_t *new_ti, e2k_size_t delta_sp)
{
thread_info_t *cur_ti = current_thread_info();
pt_regs_t *regs;
e2k_addr_t cur_base;
e2k_addr_t new_base;
e2k_size_t cur_size;
e2k_size_t new_size;
e2k_size_t delta_sz;
int regs_num = 0;
DebugKS("started for thread 0x%p, "
"delta sp 0x%lx\n",
new_ti, delta_sp);
/*
* thread_info structure created with empty state of stacks.
* We copied fully data from current stacks to new stacks, so
* it needs correct only current state of stack registers
* according to state of parent stacks:
* size & base of data stack and indexes of hardware stacks
*/
cur_base = cur_ti->k_usd_lo.USD_lo_base;
cur_size = cur_ti->k_usd_hi.USD_hi_size;
new_base = cur_base + delta_sp;
new_size = new_base - new_ti->k_stk_base;
delta_sz = new_size - cur_size;
DebugKS("corrects USD base from 0x%llx to 0x%lx, size from 0x%x to 0x%lx\n",
new_ti->k_usd_lo.USD_lo_base, new_base,
new_ti->k_usd_hi.USD_hi_size, new_size);
new_ti->k_usd_lo.USD_lo_base = new_base;
new_ti->k_usd_hi.USD_hi_size = new_size;
/*
* kernel pt_regs structures were copied from parent structures
* without any corrections, it needs remain state of stack
* registers as for parent stacks: indexes and correct addresses
* and sizes of new process: size & base of data stack
* and base & size of hardware stacks
*/
CHECK_PT_REGS_LOOP(new_ti->pt_regs);
for (regs = new_ti->pt_regs; regs != NULL; regs = regs->next) {
CHECK_PT_REGS_LOOP(regs);
if (user_mode(regs)) {
DebugKS("pt_regs 0x%p "
"is user regs, miss its\n",
regs);
continue;
}
fix_kernel_pt_regs(new_ti, regs, delta_sp, delta_sz);
regs_num ++;
}
return regs_num;
}
/*
* To increment or decrease user data stack size it needs update
* data stack size in the USD register and and in te chine regisres
* (CR1_hi.ussz field) into all user pt_regs structures of the process
*/
int
fix_all_user_stack_regs(pt_regs_t *regs, e2k_size_t delta_sp)
{
pt_regs_t *user_regs;
e2k_usd_hi_t usd_hi;
e2k_cr1_hi_t cr1_hi;
int regs_num = 0;
DebugES("started with pt_regs 0x%p, "
"delta sp 0x%lx\n",
regs, delta_sp);
CHECK_PT_REGS_LOOP(regs);
for (user_regs = regs; user_regs != NULL;
user_regs = user_regs->next) {
if (!user_mode(user_regs) &&
user_regs->stacks.usd_lo.USD_lo_base >= TASK_SIZE) {
DebugDS("pt_regs 0x%p "
"is kernel regs, miss its\n",
user_regs);
continue;
}
usd_hi = user_regs->stacks.usd_hi;
DebugES("pt_regs 0x%p is user regs, USD base 0x%llx, size 0x%x\n",
user_regs, user_regs->stacks.usd_lo.USD_lo_base,
usd_hi.USD_hi_size);
usd_hi.USD_hi_size += delta_sp;
user_regs->stacks.usd_hi = usd_hi;
DebugES("USD new size 0x%x\n",
usd_hi.USD_hi_size);
cr1_hi = user_regs->crs.cr1_hi;
DebugES("CR1_hi us size 0x%x\n",
AS_STRUCT(cr1_hi).ussz << 4);
AS_STRUCT(cr1_hi).ussz += (delta_sp >> 4);
user_regs->crs.cr1_hi = cr1_hi;
DebugES("CR1_hi new us size 0x%x\n",
AS_STRUCT(cr1_hi).ussz << 4);
regs_num ++;
}
return regs_num;
}
int
fix_all_stack_sz(e2k_addr_t base, long cr_ind,
e2k_size_t delta_sp, long start_cr_ind,
int user_stacks, int set_stack_sz)
{
e2k_cr0_hi_t cr0_hi;
e2k_cr1_hi_t cr1_hi;
e2k_cr1_lo_t cr1_lo;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
long flags = 0;
int current_pcs = 0;
if (start_cr_ind <= 0)
start_cr_ind = 0;
DebugES("started with PCSP stack base "
"0x%lx cr_ind 0x%lx, start cr_ind 0x%lx, delta sp 0x%lx\n",
base, cr_ind, start_cr_ind, delta_sp);
if (cr_ind == 0) {
DebugES("stack is empty\n");
return 0;
}
pcsp_lo = READ_PCSP_LO_REG();
pcsp_hi = READ_PCSP_HI_REG();
if (base >= pcsp_lo.PCSP_lo_base &&
base < pcsp_lo.PCSP_lo_base + pcsp_hi.PCSP_hi_size) {
current_pcs = 1;
raw_all_irq_save(flags);
E2K_FLUSHC;
E2K_FLUSH_WAIT;
}
for (cr_ind = cr_ind - SZ_OF_CR; cr_ind >= start_cr_ind;
cr_ind -= SZ_OF_CR) {
int err;
e2k_psr_t psr;
e2k_addr_t ip;
err = get_cr0_hi(&cr0_hi, base, cr_ind, user_stacks);
if (err != 0) {
DebugES("get_cr0_hi() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
base, cr_ind, err);
return err;
}
ip = (AS_STRUCT(cr0_hi).ip) << 3;
DebugES("cr_ind 0x%lx : IP "
"0x%lx\n", cr_ind, ip);
err = get_cr1_lo(&cr1_lo, base, cr_ind, user_stacks);
if (err != 0) {
DebugES("get_cr1_lo() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n", base, cr_ind, err);
return err;
}
AS_WORD(psr) = AS_STRUCT(cr1_lo).psr;
DebugES("cr_ind 0x%lx : psr "
"0x%x\n", cr_ind, AS_WORD(psr));
if ((user_stacks && (ip >= TASK_SIZE ||
AS_STRUCT(psr).pm)) ||
(!user_stacks && (ip < TASK_SIZE &&
!AS_STRUCT(psr).pm))) {
/*
* It is a kernel function in the user PC stack or
* user function in the kernel stack
* The data stack of this function is out of this
* stack and places in separate user or kernel
* space for each process.
* Do not correct this chain register
*/
DebugES("it is the "
"chain of %s procedure, do not correct "
"one\n",
(!user_stacks) ? "user" : "kernel");
continue;
}
err = get_cr1_hi(&cr1_hi, base, cr_ind, user_stacks);
if (err != 0) {
DebugES("get_cr1_hi() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n", base, cr_ind, err);
return err;
}
DebugES("cr_ind 0x%lx : ussz 0x%x\n",
cr_ind, (AS_STRUCT(cr1_hi).ussz) << 4);
if (set_stack_sz)
(AS_STRUCT(cr1_hi).ussz) = (delta_sp >> 4);
else
(AS_STRUCT(cr1_hi).ussz) += (delta_sp >> 4);
DebugES("new cr1_hi.ussz 0x%x\n",
(AS_STRUCT(cr1_hi).ussz) << 4);
err = put_cr1_hi(cr1_hi, base, cr_ind, user_stacks);
if (err != 0) {
DebugES("put_cr1_hi() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n", base, cr_ind, err);
return err;
}
}
if (current_pcs)
raw_all_irq_restore(flags);
return 0;
}
static inline void
account_the_wd_psize(e2k_mem_crs_t *crs, long cr_ind, e2k_size_t *wd_psize)
{
*wd_psize = AS_STRUCT(crs->cr1_lo).wpsz;
DebugSD("cr_ind 0x%lx : WD.psize 0x%lx\n",
cr_ind, *wd_psize);
}
static inline void
account_the_ps_frame(e2k_mem_crs_t *crs, long cr_ind, long *fp_ind)
{
*fp_ind -= AS_STRUCT(crs->cr1_lo).wbs * EXT_4_NR_SZ;
DebugST("cr_ind 0x%lx : wbs "
"0x%x, procedure stack ind 0x%lx\n",
cr_ind, AS_STRUCT(crs->cr1_lo).wbs * EXT_4_NR_SZ, *fp_ind);
}
static inline void
account_the_ds_frame(e2k_mem_crs_t *crs, long cr_ind,
e2k_addr_t *cur_usd_sp, long *cur_usd_size)
{
long cur_ussz;
cur_ussz = AS_STRUCT(crs->cr1_hi).ussz << 4;
*cur_usd_sp += (cur_ussz - *cur_usd_size);
DebugST("cr_ind 0x%lx "
": ussz 0x%lx, current SP 0x%lx\n",
cr_ind, cur_ussz, *cur_usd_sp);
*cur_usd_size = cur_ussz;
}
/*
* Get return IP for n level below pt_regs return IP
*/
e2k_addr_t
get_nested_kernel_IP(pt_regs_t *regs, int n)
{
e2k_addr_t IP = 0UL;
e2k_cr0_hi_t cr0_hi;
e2k_addr_t base;
s64 cr_ind;
u64 flags;
raw_all_irq_save(flags);
E2K_FLUSHC;
base = regs->stacks.pcsp_lo.PCSP_lo_base;
cr_ind = regs->stacks.pcsp_hi.PCSP_hi_ind;
E2K_FLUSH_WAIT;
while (--n) {
if (cr_ind <= 0) {
panic("get_nested_kernel_IP(): procedure chain "
"stack underflow\n");
}
cr_ind = cr_ind - SZ_OF_CR;
get_kernel_cr0_hi(&cr0_hi, base, cr_ind);
IP = AS_STRUCT(cr0_hi).ip << 3;
}
raw_all_irq_restore(flags);
return IP;
}
int
go_hd_stk_down(e2k_psp_hi_t psp_hi,
e2k_pcsp_lo_t pcsp_lo, e2k_pcsp_hi_t pcsp_hi,
int down,
long *psp_ind, long *pcsp_ind,
e2k_size_t *wd_psize, int *sw_num_p,
e2k_mem_crs_t *crs, int user_stacks)
{
unsigned long flags;
u64 pcs_base;
s64 cr_ind, end_cr_ind;
long fp_ind;
e2k_size_t psize = 0;
int sw_num = 0, user_frame = user_stacks;
/*
* Dump chain stack contents to memory.
*/
raw_all_irq_save(flags);
E2K_FLUSHC;
E2K_FLUSH_WAIT;
raw_all_irq_restore(flags);
pcs_base = AS_STRUCT(pcsp_lo).base;
cr_ind = AS_STRUCT(pcsp_hi).ind;
end_cr_ind = cr_ind - SZ_OF_CR * down;
fp_ind = AS_STRUCT(psp_hi).ind;
DebugSD("started with base 0x%lx cr_ind 0x%lx : "
"procedure stack ind 0x%lx, end cr_ind 0x%lx\n",
pcs_base, cr_ind, fp_ind, end_cr_ind);
while (1) {
e2k_psr_t psr;
int err;
AS_WORD(psr) = AS_STRUCT(crs->cr1_lo).psr;
if (user_frame == AS_STRUCT(psr).pm) {
if (user_frame == user_stacks)
sw_num ++;
user_frame = !user_frame;
}
DebugSD("cr_ind 0x%lx : IP "
"0x%lx , psr.pm 0x%x, frames switching # %d\n",
cr_ind, (AS_STRUCT(crs->cr0_hi).ip) << 3,
AS_STRUCT(psr).pm, sw_num);
if (cr_ind <= end_cr_ind) {
break;
}
/*
* Wd_psize should be accounted in any case
*/
account_the_wd_psize(crs, cr_ind, &psize);
account_the_ps_frame(crs, cr_ind, &fp_ind);
cr_ind -= SZ_OF_CR;
err = get_crs(crs, pcs_base, cr_ind, user_stacks);
if (err != 0) {
DebugSD("get_crs() "
"base 0x%lx cr_ind 0x%lx returned "
"error %d\n",
pcs_base, cr_ind, err);
return err;
}
DebugSD("cr_ind 0x%lx : ussz 0x%x\n",
cr_ind, (AS_STRUCT(crs->cr1_hi).ussz) << 4);
}
*psp_ind = fp_ind;
*pcsp_ind = cr_ind;
*wd_psize = psize;
*sw_num_p = sw_num;
DebugSD("returns with cr_ind 0x%lx fp_ind 0x%lx "
"WD.psize 0x%lx, SW_num %d\n",
cr_ind, fp_ind, psize, sw_num);
return 0;
}
/*
* Calculate hardware stacks indexes and sizes, kernel data stack bottom
* and SP for specified frames number (level_num up or down).
* Before (if it needs) miss some frames (down number).
* Hardware stacks can be handled only within the bounds of current
* active (resident) stack frame
*/
static inline int
get_n_stacks_frames_to_copy(e2k_stacks_t *cur_stacks, e2k_mem_crs_t *cur_crs,
int down, int level_num,
int copy_data_stack, int user_stacks,
long *fp_ind_p, long *fp_size_p,
long *cr_ind_p, long *cr_size_p, e2k_mem_crs_t *new_crs,
long *usd_bottom_p, long *usd_sp_p, long *usd_size_p)
{
thread_info_t *cur_thr = current_thread_info();
long fp_ind;
long fp_end;
e2k_addr_t pcs_base;
long cr_ind;
long cr_end;
e2k_mem_crs_t crs;
e2k_addr_t cur_usd_sp = 0;
e2k_addr_t cur_usd_start = 0;
long cur_usd_size = 0;
int sp_is_not_set = 0;
e2k_size_t psize = 0;
int sw_num = 0;
long to_copy;
e2k_psr_t psr;
int level = 0;
int other_frame;
int err;
DebugUS("entered for %s stacks "
"level down %d, num %d\n",
(user_stacks) ? "user" : "kernel",
down, level_num);
pcs_base = cur_stacks->pcsp_lo.PCSP_lo_base;
/*
* cr1_lo has wbs (num of NR in window) of previouse func
* (copy_thread) and we can get needed fp_ind in go_hd_stk_down()
*/
go_hd_stk_down(cur_stacks->psp_hi,
cur_stacks->pcsp_lo, cur_stacks->pcsp_hi,
down,
&fp_ind, &cr_ind,
&psize, &sw_num,
cur_crs, user_stacks);
fp_end = fp_ind;
DebugUS("current procedure stack: "
"start 0x%lx, ind 0x%lx\n",
cur_stacks->psp_lo.PSP_lo_base, fp_ind);
cr_end = cr_ind;
DebugUS("current procedure chain "
"stack: start 0x%lx, ind 0x%lx\n",
pcs_base, cr_ind);
if (level_num > cr_end / SZ_OF_CR) {
level_num = cr_end / SZ_OF_CR;
}
if (down == 0) {
crs = *cur_crs;
} else {
err = get_crs(&crs, pcs_base, cr_ind, user_stacks);
if (err != 0) {
DebugUS("get_crs() "
": base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
pcs_base, cr_ind, err);
return err;
}
}
*new_crs = crs;
if (copy_data_stack) {
cur_usd_size = AS_STRUCT(crs.cr1_hi).ussz << 4;
cur_usd_sp = cur_stacks->usd_lo.USD_lo_base +
(cur_usd_size -
cur_stacks->usd_hi.USD_hi_size);
cur_usd_start = cur_usd_sp;
DebugUS("current data stack: sp 0x%lx, size of free area 0x%lx USD_size 0x%x\n",
cur_usd_sp, cur_usd_size,
cur_stacks->usd_hi.USD_hi_size);
}
AS_WORD(psr) = AS_STRUCT(crs.cr1_lo).psr;
DebugUS("end cr_ind 0x%lx : IP "
"0x%lx, psr.pm 0x%x\n",
cr_ind, (AS_STRUCT(crs.cr0_hi).ip) << 3, AS_STRUCT(psr).pm);
for (level = 0; level < level_num; level ++) {
/*
* Wd_psize should be accounted in any case
*/
account_the_wd_psize(&crs, cr_ind, &psize);
account_the_ps_frame(&crs, cr_ind, &fp_ind);
if (cr_ind <= 0) {
DebugUS("procedure "
"chain stack ind 0%lx <= 0\n",
cr_ind);
BUG();
return -EINVAL;
}
cr_ind -= SZ_OF_CR;
err = get_crs(&crs, pcs_base, cr_ind, user_stacks);
if (err != 0) {
DebugUS("get_crs() "
"base 0x%lx cr_ind 0x%lx returned "
"error %d\n",
pcs_base, cr_ind, err);
return err;
}
if (!copy_data_stack)
continue;
AS_WORD(psr) = AS_STRUCT(crs.cr1_lo).psr;
other_frame = (user_stacks == AS_STRUCT(psr).pm);
DebugUS("cr_ind 0x%lx : IP "
"0x%lx, psr.pm 0x%x, other frame %d\n",
cr_ind, (AS_STRUCT(crs.cr0_hi).ip) << 3,
AS_STRUCT(psr).pm, other_frame);
if (other_frame) {
/*
* It is a kernel function in the user PC
* stack or user function in the kernel stack.
* The data stack frame of this function
* is out of this stack and places
* in own kernel or user space for each
* process. This chain has not frames in
* the procedure stack and data stack.
*/
DebugUS("cr_ind "
"0x%lx : %s function: do not copy data "
"stack frames\n",
cr_ind,
(!user_stacks) ? "user" : "kernel");
sp_is_not_set = 1;
} else {
account_the_ds_frame(&crs, cr_ind, &cur_usd_sp,
&cur_usd_size);
sp_is_not_set = 0;
}
}
to_copy = fp_end - fp_ind;
*fp_ind_p = fp_ind;
*fp_size_p = to_copy;
DebugUS("procedure stack "
"to copy ind: 0x%lx, size 0x%lx\n",
fp_ind, to_copy);
to_copy = cr_end - cr_ind;
*cr_ind_p = cr_ind;
*cr_size_p= to_copy;
DebugUS("procedure chain stack "
"to copy ind: 0x%lx, size 0x%lx\n",
cr_ind, to_copy);
if (!copy_data_stack) {
DebugUS("user data stack "
"should not be copied\n");
return 0;
}
if (sp_is_not_set) {
if (user_stacks) {
cur_usd_size = cur_thr->pt_regs->
stacks.usd_hi.USD_hi_size;
cur_usd_sp = cur_thr->pt_regs->
stacks.usd_lo.USD_lo_base;
} else {
cur_usd_size = cur_thr->k_usd_hi.USD_hi_size;
cur_usd_sp = cur_thr->k_usd_lo.USD_lo_base;
}
DebugUS("top of data stack "
"is reached, set empty stack state: "
"current SP 0x%lx, size 0x%lx\n",
cur_usd_sp, cur_usd_size);
}
if (cr_ind == 0 && fp_ind != 0) {
panic("get_n_stacks_frames_to_copy() : bottom of "
"procedure chain stack is reached, but "
"is not reached bottom of procedure "
"stack: fp_ind 0x%lx\n", fp_ind);
}
*usd_bottom_p = cur_usd_start;
*usd_sp_p = cur_usd_sp;
*usd_size_p = cur_usd_size;
DebugUS("local data stack "
"to copy: bootom 0x%lx, sp 0x%lx, size 0x%lx\n",
cur_usd_start, cur_usd_sp, cur_usd_size);
return 0;
}
/*
* Calculate full hardware stacks indexes and sizes, local data stack bootom
* and SP. Before (if it needs) miss some frames (down number).
*/
static inline int
get_all_stacks_frames_to_copy(e2k_stacks_t *cur_stacks, e2k_mem_crs_t *cur_crs,
int down,
int copy_data_stack, int user_stacks,
long *fp_size_p, long *cr_size_p, e2k_mem_crs_t *new_crs,
long *usd_bottom_p, long *usd_sp_p, long *usd_size_p)
{
thread_info_t *cur_thr = current_thread_info();
long fp_end;
e2k_addr_t pcs_base;
long cr_end;
e2k_mem_crs_t crs;
e2k_addr_t cur_usd_sp = 0;
e2k_addr_t cur_usd_start = 0;
long cur_usd_size = 0;
e2k_size_t psize = 0;
int sw_num = 0;
int err;
DebugUS("entered for %s stacks "
"level down %d\n",
(user_stacks) ? "user" : "kernel",
down);
pcs_base = cur_stacks->pcsp_lo.PCSP_lo_base;
/*
* cr1_lo has wbs (num of NR in window) of previouse func
* (copy_thread) and we can get needed fp_ind in go_hd_stk_down()
*/
go_hd_stk_down(cur_stacks->psp_hi,
cur_stacks->pcsp_lo, cur_stacks->pcsp_hi,
down,
&fp_end, &cr_end,
&psize, &sw_num,
cur_crs, user_stacks);
DebugUS("current procedure stack: "
"start 0x%lx, ind 0x%lx\n",
cur_stacks->psp_lo.PSP_lo_base, fp_end);
DebugUS("current procedure chain "
"stack: start 0x%lx, ind 0x%lx\n",
pcs_base, cr_end);
if (down == 0) {
crs = *cur_crs;
} else {
err = get_crs(&crs, pcs_base, cr_end, user_stacks);
if (err != 0) {
DebugUS("get_crs() "
": base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
pcs_base, cr_end, err);
return err;
}
}
*new_crs = crs;
*fp_size_p = fp_end;
DebugUS("procedure stack "
"to copy full size 0x%lx\n",
fp_end);
*cr_size_p= cr_end;
DebugUS("procedure chain stack "
"to copy full size 0x%lx\n",
cr_end);
if (!copy_data_stack) {
DebugUS("user data stack "
"should not be copied\n");
return 0;
}
cur_usd_size = AS_STRUCT(crs.cr1_hi).ussz << 4;
cur_usd_sp = cur_stacks->usd_lo.USD_lo_base +
(cur_usd_size -
cur_stacks->usd_hi.USD_hi_size);
cur_usd_start = cur_usd_sp;
DebugUS("current data stack: sp 0x%lx, size of free area 0x%lx USD_size 0x%x\n",
cur_usd_sp, cur_usd_size,
cur_stacks->usd_hi.USD_hi_size);
if (user_stacks) {
cur_usd_size = cur_thr->u_stk_sz;
cur_usd_sp = cur_thr->u_stk_top;
} else {
cur_usd_size = cur_thr->k_stk_sz;
cur_usd_sp = cur_stacks->sbr;
}
DebugUS("empty stack state: SP 0x%lx, size 0x%lx\n",
cur_usd_sp, cur_usd_size);
*usd_bottom_p = cur_usd_start;
*usd_sp_p = cur_usd_sp;
*usd_size_p = cur_usd_size;
DebugUS("local data stack "
"to copy: bootom 0x%lx, sp 0x%lx, size 0x%lx\n",
cur_usd_start, cur_usd_sp, cur_usd_size);
return 0;
}
/*
* Copy data stack (if it needs) from current user stacks to
* the new process of kernel or user.
* Stack will be copied from current stack pointers and only
* from specified stack indexes and sizes
*/
static int do_copy_data_stack(e2k_stacks_t *new_stacks,
int copy_data_stack, int user_stacks,
long usd_bottom, long usd_sp, long usd_size,
e2k_addr_t *delta_sp, long *delta_sz_p)
{
e2k_addr_t new_usd_base = 0;
long new_usd_size = 0;
long delta_sz = 0;
long usd_to_copy;
DebugUS("entered for %s stacks\n",
(user_stacks) ? "user" : "kernel");
new_usd_base = new_stacks->usd_lo.USD_lo_base;
new_usd_size = new_stacks->usd_hi.USD_hi_size;
DebugUS("new data stack: base "
"0x%lx, size 0x%lx\n",
new_usd_base, new_usd_size);
if (copy_data_stack) {
delta_sz = new_usd_size - usd_size;
if (delta_sp != NULL)
*delta_sp = new_usd_base - usd_sp;
} else {
delta_sz = new_usd_size;
if (delta_sp != NULL)
*delta_sp = 0;
}
if (delta_sz_p != NULL)
*delta_sz_p = delta_sz;
DebugUS("delta sz 0x%lx: new USD "
"size 0x%lx current USD size 0x%lx, delta sp 0x%lx\n",
delta_sz, new_usd_size, usd_size,
(delta_sp != NULL) ? *delta_sp : 0);
if (!copy_data_stack) {
DebugUS("user data stack should "
"not be copied\n");
return 0;
}
usd_to_copy = usd_sp - usd_bottom;
if (usd_to_copy < 0) {
DebugUS("size of data "
"stack frames to copy 0x%lx is negative\n",
usd_to_copy);
BUG();
return -EINVAL;
} else if (usd_to_copy > new_usd_size) {
DebugUS("size of data "
"stack frames to copy 0x%lx > size of free area "
"in the new stack 0x%lx\n",
usd_to_copy, new_usd_size);
return -EINVAL;
} else if (usd_to_copy != 0) {
DebugUS("copy data stack "
"frames: from addr 0x%lx size 0x%lx to new stack "
"addr 0x%lx end 0x%lx\n",
usd_bottom, usd_to_copy,
new_usd_base - usd_to_copy, new_usd_base);
tagged_memcpy_8((char *) (new_usd_base - usd_to_copy),
(char *) usd_bottom, usd_to_copy);
new_usd_base -= usd_to_copy;
new_usd_size -= usd_to_copy;
new_stacks->usd_lo.USD_lo_base = new_usd_base;
new_stacks->usd_hi.USD_hi_size = new_usd_size;
DebugUS("new user data stack: "
"base 0x%lx, free area size 0x%lx\n",
new_usd_base, new_usd_size);
} else {
DebugUS("nothing to copy for "
"user data stack\n");
}
return 0;
}
/*
* Copy hardware stacks and data stack (if it needs) from current
* user stacks to the new process of kernel or user.
* Stack will be copied from current stack pointers and only
* from specified stack indexes and sizes
* Hardware stacks can be copied only within the bounds of current
* active (resident) stack frame
*/
static inline int
do_copy_all_stacks(e2k_stacks_t *cur_stacks, e2k_mem_crs_t *cur_crs,
e2k_stacks_t *new_stacks, e2k_mem_crs_t *new_crs,
int copy_data_stack, int user_stacks,
long fp_ind, long fp_size,
long cr_ind, long cr_size,
long usd_bottom, long usd_sp, long usd_size,
e2k_addr_t *delta_sp, e2k_size_t *delta_sz_p)
{
e2k_addr_t new_ps_base;
long new_ps_ind;
long new_ps_size;
e2k_addr_t new_pcs_base;
long new_pcs_ind;
long new_pcs_size;
e2k_addr_t cur_ps_stk;
e2k_addr_t cur_pcs_stk;
long delta_sz = 0;
int err;
#if DEBUG_US_MODE
dump_stack();
#endif
DebugUS("entered for %s stacks\n",
(user_stacks) ? "user" : "kernel");
new_ps_base = new_stacks->psp_lo.PSP_lo_base;
new_ps_ind = new_stacks->psp_hi.PSP_hi_ind;
new_ps_size = new_stacks->psp_hi.PSP_hi_size;
DebugUS("new procedure stack: base "
"0x%lx, ind 0x%lx, size 0x%lx\n",
new_ps_base, new_ps_ind, new_ps_size);
new_pcs_base = new_stacks->pcsp_lo.PCSP_lo_base;
new_pcs_ind = new_stacks->pcsp_hi.PCSP_hi_ind;
new_pcs_size = new_stacks->pcsp_hi.PCSP_hi_size;
DebugUS("new procedure chain stack: base "
"0x%lx, ind 0x%lx, size 0x%lx\n",
new_pcs_base, new_pcs_ind, new_pcs_size);
cur_ps_stk = cur_stacks->psp_lo.PSP_lo_base;
cur_pcs_stk = cur_stacks->pcsp_lo.PCSP_lo_base;
err = do_copy_data_stack(new_stacks, copy_data_stack, user_stacks,
usd_bottom, usd_sp, usd_size,
delta_sp, &delta_sz);
if (err != 0) {
DebugUS("do_copy_data_stack() "
"returned error %d\n", err);
return err;
}
if (delta_sz_p != NULL)
*delta_sz_p = delta_sz;
if (fp_size < 0) {
DebugUS("size of procedure "
"fstack rames to copy 0x%lx is negative\n",
fp_size);
BUG();
return -EINVAL;
} else if (fp_size > new_ps_size - new_ps_ind) {
DebugUS("size of procedure "
"stack frames to copy 0x%lx > size of free area "
"in new stack 0x%lx\n",
fp_size, new_ps_size - new_ps_ind);
return -EINVAL;
} else if (fp_size != 0) {
DebugUS("copy procedure stack "
"frames: from addr 0x%lx size 0x%lx to new stack "
"addr 0x%lx\n",
cur_ps_stk + fp_ind, fp_size,
new_ps_base + new_ps_ind);
tagged_memcpy_8((char *)(new_ps_base + new_ps_ind),
(char *)(cur_ps_stk + fp_ind), fp_size);
new_ps_ind += fp_size;
new_stacks->psp_hi.PSP_hi_ind = new_ps_ind;
DebugUS("new procedure stack "
"frames: ind 0x%lx\n", new_ps_ind);
} else {
DebugUS("nothing to copy for "
"procedure stack\n");
}
if (cr_size < 0) {
DebugUS("size of procedure "
"chain stack frames to copy 0x%lx is negative\n",
cr_size);
BUG();
return -EINVAL;
} else if (cr_size > new_pcs_size - new_pcs_ind) {
DebugUS("size of procedure "
"chain stack frames to copy 0x%lx > size of free "
"area in new stack 0x%lx\n",
cr_size, new_pcs_size - new_pcs_ind);
return -EINVAL;
} else if (cr_size != 0) {
DebugUS("copy procedure chain "
"stack frames : from addr 0x%lx size 0x%lx to "
"new stack addr 0x%lx\n",
cur_pcs_stk + cr_ind, cr_size,
new_pcs_base + new_pcs_ind);
memcpy((char *)(new_pcs_base + new_pcs_ind),
(char *)(cur_pcs_stk + cr_ind),
cr_size);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
fix_return_values_in_chain_stack(current->mm,
new_pcs_base + new_pcs_ind,
cur_pcs_stk + cr_ind, cr_size,
new_crs);
#endif
new_stacks->pcsp_hi.PCSP_hi_ind = new_pcs_ind + cr_size;
DebugUS("new procedure chain stack frames: ind 0x%x\n",
new_stacks->pcsp_hi.PCSP_hi_ind);
if (delta_sz != 0) {
e2k_size_t delta_ussz;
err = fix_all_stack_sz(new_pcs_base,
new_pcs_ind + cr_size,
delta_sz, new_pcs_ind,
user_stacks,
(copy_data_stack) ? 0 : 1);
if (err != 0) {
DebugUS(""
"fix_all_stack_sz() "
"returned error %d\n", err);
return err;
}
delta_ussz = delta_sz >> 4;
if (copy_data_stack)
AS_STRUCT(new_crs->cr1_hi).ussz +=
delta_ussz;
else
AS_STRUCT(new_crs->cr1_hi).ussz =
delta_ussz;
}
new_pcs_ind += cr_size;
DebugUS("new cr_ind 0x%lx : "
"cr1_hi.ussz 0x%x\n",
new_pcs_ind,
(AS_STRUCT(new_crs->cr1_hi).ussz) << 4);
} else {
DebugUS("nothing to copy for "
"procedure chain stack\n");
}
return 0;
}
int
fix_all_stack_sz_for_gdb(e2k_addr_t base, long cr_ind,
e2k_size_t delta_sp, long start_cr_ind,
int user_stacks, int set_stack_sz,
struct task_struct *child)
{
e2k_cr0_hi_t cr0_hi;
e2k_cr1_hi_t cr1_hi;
e2k_cr1_lo_t cr1_lo;
if (start_cr_ind <= 0)
start_cr_ind = 0;
DebugES_GDB("started with PCSP stack base "
"0x%lx cr_ind 0x%lx, start cr_ind 0x%lx, delta sp 0x%lx\n",
base, cr_ind, start_cr_ind, delta_sp);
if (cr_ind == 0) {
DebugES_GDB("stack is empty\n");
return 0;
}
for (cr_ind = cr_ind - SZ_OF_CR; cr_ind >= start_cr_ind;
cr_ind -= SZ_OF_CR) {
int err;
e2k_psr_t psr;
e2k_addr_t ip;
if ((err= access_process_vm(child, base + cr_ind +CR0_HI_I,
&cr0_hi, sizeof(cr0_hi), 0))
!= sizeof(cr0_hi)) {
DebugES_GDB("get_cr0_hi() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
base, cr_ind, err);
return -1;
}
ip = (AS_STRUCT(cr0_hi).ip) << 3;
DebugES_GDB("cr_ind 0x%lx : IP "
"0x%lx\n", cr_ind, ip);
if ((err= access_process_vm(child, base + cr_ind + CR1_LO_I,
&cr1_lo, sizeof(cr1_lo), 0))
!= sizeof(cr1_lo)) {
DebugES_GDB("get_cr0_lo() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
base, cr_ind, err);
return -1;
}
AS_WORD(psr) = AS_STRUCT(cr1_lo).psr;
DebugES_GDB("cr_ind 0x%lx : psr "
"0x%x\n", cr_ind, AS_WORD(psr));
if ((user_stacks && (ip >= TASK_SIZE ||
AS_STRUCT(psr).pm)) ||
(!user_stacks && (ip < TASK_SIZE &&
!AS_STRUCT(psr).pm))) {
/*
* It is a kernel function in the user PC stack or
* user function in the kernel stack
* The data stack of this function is out of this
* stack and places in separate user or kernel
* space for each process.
* Do not correct this chain register
*/
DebugES_GDB("it is the "
"chain of %s procedure, do not correct "
"one\n",
(!user_stacks) ? "user" : "kernel");
continue;
}
if ((err= access_process_vm(child, base + cr_ind + CR1_HI_I,
&cr1_hi, sizeof(cr1_hi), 0))
!= sizeof(cr1_hi)) {
DebugES_GDB("get_cr1_hi() "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
base, cr_ind, err);
return -1;
}
DebugES_GDB("cr_ind 0x%lx : ussz 0x%x\n",
cr_ind, (AS_STRUCT(cr1_hi).ussz) << 4);
if (set_stack_sz)
(AS_STRUCT(cr1_hi).ussz) = (delta_sp >> 4);
else
(AS_STRUCT(cr1_hi).ussz) += (delta_sp >> 4);
if ((err= access_process_vm(child, base + cr_ind + CR1_HI_I,
&cr1_hi, sizeof(cr1_hi), 1))
!= sizeof(cr1_hi)) {
DebugES_GDB("put_cr1_hi "
"base 0x%lx cr_ind 0x%lx returned error "
"%d\n",
base, cr_ind, err);
return -1;
}
}
return 0;
}
static int create_kernel_data_stack(struct task_struct *new_task,
e2k_stacks_t *new_stacks)
{
thread_info_t *new_thread = task_thread_info(new_task);
void *c_stk;
DebugKS("started on task 0x%p thread "
"0x%p for new task 0x%p thread 0x%p\n",
current, current_thread_info(),
new_task, new_thread);
/*
* Allocate memory for data stack of new thread or task
*/
c_stk = alloc_kernel_c_stack();
if (c_stk == NULL) {
DebugKS("could not allocate "
"kernel local data stack\n");
return -ENOMEM;
}
/*
* Create initial state of kernel local data stack
*/
new_thread->k_stk_base = (e2k_addr_t)c_stk;
new_thread->k_stk_sz = KERNEL_C_STACK_SIZE;
new_thread->k_usd_lo.USD_lo_base =
((e2k_addr_t)c_stk + KERNEL_C_STACK_SIZE) &
~E2K_ALIGN_USTACK_MASK;
new_thread->k_usd_hi.USD_hi_size = KERNEL_C_STACK_SIZE;
new_stacks->usd_lo = new_thread->k_usd_lo;
new_stacks->usd_hi = new_thread->k_usd_hi;
new_stacks->sbr = (e2k_addr_t)c_stk + KERNEL_C_STACK_SIZE;
DebugKS("allocated kernel local data "
"stack 0x%lx, size 0x%lx, USD base 0x%lx SBR 0x%lx\n",
new_thread->k_stk_base, new_thread->k_stk_sz,
new_thread->k_usd_lo.USD_lo_base,
new_stacks->sbr);
return 0;
}
static inline int
create_kernel_stacks(struct task_struct *new_task,
e2k_stacks_t *new_stacks)
{
thread_info_t *new_ti = task_thread_info(new_task);
void *psp_stk;
void *pcsp_stk;
e2k_psp_lo_t psp_lo;
e2k_psp_hi_t psp_hi;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
struct hw_stack_area *u_ps;
struct hw_stack_area *u_pcs;
int ret;
DebugKS("started for new task 0x%p\n",
new_task);
DebugKS("started on task 0x%p thread 0x%p for new task 0x%p thread 0x%p\n",
current, current_thread_info(),
new_task, new_ti);
/*
* Create data stack of new thread or task
*/
ret = create_kernel_data_stack(new_task, new_stacks);
if (ret != 0) {
return ret;
}
/*
* Allocate memory for hardware stacks of new thread or task
*/
psp_stk = alloc_kernel_p_stack(new_ti);
if (psp_stk == NULL) {
DebugKS("could not allocate kernel procedure stack\n");
goto out_free_kernel_c_stack;
}
pcsp_stk = alloc_kernel_pc_stack(new_ti);
if (pcsp_stk == NULL) {
DebugKS("could not allocate kernel procedure chain stack\n");
goto out_free_p_stack;
}
/*
* Create initial state of kernel hardware stacks
*/
if (UHWS_PSEUDO_MODE) {
u_ps = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (u_ps == NULL) {
DebugKS("could not kmalloc u_ps\n");
goto out_free_pc_stack;
}
list_add_tail(&u_ps->list_entry, &new_ti->ps_list);
new_ti->cur_ps = u_ps;
u_pcs = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (u_pcs == NULL) {
DebugKS("could not kmalloc u_pcs\n");
goto out_free_u_ps;
}
list_add_tail(&u_pcs->list_entry, &new_ti->pcs_list);
new_ti->cur_pcs = u_pcs;
}
SET_PS_BASE(new_ti, psp_stk);
SET_PS_SIZE(new_ti, KERNEL_P_STACK_SIZE);
SET_PS_OFFSET(new_ti, 0);
SET_PS_TOP(new_ti, KERNEL_P_STACK_SIZE);
psp_lo.PSP_lo_base = (e2k_addr_t)psp_stk;
psp_hi.PSP_hi_size = KERNEL_P_STACK_SIZE;
psp_hi.PSP_hi_ind = 0;
new_stacks->psp_lo = psp_lo;
new_stacks->psp_hi = psp_hi;
DebugKS("allocated kernel procedure stack 0x%llx, size 0x%x\n",
new_stacks->psp_lo.PSP_lo_base,
new_stacks->psp_hi.PSP_hi_size);
SET_PCS_BASE(new_ti, pcsp_stk);
SET_PCS_SIZE(new_ti, KERNEL_PC_STACK_SIZE);
SET_PCS_OFFSET(new_ti, 0);
SET_PCS_TOP(new_ti, KERNEL_PC_STACK_SIZE);
pcsp_lo.PCSP_lo_base = (e2k_addr_t)pcsp_stk;
pcsp_hi.PCSP_hi_size = KERNEL_PC_STACK_SIZE;
pcsp_hi.PCSP_hi_ind = 0;
new_stacks->pcsp_lo = pcsp_lo;
new_stacks->pcsp_hi = pcsp_hi;
DebugKS("allocated kernel procedure chain stack 0x%llx, size 0x%x\n",
new_stacks->pcsp_lo.PCSP_lo_base,
new_stacks->pcsp_hi.PCSP_hi_size);
return 0;
out_free_u_ps:
list_del(&u_ps->list_entry);
new_ti->cur_ps = NULL;
kfree(u_ps);
out_free_pc_stack:
free_kernel_pc_stack(psp_stk);
out_free_p_stack:
free_kernel_p_stack(psp_stk);
out_free_kernel_c_stack:
free_kernel_c_stack((void *)new_ti->k_stk_base);
new_ti->k_stk_base = 0;
return -ENOMEM;
}
/**
* get_hardware_stacks_frames_to_copy - calculate stacks areas to copy
* @cur_stacks: structure to put current stack parameters in
* @cur_crs: current CR8 registers values will be returned here
* @down: number of frames to skip
* @level_num: number of frames to copy (copy all if 0)
* @copy_data_stack: whether to copy the kernel data stack
* @fp_ind_p: procedure stack area start will be returned here
* @fp_size_p: procedure stack area size will be returned here
* @cr_ind_p: chain stack area start will be returned here
* @cr_size_p: chain stack area size will be returned here
* @new_crs: new CR* registers values will be returned here
* @usd_bottom_p: kernel data stack area top byte will be returned here
* @usd_sp_p: kernel data stack area top free byte will be returned here
* @usd_size_p: kernel data stack area free size will be returned here
*
* Calculate hardware stacks indexes and sizes, kernel data stack bottom
* and SP for specified frames number (level_num up or down).
* Before (if it is needed) skip some frames (down number).
* Hardware stacks can be handled only within the bounds of
* current active (resident) stack frame
*/
static inline int
get_hardware_stacks_frames_to_copy(e2k_stacks_t *cur_stacks,
e2k_mem_crs_t *cur_crs,
int down, int level_num, int copy_data_stack,
long *fp_ind_p, long *fp_size_p,
long *cr_ind_p, long *cr_size_p, e2k_mem_crs_t *new_crs,
long *usd_bottom_p, long *usd_sp_p, long *usd_size_p)
{
unsigned long flags;
int ret;
/*
* Read current stack parameters
*/
raw_all_irq_save(flags);
E2K_FLUSHCPU;
cur_stacks->sbr = READ_SBR_REG_VALUE();
cur_stacks->usd_hi = READ_USD_HI_REG();
cur_stacks->usd_lo = READ_USD_LO_REG();
AS_WORD(cur_crs->cr0_lo) = E2K_GET_DSREG_NV(cr0.lo);
AS_WORD(cur_crs->cr0_hi) = E2K_GET_DSREG_NV(cr0.hi);
AS_WORD(cur_crs->cr1_lo) = E2K_GET_DSREG_NV(cr1.lo);
AS_WORD(cur_crs->cr1_hi) = E2K_GET_DSREG_NV(cr1.hi);
cur_stacks->psp_hi = READ_PSP_HI_REG();
cur_stacks->psp_lo = READ_PSP_LO_REG();
cur_stacks->pcsp_hi = READ_PCSP_HI_REG();
cur_stacks->pcsp_lo = READ_PCSP_LO_REG();
raw_all_irq_restore(flags);
/*
* Do not copy copy_user_stacks()'s kernel data stack frame
*/
AS_STRUCT(cur_stacks->usd_lo).base +=
((AS_STRUCT(cur_crs->cr1_hi).ussz << 4) -
AS_STRUCT(cur_stacks->usd_hi).size);
cur_stacks->usd_hi.USD_hi_size =
AS_STRUCT(cur_crs->cr1_hi).ussz << 4;
DebugCT("current kernel data stack: top 0x%lx, base 0x%llx, size 0x%x\n",
cur_stacks->sbr,
AS_STRUCT(cur_stacks->usd_lo).base,
AS_STRUCT(cur_stacks->usd_hi).size);
DebugCT("current kernel procedure stack: base 0x%llx, size 0x%x, ind 0x%x\n",
AS_STRUCT(cur_stacks->psp_lo).base,
AS_STRUCT(cur_stacks->psp_hi).size,
AS_STRUCT(cur_stacks->psp_hi).ind);
DebugCT("current kernel procedure chain stack: base 0x%llx, size 0x%x, ind 0x%x\n",
AS_STRUCT(cur_stacks->pcsp_lo).base,
AS_STRUCT(cur_stacks->pcsp_hi).size,
AS_STRUCT(cur_stacks->pcsp_hi).ind);
DebugCT("current cr: IP 0x%llx ussz 0x%x wbs 0x%x\n",
AS_STRUCT(cur_crs->cr0_hi).ip << 3,
AS_STRUCT(cur_crs->cr1_hi).ussz << 4,
AS_STRUCT(cur_crs->cr1_lo).wbs * EXT_4_NR_SZ);
if (level_num) {
ret = get_n_stacks_frames_to_copy(cur_stacks, cur_crs,
down, level_num, copy_data_stack, 0, /* user_stacks */
fp_ind_p, fp_size_p,
cr_ind_p, cr_size_p, new_crs,
usd_bottom_p, usd_sp_p, usd_size_p);
} else {
*fp_ind_p = 0;
*cr_ind_p = 0;
ret = get_all_stacks_frames_to_copy(cur_stacks, cur_crs,
down, copy_data_stack, 0, /* user_stacks */
fp_size_p, cr_size_p, new_crs,
usd_bottom_p, usd_sp_p, usd_size_p);
}
if (ret != 0) {
DebugCT("could not copy "
"kernel stacks for new process or task\n");
}
return ret;
}
/**
* copy_hardware_stacks - copy current hardware and kernel data stacks
* @new_stacks: new stacks parameters; they will be corrected as needed
* @new_crs: new CR* registers will be returned here
* @down: number of frames to skip
* @level_num: number of frames to copy (copy all if 0)
* @copy_data_stack: whether to copy the kernel data stack
* @delta_sp: difference between new and old kernel stacks' bases
* will be returned here
* @delta_sz: difference between new and old kernel stacks' sizes
* will be returned here
* @disable_sge: disable overflow/underflow checking in copied
* hardware stacks
*
* 'disable_sge' is used to ensure that child will start with
* collapsed stacks
*/
static inline int
copy_hardware_stacks(e2k_stacks_t *new_stacks, e2k_mem_crs_t *new_crs,
int down, int level_num, int copy_data_stack,
e2k_addr_t *delta_sp, e2k_size_t *delta_sz)
{
e2k_stacks_t cur_stacks;
e2k_mem_crs_t cur_crs;
int error;
long fp_ind;
long fp_size;
long cr_ind;
long cr_size;
long usd_bottom;
long usd_sp;
long usd_size;
error = get_hardware_stacks_frames_to_copy(&cur_stacks, &cur_crs,
down, level_num, copy_data_stack,
&fp_ind, &fp_size,
&cr_ind, &cr_size, new_crs,
&usd_bottom, &usd_sp, &usd_size);
if (error)
return error;
error = do_copy_all_stacks(&cur_stacks, &cur_crs,
new_stacks, new_crs,
copy_data_stack, 0, /* user stacks */
fp_ind, fp_size,
cr_ind, cr_size,
usd_bottom, usd_sp, usd_size,
delta_sp, delta_sz);
return error;
}
/**
* prepare_kernel_frame - prepare for return to kernel function
* @stacks - allocated stacks' parameters (will be corrected)
* @crs - chain stack frame will be returned here
* @fn - function to return to
* @arg - function's argument
*
* Note that cr1_lo.psr value is taken from PSR register. This means
* that interrupts and sge are expected to be enabled by caller.
*/
static void prepare_kernel_frame(e2k_stacks_t *stacks, e2k_mem_crs_t *crs,
unsigned long fn, unsigned long arg)
{
e2k_cr0_lo_t cr0_lo;
e2k_cr0_hi_t cr0_hi;
e2k_cr1_lo_t cr1_lo;
e2k_cr1_hi_t cr1_hi;
e2k_psr_t psr;
unsigned long *frame;
/*
* Prepare @fn's frame in chain stack.
*/
AS(cr0_lo).pf = -1ULL;
AW(cr0_hi) = 0;
AS(cr0_hi).ip = fn >> 3;
AW(psr) = E2K_GET_DSREG_NV(psr);
BUG_ON(AS(psr).sge == 0);
AW(cr1_lo) = 0;
AS(cr1_lo).psr = AW(psr);
AS(cr1_lo).cuir = KERNEL_CODES_INDEX;
AS(cr1_lo).wbs = 1;
AW(cr1_hi) = 0;
AS(cr1_hi).ussz = AS(stacks->usd_hi).size / 16;
crs->cr0_lo = cr0_lo;
crs->cr0_hi = cr0_hi;
crs->cr1_lo = cr1_lo;
crs->cr1_hi = cr1_hi;
/*
* Reserve space in hardware stacks for @fn and @arg
*/
AS(stacks->pcsp_hi).ind = SZ_OF_CR;
AS(stacks->psp_hi).ind = EXT_4_NR_SZ;
/*
* Prepare function's argument
*/
frame = (unsigned long *) AS(stacks->psp_lo).base;
*frame = arg;
}
noinline
static int copy_kernel_stacks(struct task_struct *new_task,
unsigned long fn, unsigned long arg)
{
e2k_stacks_t new_stacks;
e2k_mem_crs_t new_crs;
e2k_addr_t psp_stk, pcsp_stk;
int ret;
/*
* How kernel thread creation works.
*
* 1) After schedule() to the new kthread we jump to __ret_from_fork().
* 2) __ret_from_fork() calls schedule_tail() to finish the things
* for scheduler.
* 3) When __ret_from_fork() returns @fn frame will be FILLed along
* with function's argument.
*/
/*
* Allocate stacks for new thread or task
*/
ret = create_kernel_stacks(new_task, &new_stacks);
if (ret != 0) {
DebugCT("could not create kernel "
"stacks for new task or thread\n");
return ret;
}
psp_stk = new_stacks.psp_lo.PSP_lo_base;
pcsp_stk = new_stacks.pcsp_lo.PCSP_lo_base;
DebugCT("hw stacks: p_stk 0x%lx pc_stk 0x%lx new_task 0x%p\n"
"data stack: top 0x%lx, base 0x%llx, size 0x%x, pt_regs 0x%p\n",
psp_stk, pcsp_stk, new_task, new_stacks.sbr,
AS_STRUCT(new_stacks.usd_lo).base,
AS_STRUCT(new_stacks.usd_hi).size,
task_thread_info(new_task)->pt_regs);
/*
* Put function IP and argument to chain and procedure stacks.
*/
prepare_kernel_frame(&new_stacks, &new_crs, fn, arg);
/*
* Update sw_regs with new stacks' parameters.
*/
init_sw_regs(new_task, &new_stacks, &new_crs, true);
return 0;
}
static inline int
init_u_data_stack(e2k_addr_t new_stk_base, s64 new_stk_size,
struct task_struct *new_task)
{
thread_info_t *new_thr = task_thread_info(new_task);
thread_info_t *curr_thr = current_thread_info();
struct vm_area_struct *vma, *cur, *prev;
u64 new_stk_top, new_usd_size, new_usd_base, delta;
bool is_growable;
DebugUS("new user data stack: base 0x%lx size 0x%lx\n",
new_stk_base, new_stk_size);
DebugUS("current user data stack: bottom 0x%lx, top 0x%lx, max size 0x%lx\n",
curr_thr->u_stk_base, curr_thr->u_stk_top, curr_thr->u_stk_sz);
BUG_ON(!new_task->mm);
if (new_stk_size) {
DebugUS("clone2() case: new stack base and size passed as args\n");
new_stk_top = new_stk_base + new_stk_size;
} else {
DebugUS("clone() case: only new stack SP passed as arg\n");
new_stk_top = new_stk_base;
}
down_read(&new_task->mm->mmap_sem);
vma = find_vma(new_task->mm, new_stk_top - 1);
if (!vma) {
DebugUS("find_vma() could not find VMA of stack area top\n");
goto out_efault;
}
DebugUS("find_vma() returned VMA 0x%p, start 0x%lx, end 0x%lx, mm 0x%p\n",
vma, vma->vm_start, vma->vm_end, new_task->mm);
if (new_stk_top <= vma->vm_start) {
DebugUS("new stack top address "
"is out of VMA area new_stk_top=%lx vma_start=%lx\n",
new_stk_top, vma->vm_start);
goto out_efault;
}
is_growable = !!(vma->vm_flags & VM_GROWSDOWN);
cur = vma;
prev = vma->vm_prev;
if (new_stk_size) {
/*
* Check passed area
*/
while (new_stk_base < cur->vm_start) {
if (!prev || cur->vm_start != prev->vm_end ||
((cur->vm_flags ^ prev->vm_flags) & VM_GROWSDOWN)) {
DebugUS("Bad passed area (prev 0x%lx)\n", prev);
goto out_efault;
}
cur = prev;
prev = prev->vm_prev;
}
} else {
/*
* We assume here that the stack area is contained
* in a single vma.
*/
new_stk_base = cur->vm_start;
new_stk_size = new_stk_top - new_stk_base;
}
if (new_stk_size > MAX_USD_HI_SIZE) {
u64 delta = new_stk_size - MAX_USD_HI_SIZE;
new_stk_base += delta;
new_stk_size -= delta;
}
/* Leave the guard page out of stack
* (see comment before expand_user_data_stack()) */
if (is_growable && new_stk_base == cur->vm_start) {
if (new_stk_size < PAGE_SIZE) {
up_read(&new_task->mm->mmap_sem);
DebugUS("stack size < PAGE_SIZE\n");
return -EINVAL;
}
new_stk_base += PAGE_SIZE;
new_stk_size -= PAGE_SIZE;
}
up_read(&new_task->mm->mmap_sem);
/* Align the stack */
delta = round_up(new_stk_base, E2K_ALIGN_STACK) - new_stk_base;
new_stk_base += delta;
new_stk_size -= delta;
delta = new_stk_top - round_down(new_stk_top, E2K_ALIGN_STACK);
new_stk_top -= delta;
new_stk_size -= delta;
if (new_stk_size < 0)
return -EINVAL;
/* Set registers in thread_info */
new_usd_base = new_stk_top & ~E2K_ALIGN_USTACK_MASK;
new_usd_size = new_stk_size;
new_thr->u_stk_base = new_stk_base;
new_thr->u_stk_sz = new_usd_size;
new_thr->u_stk_top = new_stk_top;
DebugUS("new user data stack: bottom 0x%lx, top 0x%lx, max size 0x%lx\n",
new_thr->u_stk_base, new_thr->u_stk_top, new_thr->u_stk_sz);
return 0;
out_efault:
up_read(&new_task->mm->mmap_sem);
return -EFAULT;
}
static int
create_user_stacks(unsigned long clone_flags, e2k_addr_t new_stk_base,
e2k_size_t new_stk_sz, struct task_struct *new_task,
e2k_stacks_t *new_stacks)
{
thread_info_t *new_thr = task_thread_info(new_task);
e2k_size_t u_ps_size;
e2k_size_t u_pcs_size;
int ret;
DebugUS("entered for stack base 0x%lx, size 0x%lx\n",
new_stk_base, new_stk_sz);
/* init user data stack pointers and registers info */
if (!(clone_flags & CLONE_VFORK)) {
ret = init_u_data_stack(new_stk_base, new_stk_sz, new_task);
if (ret != 0) {
DebugUS("could not create user data stack\n");
return ret;
}
}
if (UHWS_PSEUDO_MODE) {
u_ps_size = USER_P_STACK_AREA_SIZE;
u_pcs_size = USER_PC_STACK_AREA_SIZE;
} else {
/* init user's pointers and registers info of ps & pcs stacks */
u_ps_size = PAGE_ALIGN_DOWN(32 * new_thr->u_stk_sz);
if (u_ps_size < USER_P_STACK_INIT_SIZE) {
u_ps_size = USER_P_STACK_INIT_SIZE;
} else if (u_ps_size > USER_P_STACK_SIZE) {
u_ps_size = USER_P_STACK_SIZE;
}
/* create user's pcs stack == new procedure stack / 16 */
u_pcs_size = PAGE_ALIGN_DOWN(u_ps_size / 16);
if (u_pcs_size < USER_PC_STACK_INIT_SIZE) {
u_pcs_size = USER_PC_STACK_INIT_SIZE;
}
}
ret = create_user_hard_stacks(new_thr, new_stacks,
u_ps_size, USER_P_STACK_INIT_SIZE,
u_pcs_size, USER_PC_STACK_INIT_SIZE);
if (ret != 0) {
DebugUS("could not allocate user procedure or chain stack (PS or PCS)\n");
return ret;
}
new_stacks->psp_hi.PSP_hi_size += KERNEL_P_STACK_SIZE;
DebugUS("allocated procedure stack: base 0x%p, size: max 0x%lx init 0x%x\n",
GET_PS_BASE(new_thr), GET_PS_SIZE(new_thr),
new_stacks->psp_hi.PSP_hi_size);
new_stacks->pcsp_hi.PCSP_hi_size += KERNEL_PC_STACK_SIZE;
DebugUS("allocated procedure chain stack: base 0x%p, size: max 0x%lx init 0x%x\n",
GET_PCS_BASE(new_thr), GET_PCS_SIZE(new_thr),
new_stacks->pcsp_hi.PCSP_hi_size);
return 0;
}
static noinline int
copy_user_stacks(unsigned long clone_flags, e2k_addr_t new_stk_base,
e2k_size_t new_stk_sz, struct task_struct *new_task,
pt_regs_t *regs)
{
thread_info_t *new_ti = task_thread_info(new_task);
pt_regs_t *new_regs;
e2k_addr_t delta_sp = 0;
e2k_size_t delta_sz = 0, wd_psize = 0;
e2k_stacks_t new_stacks;
e2k_mem_crs_t new_crs;
long fp_ind, cr_ind;
int down, level_num, ret, sw_num = 0;
BUG_ON(sge_checking_enabled());
DebugUS("entered for stack base 0x%lx, size 0x%lx\n",
new_stk_base, new_stk_sz);
ret = alloc_hw_stack_mappings(new_ti);
if (ret)
return ret;
/*
* Create kernel data stack of new thread or task
*/
ret = create_kernel_data_stack(new_task, &new_stacks);
if (ret) {
DebugUS("could not create kernel data stack\n");
goto out_free_mappings;
}
/*
* Create user hardware stacks and initialize user data stack
*/
ret = create_user_stacks(clone_flags, new_stk_base, new_stk_sz,
new_task, &new_stacks);
if (ret) {
DebugUS("could not create user hardware stack\n");
goto out_free_kernel_c_stack;
}
/*
* Now cr_ind (pcsp_hi.ind) & fp_ind (psp_hi.ind) are here
* create_user_thread -> ttable -> sys_clone -> do_fork ->
* copy_process -> copy_thread -> copy_user_stacks
* We can't to do copy stacks and return on the new stack in do_fork()
* and copy_process() because do_fork() and copy_process() are
* arch-independed and we can't change do_fork() and copy_process().
* So on the new stack we want to return to ttable_entry() as
* from sys_clone after schedule -> __switch_to.
*
* We will change regs (psp, pcsp, usd, cr1, psr) in __switch_to.
* To get value of needed regs we should go to down 4 levels trough
* hard stacks.
*
* In this case cr_ind & fp_ind point to sys_clone. CR1 of sys_clone
* has IP of ttable_entry. If we change regs in __switch_to, then
* __switch_to will return straight to ttable_entry.
* (see comment before __switch_to for more info)
*
*
* Remember that:
* ttable -> sys_clone -> do_fork -> copy_process-> copy_thread ->
* copy_user_stacks -> go_hd_stk_down
*
* So cr_ind, fp_ind are for copy_user_stacks and start_cr1_lo
* has wbs of copy_thread(). So we should go 4 steps down:
* i == 0 fp_ind = fp_ind - cr1_lo.wbs for copy_thread()
* cr_ind = cr_ind - SZ_OF_CR for copy_thread()
* cr1_lo = base + cr_ind + CR1_LO_I wbs for copy_process()
* i == 1 fp_ind = fp_ind - cr1_lo.wbs for copy_process()
* cr_ind = cr_ind - SZ_OF_CR for copy_process()
* cr1_lo = base + cr_ind + CR1_LO_I has wbs for do_fork()
* i == 2 fp_ind = fp_ind - cr1_lo.wbs for do_fork()
* cr_ind = cr_ind - SZ_OF_CR for do_fork()
* cr1_lo = base + cr_ind + CR1_LO_I wbs for sys_clone()
* i == 3 fp_ind = fp_ind - cr1_lo.wbs for sys_clone()
* cr_ind = cr_ind - SZ_OF_CR for sys_clone()
* cr1_lo = base + cr_ind + CR1_LO_I wbs for ttable_entry()
*/
down = 4;
if (clone_flags & CLONE_VFORK)
level_num = 3;
else
level_num = 2;
ret = copy_hardware_stacks(&new_stacks, &new_crs,
down, level_num,
1, /* copy data stack */
&delta_sp,
&delta_sz);
if (ret) {
DebugUS("could not copy kernel stacksfor new user thread\n");
goto out_free_kernel_c_stack;
}
/*
* New thread pt_regs structure should be only one:
* the same as pt_regs structure of the system call and
* this structure was copied while copying local data
* stack frames. So it needs correct all stacks registers into
* the new pt_regs structure to point to the new created stacks
* Correct pt_regs structue address because of new thread
* will run on the new kernel data stack
*/
new_regs = (pt_regs_t *)((e2k_addr_t)regs + delta_sp);
DebugUS("pt_regs structure 0x%p "
"for new thread was copied from old regs 0x%p, "
"delta SP 0x%lx\n",
new_regs, regs, delta_sp);
CHECK_PT_REGS_LOOP(new_regs);
new_regs->next = NULL;
new_ti->pt_regs = new_regs;
if (delta_sz != 0)
fix_kernel_stacks_state(new_regs, delta_sz);
/*
* Now cr_ind (pcsp_hi.ind) & fp_ind (psp_hi.ind) for new stacks
* are here
* user -> create_thread -> ttable -> sys_clone
* pt_regs structure should point to thr state of stacks after
* system call to return to user. So it needs go down 2 levels
* to save this state in pt_regs structure
* User local data stack should be in empty state
*/
down = 1;
new_regs->crs.cr0_lo = new_crs.cr0_lo;
new_regs->crs.cr0_hi = new_crs.cr0_hi;
new_regs->crs.cr1_lo = new_crs.cr1_lo;
new_regs->crs.cr1_hi = new_crs.cr1_hi;
go_hd_stk_down(new_stacks.psp_hi,
new_stacks.pcsp_lo, new_stacks.pcsp_hi,
down,
&fp_ind, &cr_ind,
&wd_psize, &sw_num,
&new_regs->crs, 0 /* user_stacks */);
if (!(clone_flags & CLONE_VFORK)) {
new_regs->stacks.sbr = new_ti->u_stk_top;
#ifdef CONFIG_PROTECTED_MODE
if (current->thread.flags & E2K_FLAG_PROTECTED_MODE) {
new_regs->stacks.usd_lo.USD_lo_base =
(new_ti->u_stk_top & 0xFFFFFFFF) |
(regs->stacks.usd_lo.USD_lo_half &
0xFFF00000000);
} else
#endif
new_regs->stacks.usd_lo.USD_lo_base = new_ti->u_stk_top;
new_regs->stacks.usd_hi.USD_hi_size = new_ti->u_stk_sz;
AS(new_regs->crs.cr1_hi).ussz = new_ti->u_stk_sz >> 4;
DebugUS("new user local data stack: bottom 0x%lx, top 0x%lx, base 0x%llx, size 0x%x\n",
new_ti->u_stk_base, new_regs->stacks.sbr,
new_regs->stacks.usd_lo.USD_lo_base,
new_regs->stacks.usd_hi.USD_hi_size);
}
init_sw_regs(new_task, &new_stacks, &new_crs, false);
DebugCT("copy_user_stacks exited.\n");
return 0;
out_free_kernel_c_stack:
free_kernel_c_stack((void *) new_ti->k_stk_base);
new_ti->k_stk_base = 0;
out_free_mappings:
free_hw_stack_mappings(new_ti);
return ret;
}
static inline int
clone_all_hardware_stacks(struct task_struct *new_task,
e2k_stacks_t *cur_stacks, e2k_stacks_t *new_stacks,
e2k_mem_crs_t *new_crs, long fp_ind, long fp_size,
long cr_ind, long cr_size, unsigned long clone_flags)
{
thread_info_t *cur_ti = current_thread_info();
thread_info_t *new_ti = task_thread_info(new_task);
struct hw_stack_area *u_ps, *u_pcs, *u_ps_tmp, *u_pcs_tmp;
e2k_addr_t cur_ps_base, ps_base, cur_pcs_base, pcs_base;
e2k_size_t ps_size, pcs_size;
int ret;
if (UHWS_PSEUDO_MODE) {
u_ps_tmp = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (u_ps_tmp == NULL) {
ret = -ENOMEM;
goto out_u_ps;
}
u_ps = list_last_entry(&cur_ti->ps_list,
struct hw_stack_area, list_entry);
memcpy(u_ps_tmp, u_ps, sizeof(*u_ps));
INIT_LIST_HEAD(&u_ps_tmp->list_entry);
list_add_tail(&u_ps_tmp->list_entry, &new_ti->ps_list);
new_ti->cur_ps = u_ps_tmp;
u_pcs_tmp = kmalloc(sizeof(struct hw_stack_area), GFP_KERNEL);
if (u_pcs_tmp == NULL) {
ret = -ENOMEM;
goto out_u_pcs;
}
u_pcs = list_last_entry(&cur_ti->pcs_list,
struct hw_stack_area, list_entry);
memcpy(u_pcs_tmp, u_pcs, sizeof(*u_pcs));
INIT_LIST_HEAD(&u_pcs_tmp->list_entry);
list_add_tail(&u_pcs_tmp->list_entry, &new_ti->pcs_list);
new_ti->cur_pcs = u_pcs_tmp;
} else {
new_ti->ps_base = current_thread_info()->ps_base;
new_ti->pcs_base = current_thread_info()->pcs_base;
}
ps_base = (e2k_addr_t)GET_PS_BASE(cur_ti);
cur_ps_base = ps_base + GET_PS_OFFSET(cur_ti);
ps_size = GET_PS_SIZE(cur_ti);
DebugUS("procedure stack: start 0x%lx, current base 0x%lx, max size 0x%lx, kernel part size 0x%lx\n",
ps_base, cur_ps_base, ps_size, KERNEL_P_STACK_SIZE);
DebugUS("will clone procedure stack from ind 0x%lx, size 0x%lx\n",
fp_ind, fp_size);
pcs_base = (e2k_addr_t)GET_PCS_BASE(cur_ti);
cur_pcs_base = pcs_base + GET_PCS_OFFSET(cur_ti);
pcs_size = GET_PCS_SIZE(cur_ti);
DebugUS("chain procedure stack: start 0x%lx, current base 0x%lx, max size 0x%lx, kernel part size 0x%lx\n",
pcs_base, cur_pcs_base, pcs_size, KERNEL_PC_STACK_SIZE);
DebugUS("will clone procedure stack from ind 0x%lx, size 0x%lx\n",
cr_ind, cr_size);
if (fp_ind != 0 || cr_ind != 0)
panic("clone_all_hardware_stacks() start index to copy of PS 0x%lx or PCS 0x%lx is not zero\n",
fp_ind, cr_ind);
ret = do_clone_all_user_hard_stacks(new_task,
cur_ps_base, fp_size,
ps_base + ps_size + KERNEL_P_STACK_SIZE,
cur_pcs_base, cr_size,
pcs_base + pcs_size + KERNEL_PC_STACK_SIZE,
clone_flags, new_crs);
if (ret != 0) {
DebugUS("could not clone hardware stacks, error %d\n",
ret);
goto out_u_pcs;
}
DebugUS("new procedure stack: base 0x%llx, size 0x%x, correct ind from 0x%x to 0x%lx\n",
cur_stacks->psp_lo.PSP_lo_base,
cur_stacks->psp_hi.PSP_hi_size,
new_stacks->psp_hi.PSP_hi_ind, fp_size);
new_stacks->psp_lo = cur_stacks->psp_lo;
new_stacks->psp_hi = cur_stacks->psp_hi;
new_stacks->psp_hi.PSP_hi_ind = fp_size;
DebugUS("new chain procedure stack: base 0x%llx, size 0x%x, correct ind from 0x%x to 0x%lx\n",
cur_stacks->pcsp_lo.PCSP_lo_base,
cur_stacks->pcsp_hi.PCSP_hi_size,
new_stacks->pcsp_hi.PCSP_hi_ind, cr_size);
new_stacks->pcsp_lo = cur_stacks->pcsp_lo;
new_stacks->pcsp_hi = cur_stacks->pcsp_hi;
new_stacks->pcsp_hi.PCSP_hi_ind = cr_size;
return 0;
out_u_pcs:
kfree(new_ti->cur_pcs);
new_ti->cur_pcs = NULL;
out_u_ps:
kfree(new_ti->cur_ps);
new_ti->cur_ps = NULL;
return ret;
}
static noinline int
clone_user_stacks(struct task_struct *new_task, pt_regs_t *regs,
unsigned long clone_flags)
{
thread_info_t *new_ti = task_thread_info(new_task);
pt_regs_t *cur_regs, *new_regs, *cur_new_regs;
e2k_addr_t delta_sp = 0;
e2k_stacks_t cur_stacks, new_stacks;
e2k_mem_crs_t cur_crs, new_crs;
long fp_ind, fp_size, cr_ind, cr_size, usd_bottom,
usd_sp, usd_size;
int down, level_num, regs_num, ret;
DebugUS("entered\n");
ret = alloc_hw_stack_mappings(new_ti);
if (ret)
return ret;
/*
* Create data stack of new thread or task
*/
ret = create_kernel_data_stack(new_task, &new_stacks);
if (ret) {
DebugUS("could not create kernel data stack\n");
goto out_free_mappings;
}
/*
* Now cr_ind (pcsp_hi.ind) & fp_ind (psp_hi.ind) are here
* user_fork() -> ttable -> sys_clone -> do_fork ->
* copy_process -> copy_thread -> clone_user_stacks
* See comments for copy_user_stacks() about down value
*
* fork() duplicates process: it must copy full stacks
* and all frames of stacks
*/
down = 4;
level_num = 0;
ret = get_hardware_stacks_frames_to_copy(&cur_stacks, &cur_crs,
down, level_num, 1, /* copy_data_stack */
&fp_ind, &fp_size,
&cr_ind, &cr_size, &new_crs,
&usd_bottom, &usd_sp, &usd_size);
if (ret) {
DebugUS("could not get kernel stacks frames sizes to copy\n");
goto out_free_kernel_c_stack;
}
ret = clone_all_hardware_stacks(new_task,
&cur_stacks, &new_stacks, &new_crs,
fp_ind, fp_size, cr_ind, cr_size,
clone_flags);
if (ret) {
DebugUS("could not clone kernel hardware stacks\n");
goto out_free_kernel_c_stack;
}
ret = do_copy_data_stack(&new_stacks,
1, /* copy_data_stack */ 0, /* user_stacks */
usd_bottom, usd_sp, usd_size,
&delta_sp, NULL);
if (ret) {
DebugUS("could not copy kernel data stack\n");
goto out_free_kernel_c_stack;
}
new_regs = (pt_regs_t *)((e2k_addr_t)regs + delta_sp);
DebugUS("pt_regs structure 0x%p for new thread was copied from old regs 0x%p, delta SP 0x%lx\n",
new_regs, regs, delta_sp);
/*
* pt_regs structure from parent task were copied while copying
* local data stack, It needs correct all stacks pointers and
* registers into the thread/thread_info structure and
* pt_regs structures of new process
*/
cur_regs = regs;
cur_new_regs = new_regs;
regs_num = 0;
CHECK_PT_REGS_LOOP(regs);
while ((cur_regs = cur_regs->next) != NULL) {
CHECK_PT_REGS_LOOP(cur_regs);
cur_new_regs->next = (pt_regs_t *)
((e2k_addr_t)cur_regs + delta_sp);
DebugUS("pt_regs structure 0x%p for new thread is previous for regs 0x%p, copied from old regs 0x%p\n",
cur_new_regs->next, cur_new_regs, cur_regs);
cur_new_regs = cur_new_regs->next;
CHECK_PT_REGS_LOOP(cur_new_regs);
}
CHECK_PT_REGS_LOOP(new_regs);
cur_new_regs->next = NULL;
new_ti->pt_regs = new_regs;
regs_num = fix_all_kernel_stack_regs(new_ti, delta_sp);
DebugUS("corrected %d pt_regs structure for new process\n", regs_num);
DebugUS("new kernel data stack: top 0x%lx, base 0x%llx, size 0x%x, pt_regs 0x%p\n",
new_stacks.sbr, AS(new_stacks.usd_lo).base,
AS(new_stacks.usd_hi).size, new_ti->pt_regs);
init_sw_regs(new_task, &new_stacks, &new_crs, false);
DebugUS("clone_user_stacks exited.\n");
return 0;
out_free_kernel_c_stack:
free_kernel_c_stack((void *) new_ti->k_stk_base);
new_ti->k_stk_base = 0;
out_free_mappings:
free_hw_stack_mappings(new_ti);
return ret;
}
/*
* Clear unallocated memory pointers which can be allocated for parent task
*/
static void clear_thread_struct(struct task_struct *task)
{
struct thread_struct *thread = &task->thread;
thread_info_t *thread_info = task_thread_info(task);
DebugEX("started for task 0x%p CPU #%d\n", task, task_cpu(task));
#ifdef CONFIG_PROTECTED_MODE
thread_info->user_stack_addr = 0;
thread_info->user_stack_size = 0;
#endif
thread_info->k_stk_base = 0;
thread_info->k_stk_sz = 0;
thread_info->k_usd_lo.USD_lo_half = 0;
thread_info->k_usd_hi.USD_hi_half = 0;
bitmap_zero(thread_info->need_tlb_flush, NR_CPUS);
thread_info->mapped_p_stack = NULL;
thread_info->mapped_pc_stack = NULL;
memset(thread_info->mapped_p_pages, 0,
sizeof(thread_info->mapped_p_pages));
memset(thread_info->mapped_pc_pages, 0,
sizeof(thread_info->mapped_pc_pages));
#ifdef CONFIG_TC_STORAGE
thread->sw_regs.tcd = 0;
#endif
thread->intr_counter = 0;
thread_info->main_context_saved = false;
thread_info->free_hw_context = false;
thread_info->prev_ctx = NULL;
thread_info->next_ctx = NULL;
thread_info->hw_context_current = 0;
thread_info->pt_regs = NULL;
if (UHWS_PSEUDO_MODE) {
thread_info->cur_ps = NULL;
thread_info->cur_pcs = NULL;
} else {
thread_info->ps_base = NULL;
thread_info->pcs_base = NULL;
}
INIT_LIST_HEAD(&thread_info->ps_list);
INIT_LIST_HEAD(&thread_info->pcs_list);
INIT_LIST_HEAD(&thread_info->old_u_pcs_list);
thread_info->status = 0;
#if defined(CONFIG_SECONDARY_SPACE_SUPPORT)
thread_info->sc_restart_ignore = 0;
thread_info->rp_start = 0;
thread_info->rp_end = 0;
#endif
}
void setup_thread_stack(struct task_struct *p, struct task_struct *org)
{
*task_thread_info(p) = *task_thread_info(org);
task_thread_info(p)->task = p;
clear_thread_struct(p);
}
/*
* thread local storage (TLS) pointer
*/
#define TLS_REG 13
int copy_thread(unsigned long clone_flags,
unsigned long stack_base,
unsigned long stack_size,
struct task_struct *new_task)
{
struct pt_regs *regs = current_thread_info()->pt_regs;
int rval = 0;
thread_info_t *new_ti = task_thread_info(new_task);
#ifdef CONFIG_KERNEL_TIMES_ACCOUNT
int i;
scall_times_t *new_scall_times;
#endif /* CONFIG_KERNEL_TIMES_ACCOUNT */
DebugCT("entered: pt_regs %p\n", regs);
/* TODO FIXME on fork g_list should be copied, not zeroed */
if (!(clone_flags & CLONE_VM))
clear_g_list(task_thread_info(new_task));
if (test_ts_flag(TS_IDLE_CLONE)) {
DebugCT("idle clone\n");
return 0;
}
set_ti_status_flag(new_ti, TS_FORK);
#ifdef CONFIG_KERNEL_TIMES_ACCOUNT
for (i = 0; i < (sizeof(new_ti->times)) / sizeof(u16); i++)
((u16 *)(new_ti->times))[i] = new_task->pid;
new_ti->times_num = 1;
new_ti->times_index = 1;
new_scall_times = &(new_ti->times[0].of.syscall);
new_ti->times[0].type = SYSTEM_CALL_TT;
new_ti->fork_scall_times = new_scall_times;
new_scall_times->syscall_num = regs->scall_times->syscall_num;
new_scall_times->signals_num = 0;
#endif /* CONFIG_KERNEL_TIMES_ACCOUNT */
if (unlikely(current->flags & PF_KTHREAD)) {
/* creation of a kernel thread */
rval = copy_kernel_stacks(new_task, stack_base, stack_size);
} else {
if (clone_flags & CLONE_VM) {
BUG_ON(context_ti_key(new_ti));
/* For fork'ed and exec'ed threads this is done
* in init_new_context(). */
set_context_ti_key(new_ti,
alloc_context_key(new_task->mm));
/*
* Create a user thread or vfork()'ed process
*/
rval = copy_user_stacks(clone_flags, stack_base,
stack_size, new_task, regs);
DebugCT("copy_user_stacks() returned %d\n", rval);
/*
* Set a new TLS for the child thread.
*/
if (clone_flags & CLONE_SETTLS) {
new_task->thread.sw_regs.gbase[TLS_REG] =
regs->tls;
new_task->thread.sw_regs.gext[TLS_REG] = 0;
new_task->thread.sw_regs.tag[TLS_REG] = 0;
}
} else {
rval = clone_user_stacks(new_task, regs, clone_flags);
DebugCT("exited with return value %d\n", rval);
#ifdef CONFIG_PROTECTED_MODE
if (new_task->thread.flags & E2K_FLAG_PROTECTED_MODE &&
!(clone_flags & CLONE_VM)) {
/*
* create new malloc pool
*/
DebugCT("init_pool_malloc\n");
init_pool_malloc(current, new_task);
}
#endif
if (!rval && MONITORING_IS_ACTIVE)
init_monitors(new_task);
}
}
return rval;
}
void free_thread(struct task_struct *task)
{
struct thread_info *ti = task_thread_info(task);
/* We don't have to free virtual memory if this was
* a fork (and we'd have to switch mm to do it). */
if (task->mm != current->mm) {
if (UHWS_PSEUDO_MODE) {
if (ti->cur_ps) {
kfree(ti->cur_ps);
ti->cur_ps = NULL;
}
if (ti->cur_pcs) {
kfree(ti->cur_pcs);
ti->cur_pcs = NULL;
}
}
return;
}
BUG_ON(current->mm != current->active_mm);
/* It is possible that copy_process() failed after
* allocating stacks in copy_thread(). In this case
* we must free the allocated stacks. */
if (UHWS_PSEUDO_MODE) {
if (ti->cur_ps) {
free_user_p_stack(ti->cur_ps, true);
ti->cur_ps = NULL;
}
if (ti->cur_pcs) {
free_user_pc_stack(ti->cur_pcs, true);
ti->cur_pcs = NULL;
}
} else {
if (ti->ps_base) {
free_user_pc_stack_cont(ti->ps_base, ti->ps_size,
ti->ps_offset, ti->ps_top);
ti->ps_base = NULL;
}
if (ti->pcs_base) {
free_user_pc_stack_cont(ti->pcs_base, ti->pcs_size,
ti->pcs_offset, ti->pcs_top);
ti->pcs_base = NULL;
}
}
}
void deactivate_mm(struct task_struct *dead_task, struct mm_struct *mm)
{
int ret;
if (!mm)
return;
DebugEX("entered for task 0x%p %d [%s], mm 0x%lx\n",
dead_task, dead_task->pid, dead_task->comm, mm);
#if defined(CONFIG_MLT_STORAGE)
if (unlikely(MLT_NOT_EMPTY())) {
WARN_ONCE(true, "MLT isn't empty\n");
invalidate_MLT_context();
}
#endif
/*
* mm is going away, so we have to remap the stacks to the kernel space
*/
switch_to_kernel_hardware_stacks();
/*
* Free user hardware stacks, as kernel created them and only kernel
* knows about them. We must free both physical and virtual memory.
*/
ret = free_user_hardware_stacks();
if (ret) {
pr_err("deactivate_mm(): Could not free user hardware stacks, error %d\n",
ret);
print_stack(current);
}
DebugEX("successfully finished\n");
}
/**
* release_hw_stacks - clean up hardware stacks after the task has died
* @thread_info: pointer to task's thread_info
*
* The dead task must have either mapped hardware stacks (for user
* threads) or plain kernel hardware stacks (for kernel threads).
*/
static void release_hw_stacks(struct thread_info *thread_info)
{
/*
* WARNING: DO NOT PRINT ANYTHING HERE
*
* This function is called asynchronously during boot-up,
* and if we access serial port while kernel_init() tries
* to configure it during PCI scan, the serial port will
* hang. We do not want that.
*/
BUG_ON(!list_empty(&thread_info->old_u_pcs_list));
if (test_ti_status_flag(thread_info, TS_MAPPED_HW_STACKS))
free_hw_stack_pages(thread_info);
free_hw_stack_mappings(thread_info);
if (UHWS_PSEUDO_MODE) {
if (thread_info->cur_ps &&
(unsigned long) thread_info->cur_ps->base >= TASK_SIZE) {
BUG_ON(!list_is_singular(&thread_info->ps_list));
DebugEX("free kernel procedure stack list head 0x%p\n",
&thread_info->ps_list);
list_del(&thread_info->cur_ps->list_entry);
free_kernel_p_stack(thread_info->cur_ps->base);
kfree(thread_info->cur_ps);
thread_info->cur_ps = NULL;
}
if (thread_info->cur_pcs &&
(unsigned long) thread_info->cur_pcs->base >= TASK_SIZE) {
BUG_ON(!list_is_singular(&thread_info->pcs_list));
DebugEX("free kernel chain stack list head 0x%p\n",
&thread_info->pcs_list);
list_del(&thread_info->cur_pcs->list_entry);
free_kernel_pc_stack(thread_info->cur_pcs->base);
kfree(thread_info->cur_pcs);
thread_info->cur_pcs = NULL;
}
} else {
if ((unsigned long) thread_info->ps_base >= TASK_SIZE) {
DebugEX("free kernel procedure stack from 0x%p\n",
thread_info->ps_base);
free_kernel_p_stack(thread_info->ps_base);
thread_info->ps_base = NULL;
}
if ((unsigned long) thread_info->pcs_base >= TASK_SIZE) {
DebugEX("free kernel procedure chain stack from 0x%p\n",
thread_info->pcs_base);
free_kernel_pc_stack(thread_info->pcs_base);
thread_info->pcs_base = NULL;
}
}
}
void release_thread(struct task_struct *dead_task)
{
DebugP("is empty function for task %s pid %d\n",
dead_task->comm, dead_task->pid);
}
/*
* Free current thread data structures etc..
*/
extern void pthread_exit(void);
#ifdef CONFIG_MCST_RT
extern int unset_user_irq_thr(void);
#endif
void exit_thread(void)
{
thread_info_t *thread_info = current_thread_info();
DebugP("CPU#%d : started for %s pid %d, u_stk_base 0x%lx\n",
smp_processor_id(), current->comm, current->pid,
thread_info->u_stk_base);
#ifdef CONFIG_HAVE_EL_POSIX_SYSCALL
if (current->pobjs)
pthread_exit();
#endif
#ifdef CONFIG_MCST_RT
if (current->irq_to_be_proc != 0)
unset_user_irq_thr();
#endif
#ifdef CONFIG_PROTECTED_MODE
free_global_sp();
if (thread_info->user_stack_addr)
sys_munmap(thread_info->user_stack_addr,
thread_info->user_stack_size);
#endif /* CONFIG_PROTECTED_MODE */
#ifdef CONFIG_KERNEL_TIMES_ACCOUNT
if (debug_process_name != NULL &&
(strncmp(current->comm, debug_process_name,
debug_process_name_len) == 0)) {
sys_e2k_print_kernel_times(current, thread_info->times,
thread_info->times_num, thread_info->times_index);
}
#endif /* CONFIG_KERNEL_TIMES_ACCOUNT */
#ifdef CONFIG_MONITORS
if (MONITORING_IS_ACTIVE) {
process_monitors(current);
add_dead_proc_events(current);
}
#endif /* CONFIG_MONITORS */
DebugP("exit_thread exited.\n");
}
/*
* Makecontext/freecontext implementation for Elbrus architecture
*
* 1. Every context has a hardware stack associated with it. Those stacks
* are organized in a hash table.
*
* 2. Contexts are a property of a process so the hash table is located
* in 'mm_struct' structure.
*
* 3. There can be multiple contexts associated with the same hardware stack,
* so we have to use some stack's property as a key for the hash table.
* Keys are allocated from mm_context.hw_context_last and stored in
* thread_info.hw_context_current. On the user side the keys are stored
* in "sbr" field of struct uc_mcontext.
*
* 4. When we switch to a context that is on current hardware stack, we
* do a longjmp to a saved location. The same limitations as for setjmp/longjmp
* apply.
*
* 5. When we switch to a context that is on another hardware stack, we
* save current context and then switch all registers.
*
* 6. Contexts created by makecontext() are always present in the hash table,
* they are just marked if someone executes on them. This is needed for
* freecontext() to distinguish between "bad pointer" and "busy context"
* errors.
*
* 7. Since on e2k signal handlers take some space in kernel data stack,
* it must be created for each new context too.
*
* 8. When context created by makecontext() exits it should return to
* the kernel trampoline which will switch to kernel data stack and then
* switch to the context mentioned in uc_link or call do_exit().
*
* 9. The original context is not in the hash table, but we have to put
* it there on the first switch.
*/
static noinline int do_swapcontext_noinline(struct ucontext __user *oucp,
const struct ucontext __user *ucp,
bool save_prev_ctx, int format)
{
return do_swapcontext(oucp, ucp, save_prev_ctx, format);
}
#define printk printk_fixed_args
#ifndef CONFIG_E2S_CPU_RF_BUG
__interrupt
#endif
static notrace __noreturn void makecontext_trampoline()
{
int ret = 0;
e2k_usd_lo_t usd_lo;
e2k_usd_hi_t usd_hi;
struct thread_info *ti;
struct hw_context *ctx;
void __user *uc_link = NULL;
/*
* Switch to kernel stacks
*/
E2K_WAIT(_all_e);
ti = (struct thread_info *) E2K_GET_DSREG_NV(osr0);
E2K_SAVE_GREG(ti->gbase, ti->gext, ti->tag, 16, 17);
E2K_SET_DGREG_NV(16, ti);
E2K_SET_DGREG_NV(17, ti->task);
E2K_SET_DGREG_NV(19, (u64) ti->cpu);
SET_KERNEL_UPSR_WITH_DISABLED_NMI(0);
AW(usd_lo) = AW(ti->k_usd_lo);
AW(usd_hi) = AW(ti->k_usd_hi);
DISABLE_US_CLW();
WRITE_SBR_REG_VALUE(ti->k_stk_base + ti->k_stk_sz);
WRITE_USD_REG(usd_hi, usd_lo);
E2K_WAIT_ALL;
set_my_cpu_offset(per_cpu_offset(raw_smp_processor_id()));
raw_all_irq_enable();
/*
* Call switchcontext if needed
*/
ctx = ti->next_ctx;
/*
* Read uc_link from user
*/
if (ctx->ptr_format == CTX_32_BIT) {
u32 ucontext_32;
if (get_user(ucontext_32, (u32 *) ctx->p_uc_link)) {
ret = -EFAULT;
goto exit;
}
uc_link = (struct ucontext_32 *) (u64) ucontext_32;
} else if (ctx->ptr_format == CTX_64_BIT) {
u64 ucontext_64;
if (get_user(ucontext_64, (u64 *) ctx->p_uc_link)) {
ret = -EFAULT;
goto exit;
}
uc_link = (struct ucontext *) ucontext_64;
} else {
/* CTX_128_BIT */
e2k_ptr_t ptr;
u64 lo_val, hi_val;
int lo_tag, hi_tag;
int tag;
u32 size;
BEGIN_USR_PFAULT("makecontext_pfault", "0f");
E2K_LOAD_TAGGED_QWORD_AND_TAGS(ctx->p_uc_link,
lo_val, hi_val, lo_tag, hi_tag);
LBL_USR_PFAULT("makecontext_pfault", "0:");
if (END_USR_PFAULT) {
ret = -EFAULT;
goto exit;
}
AW(ptr).lo = lo_val;
AW(ptr).hi = hi_val;
size = AS(ptr).size - AS(ptr).curptr;
tag = (hi_tag << 4) | lo_tag;
/*
* Check that the pointer is good.
* We must be able to access uc_mcontext.sbr field.
*/
if (!size)
/* NULL pointer, just return */
goto exit;
if (tag != ETAGAPQ || size <
offsetof(struct ucontext_prot,
uc_mcontext.usd_lo)) {
ret = -EFAULT;
goto exit;
}
uc_link = (struct ucontext_prot *) E2K_PTR_PTR(ptr);
}
DebugCTX("ctx %lx, uc_link=%lx\n",
ctx, uc_link);
if (uc_link) {
ti->free_hw_context = true;
/*
* swapcontext() must not be inlined since current
* function has __interrupt attribute.
*/
ret = do_swapcontext_noinline(NULL, uc_link, false,
ctx->ptr_format);
DebugCTX("swapcontext failed with %d\n",
ret);
}
exit:
if (test_thread_flag(TIF_NOHZ))
user_exit();
/* Convert to user codes */
ret = -ret;
DebugCTX("calling do_exit with %d\n", ret);
do_exit((ret & 0xff) << 8);
}
#undef printk
static size_t get_ps_stack_size(size_t u_stk_size)
{
size_t ps_size;
ps_size = 32 * u_stk_size;
if (ps_size < USER_P_STACK_INIT_SIZE)
ps_size = USER_P_STACK_INIT_SIZE;
else if (ps_size < USER_P_STACK_SIZE)
ps_size = USER_P_STACK_SIZE;
return ps_size;
}
static size_t get_pcs_stack_size(size_t ps_size)
{
size_t pcs_size;
pcs_size = ps_size / 16;
if (pcs_size < USER_PC_STACK_INIT_SIZE)
pcs_size = USER_PC_STACK_INIT_SIZE;
return pcs_size;
}
/**
* alloc_hw_context - allocate kernel stacks for a context
* @main_context - is this main thread's context?
* @u_stk_size - user data stack size
*
* For the main thread stacks are already allocated and we only
* have to save their parameters.
*/
static struct hw_context *alloc_hw_context(bool main_context, size_t u_stk_size)
{
size_t u_pcs_size, u_ps_size;
struct hw_context *ctx;
struct hw_stack_area *user_psp_stk, *user_pcsp_stk;
void *pcs_base, *ps_base;
ctx = kmalloc(sizeof(*ctx), GFP_USER);
if (!ctx)
return NULL;
/*
* Make sure sys_swapcontext() won't use this context in the window
* between allocating and initializing main context.
*/
ctx->in_use = true;
if (main_context) {
/*
* Stacks have been allocated already
*/
struct thread_info *ti = current_thread_info();
ctx->ti.k_stk_base = ti->k_stk_base;
ctx->ti.k_stk_sz = ti->k_stk_sz;
ctx->ti.old_u_pcs_list = ti->old_u_pcs_list;
ctx->ti.ps_list = ti->ps_list;
ctx->ti.cur_ps = ti->cur_ps;
ctx->ti.pcs_list = ti->pcs_list;
ctx->ti.cur_pcs = ti->cur_pcs;
ctx->ti.ps_base = ti->ps_base;
ctx->ti.ps_size = ti->ps_size;
ctx->ti.ps_offset = ti->ps_offset;
ctx->ti.ps_top = ti->ps_top;
ctx->ti.pcs_base = ti->pcs_base;
ctx->ti.pcs_size = ti->pcs_size;
ctx->ti.pcs_offset = ti->pcs_offset;
ctx->ti.pcs_top = ti->pcs_top;
set_context_key(ctx, context_ti_key(current_thread_info()));
DebugCTX("ctx %lx allocated for main\n", ctx);
return ctx;
}
INIT_LIST_HEAD(&ctx->ti.old_u_pcs_list);
INIT_LIST_HEAD(&ctx->ti.ps_list);
INIT_LIST_HEAD(&ctx->ti.pcs_list);
if (UHWS_PSEUDO_MODE) {
u_ps_size = get_max_psp_size(USER_P_STACK_AREA_SIZE);
user_psp_stk = alloc_user_p_stack(u_ps_size, 0,
USER_P_STACK_INIT_SIZE);
if (!user_psp_stk)
goto free_context;
u_pcs_size = get_max_pcsp_size(USER_PC_STACK_AREA_SIZE);
user_pcsp_stk = alloc_user_pc_stack(u_pcs_size, 0,
USER_PC_STACK_INIT_SIZE);
if (!user_pcsp_stk)
goto free_p_stack;
list_add(&user_psp_stk->list_entry, &ctx->ti.ps_list);
list_add(&user_pcsp_stk->list_entry, &ctx->ti.pcs_list);
ctx->ti.cur_pcs = user_pcsp_stk;
ctx->ti.cur_ps = user_psp_stk;
} else {
u_ps_size = get_max_psp_size(get_ps_stack_size(u_stk_size));
ps_base = alloc_user_p_stack_cont(u_ps_size,
USER_P_STACK_INIT_SIZE);
if (!ps_base)
goto free_context;
u_pcs_size = get_max_pcsp_size(get_pcs_stack_size(u_ps_size));
pcs_base = alloc_user_pc_stack_cont(u_pcs_size,
USER_PC_STACK_INIT_SIZE);
if (!pcs_base)
goto free_p_stack;
ctx->ti.ps_base = ps_base;
ctx->ti.ps_size = u_ps_size;
ctx->ti.ps_offset = 0;
ctx->ti.ps_top = USER_P_STACK_INIT_SIZE;
ctx->ti.pcs_base = pcs_base;
ctx->ti.pcs_size = u_pcs_size;
ctx->ti.pcs_offset = 0;
ctx->ti.pcs_top = USER_PC_STACK_INIT_SIZE;
}
ctx->ti.k_stk_base = (unsigned long) alloc_kernel_c_stack();
if (!ctx->ti.k_stk_base)
goto free_pc_stack;
ctx->ti.k_stk_sz = KERNEL_C_STACK_SIZE;
DebugCTX("allocated stacks p: 0x%lx, pc: 0x%lx, data: 0x%lx-0x%lx\n",
GET_PS_BASE(&ctx->ti), GET_PCS_BASE(&ctx->ti),
ctx->ti.k_stk_base, ctx->ti.k_stk_base + ctx->ti.k_stk_sz);
set_context_key(ctx, alloc_context_key(current->mm));
return ctx;
free_pc_stack:
if (UHWS_PSEUDO_MODE) {
free_user_pc_stack(user_pcsp_stk, true);
ctx->ti.cur_pcs = NULL;
} else {
free_user_pc_stack_cont(pcs_base, u_pcs_size, 0,
USER_PC_STACK_INIT_SIZE);
}
free_p_stack:
if (UHWS_PSEUDO_MODE) {
free_user_p_stack(user_psp_stk, true);
ctx->ti.cur_ps = NULL;
} else {
free_user_p_stack_cont(ps_base, u_ps_size, 0,
USER_P_STACK_INIT_SIZE);
}
free_context:
kfree(ctx);
return NULL;
}
/**
* free_hw_context - free kernel stacks
* @ctx - context to free
* @exit - set if the whole process exits
*/
static void free_hw_context(struct hw_context *ctx, bool ctx_in_use, bool exit)
{
if (!ctx_in_use) {
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
kfree(ctx->task.ret_stack);
ctx->task.ret_stack = NULL;
#endif
if (UHWS_PSEUDO_MODE) {
struct hw_stack_area *u_ps, *u_pcs;
free_user_old_pc_stack_areas(&ctx->ti.old_u_pcs_list);
BUG_ON(list_empty(&ctx->ti.ps_list) ||
list_empty(&ctx->ti.pcs_list));
#if DEBUG_CTX_MODE
list_for_each_entry(u_ps, &ctx->ti.ps_list, list_entry)
DebugCTX("register stack at 0x%lx\n",
u_ps->base);
list_for_each_entry(u_pcs, &ctx->ti.pcs_list,
list_entry)
DebugCTX("chain stack at 0x%lx\n", u_pcs->base);
#endif
/*
* If the whole process is exiting we do not free
* address space - it is neither needed nor possible
* because by the time of arch_exit_mmap() call
* current->mm pointer has been set to NULL already.
*/
if (exit) {
list_for_each_entry(u_ps, &ctx->ti.ps_list,
list_entry)
kfree(u_ps);
INIT_LIST_HEAD(&ctx->ti.ps_list);
list_for_each_entry(u_pcs, &ctx->ti.pcs_list,
list_entry)
kfree(u_pcs);
INIT_LIST_HEAD(&ctx->ti.pcs_list);
} else {
free_user_p_stack_areas(&ctx->ti.ps_list);
free_user_pc_stack_areas(&ctx->ti.pcs_list);
}
ctx->ti.cur_ps = NULL;
ctx->ti.cur_pcs = NULL;
} else if (!exit) {
free_user_p_stack_cont(ctx->ti.ps_base,
ctx->ti.ps_size,
ctx->ti.ps_offset,
ctx->ti.ps_top);
free_user_pc_stack_cont(ctx->ti.pcs_base,
ctx->ti.pcs_size,
ctx->ti.pcs_offset,
ctx->ti.pcs_top);
}
free_kernel_c_stack((void *) ctx->ti.k_stk_base);
}
DebugCTX("ctx %lx freed (%s stack areas, %d stack memory)\n",
ctx, ctx_in_use ? "without" : "with",
exit ? "without" : "with");
kfree(ctx);
}
/**
* free_hw_contexts - called on thread exit to free all contexts
* @mm - mm that is being freed
*/
void free_hw_contexts(struct mm_struct *mm)
{
mm_context_t *mm_context = &mm->context;
int i;
for (i = 0; i < (1 << HW_CONTEXT_HASHBITS); i++) {
struct list_head *head = &mm_context->hw_contexts[i];
while (!list_empty(head)) {
struct hw_context *ctx;
spin_lock(&mm_context->hw_context_lock);
if (!list_empty(head)) {
ctx = list_first_entry(head, struct hw_context,
list_entry);
list_del(&ctx->list_entry);
} else {
ctx = NULL;
}
spin_unlock(&mm_context->hw_context_lock);
/*
* Cannot free context's stacks if they are used
* by some thread in current process. They will
* be freed by exit_mm() instead.
*/
if (ctx)
free_hw_context(ctx, ctx->in_use, true);
}
}
}
int set_user_ap(void __user *ptr, unsigned long addr, size_t len)
{
int ret = 0;
e2k_ptr_t qptr;
qptr = MAKE_AP(addr, len);
SAVE_USR_PFAULT;
current_thread_info()->usr_pfault_jump = PG_JMP;
DebugUA("will put to ptr 0x%p\n", ptr);
E2K_CMD_SEPARATOR;
E2K_SET_TAGS_AND_STORE_QUADRO(qptr, ptr);
E2K_CMD_SEPARATOR;
if (!current_thread_info()->usr_pfault_jump) {
DebugUAF("set_user_ap interrupted %p\n", ptr);
ret = -EFAULT;
}
END_USR_PFAULT;
return ret;
}
void set_kernel_ap(void *ptr, unsigned long addr, size_t len)
{
e2k_ptr_t qptr;
qptr = MAKE_AP(addr, len);
E2K_SET_TAGS_AND_STORE_QUADRO(qptr, ptr);
}
struct longjmp_regs {
e2k_cr0_hi_t cr0_hi;
e2k_cr1_lo_t cr1_lo;
e2k_cr1_hi_t cr1_hi;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
};
/**
* prepare_hw_context - set up all stacks for a user function execution
* @ctx: hardware context
* @func: user function
* @args_size: size of all arguments
* @args: pointer to arguments
* @u_stk_base: user data stack base
* @u_stk_size: user data stack size
* @protected: protected mode execution
*
* The first frame in the context is set to point to a kernel function
* which will handle return from @func, and the second frame points to
* @func.
*/
noinline
static int prepare_hw_context(struct longjmp_regs *user_regs,
struct hw_context *ctx, void (*func)(),
u64 args_size, void __user *args,
void *u_stk_base, size_t u_stk_size, bool protected)
{
e2k_stacks_t stacks;
e2k_mem_crs_t crs;
e2k_sbr_t u_sbr;
e2k_psr_t psr;
e2k_usd_lo_t u_usd_lo;
e2k_usd_hi_t u_usd_hi;
e2k_cr0_lo_t cr0_lo;
e2k_cr0_hi_t cr0_hi;
e2k_cr1_lo_t cr1_lo;
e2k_cr1_hi_t cr1_hi;
e2k_mem_crs_t *cs_frame;
void *ps_frame;
e2k_addr_t delta_sp;
e2k_size_t delta_sz;
u64 args_registers_size;
u64 args_stack_size;
u64 func_frame_size;
unsigned long func_frame_ptr;
int first_user_cui;
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
struct ftrace_ret_stack *ret_stack;
#endif
int i;
if (ALIGN(args_size, 16) + (protected ? 16 : 0) > u_stk_size)
return -EINVAL;
INIT_LIST_HEAD(&ctx->list_entry);
AW(stacks.pcsp_lo) = 0;
AS(stacks.pcsp_lo).base = (u64) GET_PCS_BASE(&ctx->ti);
AS(stacks.pcsp_lo).rw = 3;
AW(stacks.psp_lo) = 0;
AS(stacks.psp_lo).base = (u64) GET_PS_BASE(&ctx->ti);
AS(stacks.psp_lo).rw = 3;
/*
* Do not add kernel part size since we are executing
* under disabled PSR.sge.
*/
AS(stacks.pcsp_hi).size = USER_PC_STACK_INIT_SIZE;
AS(stacks.psp_hi).size = USER_P_STACK_INIT_SIZE;
/*
* Leave space for trampoline's frame so that there is space
* for the user function to return to.
*/
AS(stacks.pcsp_hi).ind = 2 * SZ_OF_CR;
/*
* And this is space for user function
*/
AS(stacks.psp_hi).ind = 4 * EXT_4_NR_SZ;
ps_frame = (void *) GET_PS_BASE(&ctx->ti);
AW(stacks.usd_lo) = 0;
AS(stacks.usd_lo).base = (u64) ctx->ti.k_stk_base + ctx->ti.k_stk_sz;
AS(stacks.usd_lo).rw = 3;
AS(stacks.usd_hi).ind = 0;
AS(stacks.usd_hi).size = ctx->ti.k_stk_sz;
/*
* Calculate user function frame's parameters.
*/
if (protected) {
args_registers_size = min(args_size, (u64) 64 - 16);
/* Data stack must be 16-bytes aligned. */
func_frame_size = ALIGN(args_size, 16) + 16;
} else {
args_registers_size = min(args_size, 64ULL);
/* Data stack must be 16-bytes aligned. */
func_frame_size = ALIGN(args_size, 16);
}
args_stack_size = args_size - args_registers_size;
func_frame_ptr = (unsigned long) u_stk_base + u_stk_size
- func_frame_size;
if (!access_ok(VERIFY_WRITE, func_frame_ptr, func_frame_size))
return -EFAULT;
DebugCTX("arguments: base 0x%lx, size %ld (regs %ld + stack %ld)\n",
args, args_size, args_registers_size, args_stack_size);
u_sbr = (u64) u_stk_base + u_stk_size;
AW(u_usd_lo) = 0;
AS(u_usd_lo).base = func_frame_ptr;
AS(u_usd_lo).rw = 3;
AS(u_usd_hi).ind = 0;
AS(u_usd_hi).size = u_stk_size - func_frame_size;
if (protected) {
e2k_pusd_lo_t pusd_lo;
/* Check that the stack does not cross 4Gb boundary */
if (((u64) u_stk_base & ~0xffffffffULL)
!= (u_sbr & ~0xffffffffULL)) {
DebugCTX("stack crosses 4Gb boundary\n");
return -EINVAL;
}
/*
* Set PSL to 2 (we must allow for two returns:
* first to user function and second to the trampoline)
*/
AW(pusd_lo) = AW(u_usd_lo);
AS(pusd_lo).psl = 2;
/*
* Set 'protected' bit
*/
AS(pusd_lo).p = 1;
AW(u_usd_lo) = AW(pusd_lo);
/*
* Put descriptor of user function frame in %qr0.
*/
set_kernel_ap(ps_frame, func_frame_ptr, args_size);
ps_frame += EXT_4_NR_SZ;
}
/*
* Put arguments into registers and user data stack
*/
BEGIN_USR_PFAULT("lbl_prepare_hw_context", "1f");
for (i = 0; i < args_registers_size / 16; i++) {
#if DEBUG_CTX_MODE
u64 val_lo, val_hi;
int tag_lo, tag_hi;
E2K_LOAD_TAGGED_QWORD_AND_TAGS(args + 16 * i, val_lo, val_hi,
tag_lo, tag_hi);
DebugCTX("register arguments: 0x%llx 0x%llx\n", val_lo, val_hi);
#endif
if (protected) {
/* We have to check for SAP */
u64 val_lo, val_hi;
int tag_lo, tag_hi;
e2k_sap_lo_t sap;
e2k_ap_lo_t ap;
E2K_LOAD_TAGGED_QWORD_AND_TAGS(args + 16 * i,
val_lo, val_hi, tag_lo, tag_hi);
if (((tag_hi << 4) | tag_lo) == ETAGAPQ &&
((val_lo & AP_ITAG_MASK) >>
AP_ITAG_SHIFT) == SAP_ITAG) {
/* SAP was passed, convert to AP
* for the new context since it has
* separate data stack. */
AW(sap) = val_lo;
AW(ap) = 0;
AS(ap).itag = AP_ITAG;
AS(ap).rw = AS(sap).rw;
AS(ap).base = AS(sap).base +
(current_thread_info()->u_stk_base &
0xFFFF00000000UL);
val_lo = AW(ap);
DebugCTX("\tfixed SAP: 0x%llx 0x%llx\n",
val_lo, val_hi);
}
E2K_STORE_TAGGED_QWORD(ps_frame + EXT_4_NR_SZ * i,
val_lo, val_hi, tag_lo, tag_hi);
} else {
E2K_MOVE_TAGGED_DWORD(args + 16 * i,
ps_frame + EXT_4_NR_SZ * i);
E2K_MOVE_TAGGED_DWORD(args + 16 * i + 8,
ps_frame + EXT_4_NR_SZ * i + 8);
}
}
if (2 * i < args_registers_size / 8) {
#if DEBUG_CTX_MODE
u64 val;
int tag;
E2K_LOAD_VAL_AND_TAGD(args + 16 * i, val, tag);
DebugCTX("register arguments: 0x%llx\n", val);
#endif
E2K_MOVE_TAGGED_DWORD(args + 16 * i,
ps_frame + EXT_4_NR_SZ * i);
}
#if DEBUG_CTX_MODE
for (i = 0; i + 1 < args_stack_size / 8; i += 2) {
u64 val_lo, val_hi;
int tag_lo, tag_hi;
E2K_LOAD_TAGGED_QWORD_AND_TAGS(args +
args_registers_size + 8 * i,
val_lo, val_hi, tag_lo, tag_hi);
DebugCTX("stack arguments: 0x%llx 0x%llx\n",
val_lo, val_hi);
}
#endif
LBL_USR_PFAULT("lbl_prepare_hw_context", "1:");
if (END_USR_PFAULT)
return -EFAULT;
if (args_stack_size) {
DebugCTX("Copying stack arguments to 0x%lx\n",
(void *) func_frame_ptr + 64);
if (copy_in_user_with_tags((void *) func_frame_ptr + 64,
args + args_registers_size, args_stack_size))
return -EFAULT;
}
user_regs->pcsp_lo = stacks.pcsp_lo;
user_regs->pcsp_hi = stacks.pcsp_hi;
/*
* We are here:
*
* hard_sys_calls -> sys_makecontext -> prepare_hw_context
*
* We want to copy hard_sys_calls() frame.
*/
copy_hardware_stacks(&stacks, &crs, 1 /* down */,
1 /* level_num */, 1 /* copy data stack */,
&delta_sp, &delta_sz);
DebugCTX("copied stack, delta_sp = 0x%lx\n",
delta_sp);
ctx->pcsp_lo = stacks.pcsp_lo;
ctx->pcsp_hi = stacks.pcsp_hi;
ctx->psp_lo = stacks.psp_lo;
ctx->psp_hi = stacks.psp_hi;
ctx->k_usd_lo = stacks.usd_lo;
ctx->k_usd_hi = stacks.usd_hi;
ctx->k_sbr = (u64) ctx->ti.k_stk_base + ctx->ti.k_stk_sz;
ctx->u_stk_top = (unsigned long) u_stk_base + u_stk_size;
#ifdef CONFIG_GREGS_CONTEXT
memset(ctx->gbase, 0, sizeof(ctx->gbase));
memset(ctx->gext, 0, sizeof(ctx->gext));
memset(ctx->tag, ETAGEWD, sizeof(ctx->tag));
AS(ctx->bgr).cur = 0;
AS(ctx->bgr).val = 0xff;
#endif
/*
* Set chain stack for hard_sys_calls()
*/
ctx->cr0_lo = crs.cr0_lo;
ctx->cr0_hi = crs.cr0_hi;
ctx->cr1_lo = crs.cr1_lo;
ctx->cr1_hi = crs.cr1_hi;
/*
* Set chain stack for the user function
*/
cs_frame = (e2k_mem_crs_t *) (GET_PCS_BASE(&ctx->ti) + 2 * SZ_OF_CR);
AS(cr0_lo).pf = -1ULL;
AW(cr0_hi) = 0;
AS(cr0_hi).ip = (unsigned long) func >> 3;
AW(cr1_lo) = 0;
AS(cr1_lo).psr = AW(E2K_USER_INITIAL_PSR);
#ifdef CONFIG_KERNEL_CODE_CONTEXT
first_user_cui = USER_CODES_32_INDEX;
#else
first_user_cui = 1;
#endif
AS(cr1_lo).cuir = first_user_cui;
AS(cr1_lo).wbs = 4;
AW(cr1_hi) = 0;
AS(cr1_hi).ussz = AS(u_usd_hi).size / 16;
cs_frame->cr0_lo = cr0_lo;
cs_frame->cr0_hi = cr0_hi;
cs_frame->cr1_lo = cr1_lo;
cs_frame->cr1_hi = cr1_hi;
user_regs->cr0_hi = cr0_hi;
user_regs->cr1_lo = cr1_lo;
user_regs->cr1_hi = cr1_hi;
/*
* Set chain stack for the trampoline
*/
cs_frame = (e2k_mem_crs_t *) (GET_PCS_BASE(&ctx->ti) + SZ_OF_CR);
AS(cr0_hi).ip = (u64) &makecontext_trampoline >> 3;
AW(psr) = E2K_GET_DSREG_NV(psr);
AS(psr).ie = 0;
AS(psr).nmie = 0;
AS(psr).sge = 0;
AS(cr1_lo).psr = AW(psr);
AS(cr1_lo).cuir = KERNEL_CODES_INDEX;
AS(cr1_lo).wbs = 0;
AS(cr1_hi).ussz = ctx->ti.k_stk_sz / 16;
cs_frame->cr0_lo = cr0_lo;
cs_frame->cr0_hi = cr0_hi;
cs_frame->cr1_lo = cr1_lo;
cs_frame->cr1_hi = cr1_hi;
/*
* Initialize thread_info
*/
ctx->ti.pt_regs = (void *) current_thread_info()->pt_regs +
delta_sp;
DebugCTX("old pt_regs at 0x%lx, new at %lx\n",
current_thread_info()->pt_regs,
ctx->ti.pt_regs);
DebugCTX("old pt_regs->ctpr1 is 0x%lx, new is %lx\n",
current_thread_info()->pt_regs->ctpr1,
ctx->ti.pt_regs->ctpr1);
/* Only one pt_regs structure for the new context */
ctx->ti.pt_regs->next = NULL;
ctx->ti.k_usd_hi = ctx->k_usd_hi;
ctx->ti.k_usd_lo = ctx->k_usd_lo;
ctx->ti.upsr = E2K_USER_INITIAL_UPSR;
ctx->ti.u_stk_base = (u64) u_stk_base;
ctx->ti.u_stk_sz = u_stk_size;
ctx->ti.u_stk_top = (u64) u_stk_base + u_stk_size;
ctx->ti.pusd_pil = 0;
#ifdef CONFIG_PROTECTED_MODE
ctx->ti.g_list = 0;
ctx->ti.multithread_address = 0;
ctx->ti.lock = NULL;
ctx->ti.user_stack_addr = 0;
ctx->ti.user_stack_size = 0;
#endif
/*
* Initialize task_struct
*/
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH
* sizeof(struct ftrace_ret_stack), GFP_KERNEL);
if (ret_stack) {
atomic_set(&ctx->task.tracing_graph_pause, 0);
atomic_set(&ctx->task.trace_overrun, 0);
ctx->task.ftrace_timestamp = 0;
}
ctx->task.ret_stack = ret_stack;
ctx->task.curr_ret_stack = -1;
DebugFTRACE("ret_stack %p allocated\n", ret_stack);
#endif
/*
* Set stack registers in new pt_regs
*/
cs_frame = (e2k_mem_crs_t *) (GET_PCS_BASE(&ctx->ti) + 2 * SZ_OF_CR);
ctx->ti.pt_regs->crs = *cs_frame;
ctx->ti.pt_regs->stacks.sbr = u_sbr;
ctx->ti.pt_regs->stacks.usd_lo = u_usd_lo;
ctx->ti.pt_regs->stacks.usd_hi = u_usd_hi;
ctx->in_use = false;
return 0;
}
static inline u64 key2index(u64 key)
{
return jhash_1word((u32) key, (u32) (key >> 32))
& ((1 << HW_CONTEXT_HASHBITS) - 1);
}
static inline struct hw_context *key2hw_context(u64 key, u64 index,
mm_context_t *mm_context, bool used)
{
struct hw_context *ctx;
list_for_each_entry(ctx, &mm_context->hw_contexts[index],
list_entry) {
if (context_key_matches(key, ctx)) {
if (unlikely(ctx->in_use && !used ||
!ctx->in_use && used)) {
DebugCTX("ucp %lx found by key %llx but in use == %d\n",
ctx, key, used);
break;
}
goto found;
}
}
ctx = NULL;
found:
DebugCTX("ucp %lx found by key %llx (index %lld)\n",
ctx, key, index);
return ctx;
}
int sys_makecontext(struct ucontext __user *ucp, void (*func)(),
u64 args_size, void __user *args, int sigsetsize)
{
void *u_stk_base;
size_t u_stk_size;
struct hw_context *ctx;
struct list_head *hb;
struct longjmp_regs user_regs;
mm_context_t *mm_context = &current->mm->context;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
u32 index;
int ret;
DebugCTX("ucp %lx started\n", ucp);
if (!access_ok(ACCESS_WRITE, ucp, sizeof(*ucp))) {
ret = -EFAULT;
goto out;
}
ret = __get_user(u_stk_base, &ucp->uc_stack.ss_sp);
ret |= __get_user(u_stk_size, &ucp->uc_stack.ss_size);
if (ret) {
ret = -EFAULT;
goto out;
}
u_stk_size -= PTR_ALIGN(u_stk_base, 16) - u_stk_base;
u_stk_base = PTR_ALIGN(u_stk_base, 16);
u_stk_size = round_down(u_stk_size, 16);
DebugCTX("user stack at %lx, size=%lx\n",
u_stk_base, u_stk_size);
if (sigsetsize != sizeof(sigset_t)) {
ret = -EINVAL;
goto out;
}
ctx = alloc_hw_context(false, u_stk_size);
if (!ctx) {
ret = -ENOMEM;
goto out;
}
DebugCTX("ctx %lx allocated, key=%lx\n", ctx,
context_key(ctx));
ctx->p_uc_link = &ucp->uc_link;
ctx->ptr_format = CTX_64_BIT;
ret = prepare_hw_context(&user_regs, ctx, func, args_size, args,
u_stk_base, u_stk_size, false);
if (ret)
goto out_free_ctx;
/*
* Initialize user structure
*/
GET_FPU_DEFAULTS(fpsr, fpcr, pfpfr);
ret = __clear_user(&ucp->uc_sigmask, sigsetsize);
ret |= __put_user(context_key(ctx), &ucp->uc_mcontext.sbr);
ret |= __put_user(AW(user_regs.cr0_hi), &ucp->uc_mcontext.cr0_hi);
ret |= __put_user(AW(user_regs.cr1_lo), &ucp->uc_mcontext.cr1_lo);
ret |= __put_user(AW(user_regs.cr1_hi), &ucp->uc_mcontext.cr1_hi);
ret |= __put_user(AW(user_regs.pcsp_lo), &ucp->uc_mcontext.pcsp_lo);
ret |= __put_user(AW(user_regs.pcsp_hi), &ucp->uc_mcontext.pcsp_hi);
ret |= __put_user(AW(fpcr), &ucp->uc_extra.fpcr);
ret |= __put_user(AW(fpsr), &ucp->uc_extra.fpsr);
ret |= __put_user(AW(pfpfr), &ucp->uc_extra.pfpfr);
if (ret) {
ret = -EFAULT;
goto out_free_ctx;
}
index = key2index(context_key(ctx));
if (unlikely(key2hw_context(context_key(ctx), index, mm_context, true)
|| key2hw_context(context_key(ctx), index, mm_context,
false))) {
/*
* This key is already in use.
*/
ret = -EBUSY;
goto out_free_ctx;
}
spin_lock(&mm_context->hw_context_lock);
DebugCTX("adding ctx %lx with key %llx and index %lld\n",
ctx, context_key(ctx), index);
hb = &mm_context->hw_contexts[index];
list_add(&ctx->list_entry, hb);
spin_unlock(&mm_context->hw_context_lock);
return 0;
out_free_ctx:
free_hw_context(ctx, false, false);
out:
DebugCTX("failed with %d\n", ret);
return ret;
}
#ifdef CONFIG_COMPAT
int compat_sys_makecontext(struct ucontext_32 __user *ucp,
void (*func)(), u64 args_size, void __user *args,
int sigsetsize)
{
void *u_stk_base;
u32 __u_stk_base;
size_t u_stk_size;
struct hw_context *ctx;
struct list_head *hb;
struct longjmp_regs user_regs;
mm_context_t *mm_context = &current->mm->context;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
u32 index;
int ret;
DebugCTX("ucp %lx started\n", ucp);
if (!access_ok(ACCESS_WRITE, ucp, sizeof(*ucp))) {
ret = -EFAULT;
goto out;
}
ret = __get_user(__u_stk_base, &ucp->uc_stack.ss_sp);
ret |= __get_user(u_stk_size, &ucp->uc_stack.ss_size);
if (ret) {
ret = -EFAULT;
goto out;
}
u_stk_base = (void *) (unsigned long) __u_stk_base;
u_stk_size -= PTR_ALIGN(u_stk_base, 16) - u_stk_base;
u_stk_base = PTR_ALIGN(u_stk_base, 16);
u_stk_size = round_down(u_stk_size, 16);
DebugCTX("user stack at %lx, size=%lx\n",
u_stk_base, u_stk_size);
if (sigsetsize != sizeof(sigset_t)) {
ret = -EINVAL;
goto out;
}
ctx = alloc_hw_context(false, u_stk_size);
if (!ctx) {
ret = -ENOMEM;
goto out;
}
DebugCTX("ctx %lx allocated, key=%lx\n", ctx,
context_key(ctx));
ctx->p_uc_link = &ucp->uc_link;
ctx->ptr_format = CTX_32_BIT;
ret = prepare_hw_context(&user_regs, ctx, func, args_size, args,
u_stk_base, u_stk_size, false);
if (ret)
goto out_free_ctx;
/*
* Initialize user structure
*/
GET_FPU_DEFAULTS(fpsr, fpcr, pfpfr);
ret = __clear_user(&ucp->uc_sigmask, sigsetsize);
ret |= __put_user(context_key(ctx), &ucp->uc_mcontext.sbr);
ret |= __put_user(AW(user_regs.cr0_hi), &ucp->uc_mcontext.cr0_hi);
ret |= __put_user(AW(user_regs.cr1_lo), &ucp->uc_mcontext.cr1_lo);
ret |= __put_user(AW(user_regs.cr1_hi), &ucp->uc_mcontext.cr1_hi);
ret |= __put_user(AW(user_regs.pcsp_lo), &ucp->uc_mcontext.pcsp_lo);
ret |= __put_user(AW(user_regs.pcsp_hi), &ucp->uc_mcontext.pcsp_hi);
ret |= __put_user(AW(fpcr), &ucp->uc_extra.fpcr);
ret |= __put_user(AW(fpsr), &ucp->uc_extra.fpsr);
ret |= __put_user(AW(pfpfr), &ucp->uc_extra.pfpfr);
if (ret) {
ret = -EFAULT;
goto out_free_ctx;
}
index = key2index(context_key(ctx));
spin_lock(&mm_context->hw_context_lock);
DebugCTX("adding ctx %lx with key %llx and index %lld\n",
ctx, context_key(ctx), index);
hb = &mm_context->hw_contexts[index];
list_add(&ctx->list_entry, hb);
spin_unlock(&mm_context->hw_context_lock);
return 0;
out_free_ctx:
free_hw_context(ctx, false, false);
out:
DebugCTX("failed with %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_PROTECTED_MODE
int protected_sys_makecontext(struct ucontext_prot __user *ucp,
void (*func)(), u64 args_size,
void __user *args, int sigsetsize)
{
e2k_ptr_t stack_ptr;
void *u_stk_base;
size_t u_stk_size;
struct hw_context *ctx;
struct list_head *hb;
struct longjmp_regs user_regs;
mm_context_t *mm_context = &current->mm->context;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
u32 index;
int ret;
DebugCTX("ucp %lx started\n", ucp);
if (!access_ok(ACCESS_WRITE, ucp, sizeof(*ucp))) {
ret = -EFAULT;
goto out;
}
ret = __copy_from_user(&stack_ptr, &ucp->uc_stack.ss_sp, 16);
ret |= __get_user(u_stk_size, &ucp->uc_stack.ss_size);
if (ret) {
ret = -EFAULT;
goto out;
}
if (AS(stack_ptr).size < u_stk_size)
return -EINVAL;
u_stk_base = (void *) E2K_PTR_PTR(stack_ptr);
u_stk_size -= PTR_ALIGN(u_stk_base, 16) - u_stk_base;
u_stk_base = PTR_ALIGN(u_stk_base, 16);
u_stk_size = round_down(u_stk_size, 16);
DebugCTX("user stack at %lx, size=%lx\n",
u_stk_base, u_stk_size);
if (sigsetsize != sizeof(sigset_t)) {
ret = -EINVAL;
goto out;
}
ctx = alloc_hw_context(false, u_stk_size);
if (!ctx) {
ret = -ENOMEM;
goto out;
}
DebugCTX("ctx %lx allocated, key=%lx\n", ctx,
context_key(ctx));
ctx->p_uc_link = &ucp->uc_link;
ctx->ptr_format = CTX_128_BIT;
ret = prepare_hw_context(&user_regs, ctx, func, args_size, args,
u_stk_base, u_stk_size, true);
if (ret)
goto out_free_ctx;
/*
* Initialize user structure
*/
GET_FPU_DEFAULTS(fpsr, fpcr, pfpfr);
ret = __clear_user(&ucp->uc_sigmask, sigsetsize);
ret |= __put_user(context_key(ctx), &ucp->uc_mcontext.sbr);
ret |= __put_user(AW(user_regs.cr0_hi), &ucp->uc_mcontext.cr0_hi);
ret |= __put_user(AW(user_regs.cr1_lo), &ucp->uc_mcontext.cr1_lo);
ret |= __put_user(AW(user_regs.cr1_hi), &ucp->uc_mcontext.cr1_hi);
ret |= __put_user(AW(user_regs.pcsp_lo), &ucp->uc_mcontext.pcsp_lo);
ret |= __put_user(AW(user_regs.pcsp_hi), &ucp->uc_mcontext.pcsp_hi);
ret |= __put_user(AW(fpcr), &ucp->uc_extra.fpcr);
ret |= __put_user(AW(fpsr), &ucp->uc_extra.fpsr);
ret |= __put_user(AW(pfpfr), &ucp->uc_extra.pfpfr);
if (ret) {
ret = -EFAULT;
goto out_free_ctx;
}
index = key2index(context_key(ctx));
/*
* Fix global pointers before making the context available
*/
mark_all_global_sp(current_thread_info()->pt_regs, current->pid);
spin_lock(&mm_context->hw_context_lock);
DebugCTX("adding ctx %lx with key %llx and index %lld\n",
ctx, context_key(ctx), index);
hb = &mm_context->hw_contexts[index];
list_add(&ctx->list_entry, hb);
spin_unlock(&mm_context->hw_context_lock);
return 0;
out_free_ctx:
free_hw_context(ctx, false, false);
out:
DebugCTX("failed with %d\n", ret);
return ret;
}
#endif
static int do_freecontext(u64 free_key)
{
mm_context_t *mm_context = &current->mm->context;
u64 free_index;
struct hw_context *ctx;
int ret = 0;
free_index = key2index(free_key);
spin_lock(&mm_context->hw_context_lock);
ctx = key2hw_context(free_key, free_index, mm_context, false);
if (!ctx) {
ret = -EINVAL;
goto out;
}
list_del(&ctx->list_entry);
out:
spin_unlock(&mm_context->hw_context_lock);
if (!ret)
free_hw_context(ctx, ctx->in_use, false);
return ret;
}
int sys_freecontext(struct ucontext __user *ucp)
{
u64 free_key;
DebugCTX("entered for ucp %lx\n", ucp);
if (get_user(free_key, &ucp->uc_mcontext.sbr))
return -EFAULT;
return do_freecontext(free_key);
}
#ifdef CONFIG_COMPAT
int compat_sys_freecontext(struct ucontext_32 __user *ucp)
{
u64 free_key;
DebugCTX("entered for ucp %lx\n", ucp);
if (get_user(free_key, &ucp->uc_mcontext.sbr))
return -EFAULT;
return do_freecontext(free_key);
}
#endif
#ifdef CONFIG_PROTECTED_MODE
int protected_sys_freecontext(struct ucontext_prot __user *ucp)
{
u64 free_key;
DebugCTX("entered for ucp %lx\n", ucp);
if (get_user(free_key, &ucp->uc_mcontext.sbr))
return -EFAULT;
return do_freecontext(free_key);
}
#endif
/*
* Actually do the switch to another hardware stack described by ucp.
*
* Called from sys_setcontext() or sys_swapcontext().
*/
#define printk printk_fixed_args
__interrupt
static noinline long switch_hw_contexts(
struct hw_context *__restrict prev_ctx,
struct hw_context *__restrict next_ctx,
u32 fpcr, u32 fpsr, u32 pfpfr)
{
#if DEBUG_CTX_MODE
struct hw_stack_area *u_ps, *u_pcs;
DebugCTX("Before switching (pt_regs 0x%lx):\n",
current_thread_info()->pt_regs);
print_stack_frames(current, 0, 0);
list_for_each_entry(u_ps, &current_thread_info()->ps_list, list_entry)
DebugCTX("register stack at 0x%lx\n", u_ps->base);
list_for_each_entry(u_pcs, &current_thread_info()->pcs_list, list_entry)
DebugCTX("chain stack at 0x%lx\n", u_pcs->base);
#endif
next_ctx->in_use = true;
/*
* Save thread_info
*/
prev_ctx->ti.hw_context_current =
current_thread_info()->hw_context_current;
prev_ctx->ti.pt_regs = current_thread_info()->pt_regs;
prev_ctx->ti.k_usd_hi = current_thread_info()->k_usd_hi;
prev_ctx->ti.k_usd_lo = current_thread_info()->k_usd_lo;
prev_ctx->ti.k_stk_base = current_thread_info()->k_stk_base;
prev_ctx->ti.k_stk_sz = current_thread_info()->k_stk_sz;
prev_ctx->ti.upsr = current_thread_info()->upsr;
prev_ctx->ti.u_stk_base = current_thread_info()->u_stk_base;
prev_ctx->ti.u_stk_sz = current_thread_info()->u_stk_sz;
prev_ctx->ti.u_stk_top = current_thread_info()->u_stk_top;
/* We moved the lists so the pointers in them
* must be fixed to point to the new location. */
if (list_empty(&current_thread_info()->old_u_pcs_list)) {
INIT_LIST_HEAD(&prev_ctx->ti.old_u_pcs_list);
} else {
prev_ctx->ti.old_u_pcs_list =
current_thread_info()->old_u_pcs_list;
current_thread_info()->old_u_pcs_list.next->prev =
&prev_ctx->ti.old_u_pcs_list;
current_thread_info()->old_u_pcs_list.prev->next =
&prev_ctx->ti.old_u_pcs_list;
}
if (list_empty(&current_thread_info()->ps_list)) {
INIT_LIST_HEAD(&prev_ctx->ti.ps_list);
} else {
prev_ctx->ti.ps_list = current_thread_info()->ps_list;
current_thread_info()->ps_list.next->prev =
&prev_ctx->ti.ps_list;
current_thread_info()->ps_list.prev->next =
&prev_ctx->ti.ps_list;
}
if (list_empty(&current_thread_info()->pcs_list)) {
INIT_LIST_HEAD(&prev_ctx->ti.pcs_list);
} else {
prev_ctx->ti.pcs_list = current_thread_info()->pcs_list;
current_thread_info()->pcs_list.next->prev =
&prev_ctx->ti.pcs_list;
current_thread_info()->pcs_list.prev->next =
&prev_ctx->ti.pcs_list;
}
prev_ctx->ti.cur_ps = current_thread_info()->cur_ps;
prev_ctx->ti.cur_pcs = current_thread_info()->cur_pcs;
prev_ctx->ti.ps_base = current_thread_info()->ps_base;
prev_ctx->ti.ps_size = current_thread_info()->ps_size;
prev_ctx->ti.ps_offset = current_thread_info()->ps_offset;
prev_ctx->ti.ps_top = current_thread_info()->ps_top;
prev_ctx->ti.pcs_base = current_thread_info()->pcs_base;
prev_ctx->ti.pcs_size = current_thread_info()->pcs_size;
prev_ctx->ti.pcs_offset = current_thread_info()->pcs_offset;
prev_ctx->ti.pcs_top = current_thread_info()->pcs_top;
prev_ctx->ti.pusd_pil = current_thread_info()->pusd_pil;
#ifdef CONFIG_PROTECTED_MODE
prev_ctx->ti.g_list = current_thread_info()->g_list;
prev_ctx->ti.user_stack_addr = current_thread_info()->user_stack_addr;
prev_ctx->ti.user_stack_size = current_thread_info()->user_stack_size;
prev_ctx->ti.multithread_address =
current_thread_info()->multithread_address;
prev_ctx->ti.lock = current_thread_info()->lock;
#endif
/*
* Save task_struct
*/
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
DebugFTRACE("ret_stack %p saved\n", current->ret_stack);
prev_ctx->task.tracing_graph_pause = current->tracing_graph_pause;
prev_ctx->task.trace_overrun = current->trace_overrun;
prev_ctx->task.ftrace_timestamp = current->ftrace_timestamp;
prev_ctx->task.ret_stack = current->ret_stack;
prev_ctx->task.curr_ret_stack = current->curr_ret_stack;
#endif
raw_all_irq_disable();
E2K_FLUSHCPU;
prev_ctx->k_sbr = READ_SBR_REG_VALUE();
prev_ctx->k_usd_hi = READ_USD_HI_REG();
prev_ctx->k_usd_lo = READ_USD_LO_REG();
AW(prev_ctx->cr1_lo) = E2K_GET_DSREG_NV(cr1.lo);
AW(prev_ctx->cr1_hi) = E2K_GET_DSREG_NV(cr1.hi);
AW(prev_ctx->cr0_lo) = E2K_GET_DSREG_NV(cr0.lo);
AW(prev_ctx->cr0_hi) = E2K_GET_DSREG_NV(cr0.hi);
#ifdef CONFIG_GREGS_CONTEXT
DO_SAVE_GLOBAL_REGISTERS(prev_ctx, false, true);
#endif /* CONFIG_GREGS_CONTEXT */
/* These will wait for the flush so we give
* the flush some time to finish. */
prev_ctx->psp_hi = RAW_READ_PSP_HI_REG();
prev_ctx->psp_lo = READ_PSP_LO_REG();
prev_ctx->pcsp_hi = RAW_READ_PCSP_HI_REG();
prev_ctx->pcsp_lo = READ_PCSP_LO_REG();
WRITE_SBR_REG_VALUE(next_ctx->k_sbr);
WRITE_USD_REG(next_ctx->k_usd_hi, next_ctx->k_usd_lo);
RAW_WRITE_PSP_REG(next_ctx->psp_hi, next_ctx->psp_lo);
RAW_WRITE_PCSP_REG(next_ctx->pcsp_hi, next_ctx->pcsp_lo);
E2K_SET_DSREG_NV_NOIRQ(cr1.lo, AW(next_ctx->cr1_lo));
E2K_SET_DSREG_NV_NOIRQ(cr1.hi, AW(next_ctx->cr1_hi));
E2K_SET_DSREG_NV_NOIRQ(cr0.lo, AW(next_ctx->cr0_lo));
E2K_SET_DSREG_NV_NOIRQ(cr0.hi, AW(next_ctx->cr0_hi));
E2K_SET_SREG_NV(fpcr, fpcr);
E2K_SET_SREG_NV(fpsr, fpsr);
E2K_SET_SREG_NV(pfpfr, pfpfr);
#ifdef CONFIG_GREGS_CONTEXT
DO_LOAD_GLOBAL_REGISTERS(next_ctx, false);
#endif /* CONFIG_GREGS_CONTEXT */
CLEAR_DAM;
/*
* Restore thread_info
*/
current_thread_info()->hw_context_current =
next_ctx->ti.hw_context_current;
current_thread_info()->pt_regs = next_ctx->ti.pt_regs;
current_thread_info()->k_usd_hi = next_ctx->ti.k_usd_hi;
current_thread_info()->k_usd_lo = next_ctx->ti.k_usd_lo;
current_thread_info()->k_stk_base = next_ctx->ti.k_stk_base;
current_thread_info()->k_stk_sz = next_ctx->ti.k_stk_sz;
current_thread_info()->upsr = next_ctx->ti.upsr;
current_thread_info()->u_stk_base = next_ctx->ti.u_stk_base;
current_thread_info()->u_stk_sz = next_ctx->ti.u_stk_sz;
current_thread_info()->u_stk_top = next_ctx->ti.u_stk_top;
/* We moved the lists so the pointers in them
* must be fixed to point to the new location. */
if (list_empty(&next_ctx->ti.old_u_pcs_list)) {
INIT_LIST_HEAD(&current_thread_info()->old_u_pcs_list);
} else {
current_thread_info()->old_u_pcs_list =
next_ctx->ti.old_u_pcs_list;
next_ctx->ti.old_u_pcs_list.next->prev =
&current_thread_info()->old_u_pcs_list;
next_ctx->ti.old_u_pcs_list.prev->next =
&current_thread_info()->old_u_pcs_list;
}
if (list_empty(&next_ctx->ti.ps_list)) {
INIT_LIST_HEAD(&current_thread_info()->ps_list);
} else {
current_thread_info()->ps_list = next_ctx->ti.ps_list;
next_ctx->ti.ps_list.next->prev =
&current_thread_info()->ps_list;
next_ctx->ti.ps_list.prev->next =
&current_thread_info()->ps_list;
}
if (list_empty(&next_ctx->ti.pcs_list)) {
INIT_LIST_HEAD(&current_thread_info()->pcs_list);
} else {
current_thread_info()->pcs_list = next_ctx->ti.pcs_list;
next_ctx->ti.pcs_list.next->prev =
&current_thread_info()->pcs_list;
next_ctx->ti.pcs_list.prev->next =
&current_thread_info()->pcs_list;
}
current_thread_info()->cur_ps = next_ctx->ti.cur_ps;
current_thread_info()->cur_pcs = next_ctx->ti.cur_pcs;
current_thread_info()->ps_base = next_ctx->ti.ps_base;
current_thread_info()->ps_size = next_ctx->ti.ps_size;
current_thread_info()->ps_offset = next_ctx->ti.ps_offset;
current_thread_info()->ps_top = next_ctx->ti.ps_top;
current_thread_info()->pcs_base = next_ctx->ti.pcs_base;
current_thread_info()->pcs_size = next_ctx->ti.pcs_size;
current_thread_info()->pcs_offset = next_ctx->ti.pcs_offset;
current_thread_info()->pcs_top = next_ctx->ti.pcs_top;
current_thread_info()->pusd_pil = next_ctx->ti.pusd_pil;
#ifdef CONFIG_PROTECTED_MODE
current_thread_info()->g_list = next_ctx->ti.g_list;
current_thread_info()->user_stack_addr = next_ctx->ti.user_stack_addr;
current_thread_info()->user_stack_size = next_ctx->ti.user_stack_size;
current_thread_info()->multithread_address =
next_ctx->ti.multithread_address;
current_thread_info()->lock = next_ctx->ti.lock;
#endif
/*
* Restore task_struct
*/
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
current->tracing_graph_pause = next_ctx->task.tracing_graph_pause;
current->trace_overrun = next_ctx->task.trace_overrun;
current->ftrace_timestamp = next_ctx->task.ftrace_timestamp;
current->ret_stack = next_ctx->task.ret_stack;
current->curr_ret_stack = next_ctx->task.curr_ret_stack;
DebugFTRACE("ret_stack %p restored\n", current->ret_stack);
#endif
#if DEBUG_CTX_MODE
DebugCTX("After switching (pt_regs 0x%lx):\n",
current_thread_info()->pt_regs);
print_stack_frames(current, 0, 0);
list_for_each_entry(u_ps, &current_thread_info()->ps_list, list_entry)
DebugCTX("register stack at 0x%lx\n", u_ps->base);
list_for_each_entry(u_pcs, &current_thread_info()->pcs_list, list_entry)
DebugCTX("chain stack at 0x%lx\n", u_pcs->base);
#endif
raw_all_irq_enable();
prev_ctx->in_use = false;
return HW_CONTEXT_TAIL;
}
#undef printk
/**
* hw_context_tail() - first thing a new hardware context must call
*/
void hw_context_tail()
{
struct hw_context *prev_ctx, *next_ctx;
prev_ctx = current_thread_info()->prev_ctx;
next_ctx = current_thread_info()->next_ctx;
DebugCTX("switching from ctx %lx to ctx %lx\n",
prev_ctx, next_ctx);
if (current_thread_info()->free_hw_context)
list_del(&prev_ctx->list_entry);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/*
* First switch always gets us here, so this check
* should not be done in do_swapcontext after the switch
* (since the check only applies to the first switch).
*/
if (unlikely(!current_thread_info()->main_context_saved)) {
current_thread_info()->main_context_saved = true;
DebugCTX("main context was saved (%d)\n",
current_thread_info()->main_context_saved);
if (!prev_ctx->task.ret_stack) {
struct ftrace_ret_stack *ret_stack;
ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH
* sizeof(struct ftrace_ret_stack),
GFP_ATOMIC);
if (ret_stack) {
atomic_set(&prev_ctx->task.tracing_graph_pause,
0);
atomic_set(&prev_ctx->task.trace_overrun, 0);
prev_ctx->task.ftrace_timestamp = 0;
}
prev_ctx->task.ret_stack = ret_stack;
prev_ctx->task.curr_ret_stack = -1;
DebugFTRACE("ret_stack %p allocated for main ctx\n",
ret_stack);
}
}
#endif
spin_unlock(&current->mm->context.hw_context_lock);
if (current_thread_info()->free_hw_context) {
current_thread_info()->free_hw_context = false;
free_hw_context(prev_ctx, false, false);
}
}
static int save_ctx_32_bit(struct ucontext_32 __user *oucp)
{
struct pt_regs *regs;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
int ret = 0;
regs = current_thread_info()->pt_regs;
E2K_GET_FPU(AW(fpcr), AW(fpsr), AW(pfpfr));
if (!access_ok(ACCESS_WRITE, oucp, sizeof(*oucp)))
return -EFAULT;
BEGIN_USR_PFAULT("lbl_save_ctx_32_bit", "2f");
*((u64 *) &oucp->uc_sigmask) = current->blocked.sig[0];
oucp->uc_mcontext.sbr = context_ti_key(current_thread_info());
oucp->uc_mcontext.cr0_hi = AW(regs->crs.cr0_hi);
oucp->uc_mcontext.cr1_lo = AW(regs->crs.cr1_lo);
oucp->uc_mcontext.cr1_hi = AW(regs->crs.cr1_hi);
oucp->uc_mcontext.pcsp_lo = AW(regs->stacks.pcsp_lo);
oucp->uc_mcontext.pcsp_hi = AW(regs->stacks.pcsp_hi);
oucp->uc_extra.fpcr = AW(fpcr);
oucp->uc_extra.fpsr = AW(fpsr);
oucp->uc_extra.pfpfr = AW(pfpfr);
LBL_USR_PFAULT("lbl_save_ctx_32_bit", "2:");
if (END_USR_PFAULT)
ret = -EFAULT;
return ret;
}
static int save_ctx_64_bit(struct ucontext __user *oucp)
{
struct pt_regs *regs;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
int ret = 0;
regs = current_thread_info()->pt_regs;
E2K_GET_FPU(AW(fpcr), AW(fpsr), AW(pfpfr));
if (!access_ok(ACCESS_WRITE, oucp, sizeof(*oucp)))
return -EFAULT;
BEGIN_USR_PFAULT("lbl_save_ctx_64_bit", "3f");
*((u64 *) &oucp->uc_sigmask) = current->blocked.sig[0];
oucp->uc_mcontext.sbr = context_ti_key(current_thread_info());
oucp->uc_mcontext.cr0_hi = AW(regs->crs.cr0_hi);
oucp->uc_mcontext.cr1_lo = AW(regs->crs.cr1_lo);
oucp->uc_mcontext.cr1_hi = AW(regs->crs.cr1_hi);
oucp->uc_mcontext.pcsp_lo = AW(regs->stacks.pcsp_lo);
oucp->uc_mcontext.pcsp_hi = AW(regs->stacks.pcsp_hi);
oucp->uc_extra.fpcr = AW(fpcr);
oucp->uc_extra.fpsr = AW(fpsr);
oucp->uc_extra.pfpfr = AW(pfpfr);
LBL_USR_PFAULT("lbl_save_ctx_64_bit", "3:");
if (END_USR_PFAULT)
ret = -EFAULT;
return ret;
}
static int save_ctx_128_bit(struct ucontext_prot __user *oucp)
{
struct pt_regs *regs;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
int ret = 0;
regs = current_thread_info()->pt_regs;
E2K_GET_FPU(AW(fpcr), AW(fpsr), AW(pfpfr));
if (!access_ok(ACCESS_WRITE, oucp, sizeof(*oucp)))
return -EFAULT;
BEGIN_USR_PFAULT("lbl_save_ctx_128_bit", "4f");
*((u64 *) &oucp->uc_sigmask) = current->blocked.sig[0];
oucp->uc_mcontext.sbr = context_ti_key(current_thread_info());
oucp->uc_mcontext.cr0_hi = AW(regs->crs.cr0_hi);
oucp->uc_mcontext.cr1_lo = AW(regs->crs.cr1_lo);
oucp->uc_mcontext.cr1_hi = AW(regs->crs.cr1_hi);
oucp->uc_mcontext.pcsp_lo = AW(regs->stacks.pcsp_lo);
oucp->uc_mcontext.pcsp_hi = AW(regs->stacks.pcsp_hi);
oucp->uc_extra.fpcr = AW(fpcr);
oucp->uc_extra.fpsr = AW(fpsr);
oucp->uc_extra.pfpfr = AW(pfpfr);
LBL_USR_PFAULT("lbl_save_ctx_128_bit", "4:");
if (END_USR_PFAULT)
ret = -EFAULT;
return ret;
}
#if _NSIG != 64
# error We read u64 value here...
#endif
inline int do_swapcontext(void __user *oucp, const void __user *ucp,
bool save_prev_ctx, int format)
{
u64 next_key, prev_key, sigset;
int next_index, prev_index;
struct list_head *hb;
struct hw_context *next_ctx, *prev_ctx;
mm_context_t *mm_context = &current->mm->context;
e2k_pcsp_lo_t pcsp_lo;
e2k_pcsp_hi_t pcsp_hi;
e2k_cr0_hi_t cr0_hi;
e2k_fpcr_t fpcr;
e2k_fpsr_t fpsr;
e2k_pfpfr_t pfpfr;
struct pt_regs *regs;
int ret;
DebugCTX("oucp=%lx ucp=%lx started\n", oucp, ucp);
BUILD_BUG_ON(sizeof(current->blocked.sig[0]) != 8);
if (save_prev_ctx) {
if (format == CTX_32_BIT) {
ret = save_ctx_32_bit(
(struct ucontext_32 __user *) oucp);
} else if (format == CTX_64_BIT) {
ret = save_ctx_64_bit((struct ucontext __user *) oucp);
} else {
/* CTX_128_BIT */
ret = save_ctx_128_bit(
(struct ucontext_prot __user *) oucp);
}
if (ret)
return -EFAULT;
}
if (format == CTX_32_BIT) {
const struct ucontext_32 __user *_ucp = ucp;
if (!access_ok(ACCESS_READ, _ucp, sizeof(*_ucp)))
return -EFAULT;
ret = __get_user(next_key, &_ucp->uc_mcontext.sbr);
ret |= __get_user(AW(fpcr), &_ucp->uc_extra.fpcr);
ret |= __get_user(AW(fpsr), &_ucp->uc_extra.fpsr);
ret |= __get_user(AW(pfpfr), &_ucp->uc_extra.pfpfr);
} else if (format == CTX_64_BIT) {
const struct ucontext __user *_ucp = ucp;
if (!access_ok(ACCESS_READ, _ucp, sizeof(*_ucp)))
return -EFAULT;
ret = __get_user(next_key, &_ucp->uc_mcontext.sbr);
ret |= __get_user(AW(fpcr), &_ucp->uc_extra.fpcr);
ret |= __get_user(AW(fpsr), &_ucp->uc_extra.fpsr);
ret |= __get_user(AW(pfpfr), &_ucp->uc_extra.pfpfr);
} else {
/* CTX_128_BIT */
const struct ucontext_prot __user *_ucp = ucp;
if (!access_ok(ACCESS_READ, _ucp, sizeof(*_ucp)))
return -EFAULT;
ret = __get_user(next_key, &_ucp->uc_mcontext.sbr);
ret |= __get_user(AW(fpcr), &_ucp->uc_extra.fpcr);
ret |= __get_user(AW(fpsr), &_ucp->uc_extra.fpsr);
ret |= __get_user(AW(pfpfr), &_ucp->uc_extra.pfpfr);
}
if (ret)
return -EFAULT;
prev_key = context_ti_key(current_thread_info());
DebugCTX("prev_key %lx, next_key %lx\n",
prev_key, next_key);
next_index = key2index(next_key);
prev_index = key2index(prev_key);
/*
* If this is the first time this thread is changing contexts
* we'll have to allocate memory for the main context.
*/
if (likely(current_thread_info()->main_context_saved)) {
prev_ctx = NULL;
} else {
DebugCTX("will save main context (%d)\n",
current_thread_info()->main_context_saved);
prev_ctx = alloc_hw_context(true,
current_thread_info()->u_stk_sz);
if (!prev_ctx)
return -ENOMEM;
if (save_prev_ctx) {
if (format == CTX_32_BIT) {
ret = put_user(prev_key,
&((struct ucontext_32 __user *)
oucp)->uc_mcontext.sbr);
} else if (format == CTX_64_BIT) {
ret = put_user(prev_key,
&((struct ucontext __user *)
oucp)->uc_mcontext.sbr);
} else {
/* CTX_128_BIT */
ret = put_user(prev_key,
&((struct ucontext_prot __user *)
oucp)->uc_mcontext.sbr);
}
if (ret) {
free_hw_context(prev_ctx, true, false);
return -EFAULT;
}
}
if (format == CTX_32_BIT) {
prev_ctx->p_uc_link =
&((struct ucontext_32 __user *)
oucp)->uc_link;
prev_ctx->ptr_format = CTX_32_BIT;
} else if (format == CTX_64_BIT) {
prev_ctx->p_uc_link = &((struct ucontext __user *)
oucp)->uc_link;
prev_ctx->ptr_format = CTX_64_BIT;
} else {
/* CTX_128_BIT */
prev_ctx->p_uc_link =
&((struct ucontext_prot __user *)
oucp)->uc_link;
prev_ctx->ptr_format = CTX_128_BIT;
}
}
/*
* Do the switch
*/
spin_lock(&mm_context->hw_context_lock);
hb = &mm_context->hw_contexts[prev_index];
/*
* Find the next context
*/
next_ctx = key2hw_context(next_key, next_index, mm_context,
false);
if (!next_ctx) {
spin_unlock(&mm_context->hw_context_lock);
if (!current_thread_info()->main_context_saved)
free_hw_context(prev_ctx, true, false);
return -ESRCH;
}
if (likely(current_thread_info()->main_context_saved)) {
prev_ctx = key2hw_context(prev_key, prev_index,
mm_context, true);
DebugCTX("ctx %lx found\n", prev_ctx);
BUG_ON(!prev_ctx);
} else {
list_add(&prev_ctx->list_entry, hb);
#ifndef CONFIG_FUNCTION_GRAPH_TRACER
current_thread_info()->main_context_saved = true;
#endif
}
DebugCTX("switching from ctx %lx to ctx %lx\n",
prev_ctx, next_ctx);
current_thread_info()->prev_ctx = prev_ctx;
current_thread_info()->next_ctx = next_ctx;
current_thread_info()->ucp = ucp;
(void) switch_hw_contexts(prev_ctx, next_ctx,
AW(fpcr), AW(fpsr), AW(pfpfr));
prev_ctx = current_thread_info()->prev_ctx;
next_ctx = current_thread_info()->next_ctx;
ucp = current_thread_info()->ucp;
if (current_thread_info()->free_hw_context)
list_del(&prev_ctx->list_entry);
spin_unlock(&mm_context->hw_context_lock);
if (current_thread_info()->free_hw_context) {
current_thread_info()->free_hw_context = false;
free_hw_context(prev_ctx, false, false);
}
regs = current_thread_info()->pt_regs;
/*
* Read sigmask and stack parameters
*/
if (format == CTX_32_BIT) {
const struct ucontext_32 __user *_ucp = ucp;
ret = __get_user(sigset, (u64 *) &_ucp->uc_sigmask);
ret |= __get_user(AW(pcsp_lo),
(u64 *) &_ucp->uc_mcontext.pcsp_lo);
ret |= __get_user(AW(pcsp_hi),
(u64 *) &_ucp->uc_mcontext.pcsp_hi);
ret |= __get_user(AW(cr0_hi), &_ucp->uc_mcontext.cr0_hi);
} else if (format == CTX_64_BIT) {
const struct ucontext __user *_ucp = ucp;
ret = __get_user(sigset, (u64 *) &_ucp->uc_sigmask);
ret |= __get_user(AW(pcsp_lo),
(u64 *) &_ucp->uc_mcontext.pcsp_lo);
ret |= __get_user(AW(pcsp_hi),
(u64 *) &_ucp->uc_mcontext.pcsp_hi);
ret |= __get_user(AW(cr0_hi), &_ucp->uc_mcontext.cr0_hi);
} else {
/* CTX_128_BIT */
const struct ucontext_prot __user *_ucp = ucp;
ret = __get_user(sigset, (u64 *) &_ucp->uc_sigmask);
ret |= __get_user(AW(pcsp_lo),
(u64 *) &_ucp->uc_mcontext.pcsp_lo);
ret |= __get_user(AW(pcsp_hi),
(u64 *) &_ucp->uc_mcontext.pcsp_hi);
ret |= __get_user(AW(cr0_hi), &_ucp->uc_mcontext.cr0_hi);
}
if (ret)
return -EFAULT;
/*
* Do we need to jump backwards in the new context?
*/
if (AS(regs->stacks.pcsp_lo).base + AS(regs->stacks.pcsp_hi).size
!= AS(pcsp_lo).base + AS(pcsp_hi).size
|| AW(cr0_hi) != AW(regs->crs.cr0_hi)) {
e2k_cr1_lo_t cr1_lo;
e2k_cr1_hi_t cr1_hi;
if (format == CTX_32_BIT) {
const struct ucontext_32 __user *_ucp = ucp;
ret = __get_user(AW(cr1_lo),
&_ucp->uc_mcontext.cr1_lo);
ret |= __get_user(AW(cr1_hi),
&_ucp->uc_mcontext.cr1_hi);
} else if (format == CTX_64_BIT) {
const struct ucontext __user *_ucp = ucp;
ret = __get_user(AW(cr1_lo),
&_ucp->uc_mcontext.cr1_lo);
ret |= __get_user(AW(cr1_hi),
&_ucp->uc_mcontext.cr1_hi);
} else {
/* CTX_128_BIT */
const struct ucontext_prot __user *_ucp = ucp;
ret = __get_user(AW(cr1_lo),
&_ucp->uc_mcontext.cr1_lo);
ret |= __get_user(AW(cr1_hi),
&_ucp->uc_mcontext.cr1_hi);
}
if (ret)
return -EFAULT;
/* A hack to make do_longjmp() restore
* blocked signals mask */
sigset |= sigmask(SIGKILL);
DebugCTX("calling longjmp\n");
do_longjmp(0, sigset, cr0_hi, cr1_lo, pcsp_lo, pcsp_hi,
AS(cr1_hi).br,
0, 0, 0, 0);
return HW_CONTEXT_NEW_STACKS;
}
current->blocked.sig[0] = sigset & _BLOCKABLE;
return 0;
}
int sys_swapcontext(struct ucontext __user *oucp,
const struct ucontext __user *ucp,
int sigsetsize)
{
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
return do_swapcontext(oucp, ucp, true, CTX_64_BIT);
}
#ifdef CONFIG_COMPAT
int compat_sys_swapcontext(struct ucontext_32 __user *oucp,
const struct ucontext_32 __user *ucp, int sigsetsize)
{
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
return do_swapcontext(oucp, ucp, true, CTX_32_BIT);
}
#endif
#ifdef CONFIG_PROTECTED_MODE
int protected_sys_swapcontext(struct ucontext_prot __user *oucp,
const struct ucontext_prot __user *ucp, int sigsetsize)
{
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
return do_swapcontext(oucp, ucp, true, CTX_128_BIT);
}
#endif
void machine_restart(char * __unused)
{
DebugP("machine_restart entered.\n");
if (machine.restart != NULL)
machine.restart(__unused);
DebugP("machine_restart exited.\n");
}
void machine_halt(void)
{
DebugP("machine_halt entered.\n");
if (machine.halt != NULL)
machine.halt();
DebugP("machine_halt exited.\n");
}
void machine_power_off(void)
{
DebugP("machine_power_off entered.\n");
if (machine.power_off != NULL)
machine.power_off();
DebugP("machine_power_off exited.\n");
}
/*
* We use this if we don't have any better
* idle routine..
*/
void default_idle(void)
{
if (psr_and_upsr_irqs_disabled())
local_irq_enable();
/* loop is done by the caller */
cpu_relax();
}
EXPORT_SYMBOL(default_idle);
#ifdef CONFIG_HOTPLUG_CPU
/* We take CLK off on CPU on ES2 due we have hw support for it,
* on other e2k machines we don't actually take CPU down,
* just spin without interrupts. */
void arch_cpu_idle_dead(void)
{
unsigned int this_cpu = raw_smp_processor_id();
raw_local_irq_disable();
/* Ack it for __cpu_die() */
__this_cpu_write(cpu_state, CPU_DEAD);
/* idle_task_exit(); - we do not leave idle task,
* and do not create new one, as we use common hw stack.
*/
/* Busy loop inside, waiting for bit in callin_go mask.
* For ES2 there is opportunity to switch off the CLK also.
*/
e2k_up_secondary(this_cpu);
/*
* We are here after __cpu_up. Now we are going back to cpu_idle.
*/
raw_local_irq_enable();
}
#endif /* CONFIG_HOTPLUG_CPU */
void arch_cpu_idle_enter()
{
/* It works under CONFIG_PROFILING flag only */
cpu_idle_time();
}
void arch_cpu_idle_exit()
{
/* It works under CONFIG_PROFILING flag only */
calculate_cpu_idle_time();
}
void arch_cpu_idle()
{
if (cpuidle_idle_call())
default_idle();
}
void flush_thread(void)
{
DebugP("flush_thread entered.\n");
DebugP("flush_thread exited.\n");
}
int dump_fpu( struct pt_regs *regs, void *fpu )
{
DebugP("dump_fpu entered.\n");
DebugP("dump_fpu exited.\n");
return 0;
}
unsigned long
thread_saved_pc(struct task_struct *task)
{
// ????
return AS_STRUCT(task->thread.sw_regs.cr0_hi).ip << 3;
}