mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-14 06:35:12 +00:00
ddb815bd54
This is part of a larger series that aims at getting rid of the copy_thread()/copy_thread_tls() split that makes the process creation codepaths in the kernel more convoluted and error-prone than they need to be. I'm converting all the remaining arches that haven't yet switched and am collecting individual acks. Once I have them, I'll send the whole series removing the copy_thread()/copy_thread_tls() split, the HAVE_COPY_THREAD_TLS define and the legacy do_fork() helper. The only kernel-wide process creation entry point for anything not going directly through the syscall path will then be based on struct kernel_clone_args. No more danger of weird process creation abi quirks between architectures hopefully, and easier to maintain overall. It also unblocks implementing clone3() on architectures not support copy_thread_tls(). Any architecture that wants to implement clone3() will need to select HAVE_COPY_THREAD_TLS and thus need to implement copy_thread_tls(). So both goals are connected but independently beneficial. HAVE_COPY_THREAD_TLS means that a given architecture supports CLONE_SETTLS and not setting it should usually mean that the architectures doesn't implement it but that's not how things are. In fact all architectures support CLONE_TLS it's just that they don't follow the calling convention that HAVE_COPY_THREAD_TLS implies. That means all architectures can be switched over to select HAVE_COPY_THREAD_TLS. Once that is done we can remove that macro (yay, less code), the unnecessary do_fork() export in kernel/fork.c, and also rename copy_thread_tls() back to copy_thread(). At this point copy_thread() becomes the main architecture specific part of process creation but it will be the same layout and calling convention for all architectures. (Once that is done we can probably cleanup each copy_thread() function even more but that's for the future.) Since ia64 does support CLONE_SETTLS there's no reason to not select HAVE_COPY_THREAD_TLS. This brings us one step closer to getting rid of the copy_thread()/copy_thread_tls() split we still have and ultimately the HAVE_COPY_THREAD_TLS define in general. A lot of architectures have already converted and ia64 is one of the few hat haven't yet. This also unblocks implementing the clone3() syscall on ia64. Once that is done we can get of another ARCH_WANTS_* macro. Once Any architecture that supports HAVE_COPY_THREAD_TLS cannot call the do_fork() helper anymore. This is fine and intended since it should be removed in favor of the new, cleaner _do_fork() calling convention based on struct kernel_clone_args. In fact, most architectures have already switched. With this patch, ia64 joins the other arches which can't use the fork(), vfork(), clone(), clone3() syscalls directly and who follow the new process creation calling convention that is based on struct kernel_clone_args which we introduced a while back. This means less custom assembly in the architectures entry path to set up the registers before calling into the process creation helper and it is easier to to support new features without having to adapt calling conventions. It also unifies all process creation paths between fork(), vfork(), clone(), and clone3(). (We can't fix the ABI nightmare that legacy clone() is but we can prevent stuff like this happening in the future.) Well, the first version I nothing to test this with. I don't know how to reasonably explain what happened but thanks to Adrian I'm now sitting at home next to a HP Integrity RX2600. I've done some testing and my initial version had a bug that became obvious when I took a closer look. The switch stack logic assumes that ar.pfs is stored in r16 and I changed that to r2. So with that fixed the following test program runs without any problems: #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include <errno.h> #include <fcntl.h> #include <linux/sched.h> #include <sched.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <syscall.h> #include <unistd.h> #define IA64_SYSCALL_OFFSET 1024 #ifndef __NR_clone #define __NR_clone (104 + IA64_SYSCALL_OFFSET) #endif #ifndef __NR_clone2 #define __NR_clone2 (189 + IA64_SYSCALL_OFFSET) #endif /* * sys_clone(unsigned long flags, * unsigned long stack, * int *parent_tidptr, * int *child_tidptr, * unsigned long tls) */ static pid_t ia64_raw_clone(void) { return syscall(__NR_clone, SIGCHLD, 0, NULL, NULL, 0); } /* * sys_clone2(unsigned long flags, * unsigned long stack, * unsigned long stack_size, * int *parent_tidptr, * int *child_tidptr, * unsigned long tls) */ static pid_t ia64_raw_clone2(void) { return syscall(__NR_clone2, SIGCHLD, 0, 0, NULL, NULL, 0); } /* * Let's use the "standard stack limit" (i.e. glibc thread size default) for * stack sizes: 8MB. */ #define __STACK_SIZE (8 * 1024 * 1024) /* This is not always defined in sched.h. */ extern int __clone2 (int (*__fn) (void *__arg), void *__child_stack_base, size_t __child_stack_size, int __flags, void *__arg, ...); pid_t libc_clone2(int (*fn)(void *), void *arg) { pid_t ret; void *stack; stack = malloc(__STACK_SIZE); if (!stack) return -ENOMEM; return __clone2(fn, stack, __STACK_SIZE, SIGCHLD, arg, NULL, NULL, NULL); } static int libc_clone2_child(void *data) { fprintf(stderr, "I'll just see myself out\n"); _exit(EXIT_SUCCESS); } int main(void) { for (int i = 0; i < 1000; i++) { pid_t pid = ia64_raw_clone(); if (pid < 0) _exit(EXIT_FAILURE); if (pid == 0) _exit(EXIT_SUCCESS); if (wait(NULL) != pid) _exit(EXIT_FAILURE); fprintf(stderr, "ia64_raw_clone() passed\n"); pid = ia64_raw_clone2(); if (pid < 0) _exit(EXIT_FAILURE); if (pid == 0) _exit(EXIT_SUCCESS); if (wait(NULL) != pid) _exit(EXIT_FAILURE); fprintf(stderr, "ia64_raw_clone2() passed\n"); pid = libc_clone2(libc_clone2_child, NULL); if (pid < 0) _exit(EXIT_FAILURE); if (wait(NULL) != pid) _exit(EXIT_FAILURE); fprintf(stderr, "libc_clone2() passed\n"); } _exit(EXIT_SUCCESS); } For some more context, please see: commit606e9ad200
Merge:ac61145a72
457677c70c
Author: Linus Torvalds <torvalds@linux-foundation.org> Date: Sat Jan 11 15:33:48 2020 -0800 Merge tag 'clone3-tls-v5.5-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux Pull thread fixes from Christian Brauner: "This contains a series of patches to fix CLONE_SETTLS when used with clone3(). The clone3() syscall passes the tls argument through struct clone_args instead of a register. This means, all architectures that do not implement copy_thread_tls() but still support CLONE_SETTLS via copy_thread() expecting the tls to be located in a register argument based on clone() are currently unfortunately broken. Their tls value will be garbage. The patch series fixes this on all architectures that currently define __ARCH_WANT_SYS_CLONE3. It also adds a compile-time check to ensure that any architecture that enables clone3() in the future is forced to also implement copy_thread_tls(). My ultimate goal is to get rid of the copy_thread()/copy_thread_tls() split and just have copy_thread_tls() at some point in the not too distant future (Maybe even renaming copy_thread_tls() back to simply copy_thread() once the old function is ripped from all arches). This is dependent now on all arches supporting clone3(). While all relevant arches do that now there are still four missing: ia64, m68k, sh and sparc. They have the system call reserved, but not implemented. Once they all implement clone3() we can get rid of ARCH_WANT_SYS_CLONE3 and HAVE_COPY_THREAD_TLS. Note that in the meantime, m68k has already switched to the new calling convention. And I've got sparc patches acked by Dave, too. Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> Cc: Tony Luck <tony.luck@intel.com> Cc: Fenghua Yu <fenghua.yu@intel.com> Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@kernel.org> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: "Peter Zijlstra (Intel)" <peterz@infradead.org> Cc: Qais Yousef <qais.yousef@arm.com> Cc: linux-ia64@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20200517151635.3085756-1-christian.brauner@ubuntu.com
697 lines
19 KiB
C
697 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Architecture-specific setup.
|
|
*
|
|
* Copyright (C) 1998-2003 Hewlett-Packard Co
|
|
* David Mosberger-Tang <davidm@hpl.hp.com>
|
|
* 04/11/17 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
|
|
*
|
|
* 2005-10-07 Keith Owens <kaos@sgi.com>
|
|
* Add notify_die() hooks.
|
|
*/
|
|
#include <linux/cpu.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/debug.h>
|
|
#include <linux/sched/hotplug.h>
|
|
#include <linux/sched/task.h>
|
|
#include <linux/sched/task_stack.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/thread_info.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/efi.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kdebug.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/tracehook.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
#include <asm/cpu.h>
|
|
#include <asm/delay.h>
|
|
#include <asm/elf.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/kexec.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/sal.h>
|
|
#include <asm/switch_to.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/unwind.h>
|
|
#include <asm/user.h>
|
|
|
|
#include "entry.h"
|
|
|
|
#ifdef CONFIG_PERFMON
|
|
# include <asm/perfmon.h>
|
|
#endif
|
|
|
|
#include "sigframe.h"
|
|
|
|
void (*ia64_mark_idle)(int);
|
|
|
|
unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
|
|
EXPORT_SYMBOL(boot_option_idle_override);
|
|
void (*pm_power_off) (void);
|
|
EXPORT_SYMBOL(pm_power_off);
|
|
|
|
static void
|
|
ia64_do_show_stack (struct unw_frame_info *info, void *arg)
|
|
{
|
|
unsigned long ip, sp, bsp;
|
|
const char *loglvl = arg;
|
|
|
|
printk("%s\nCall Trace:\n", loglvl);
|
|
do {
|
|
unw_get_ip(info, &ip);
|
|
if (ip == 0)
|
|
break;
|
|
|
|
unw_get_sp(info, &sp);
|
|
unw_get_bsp(info, &bsp);
|
|
printk("%s [<%016lx>] %pS\n"
|
|
" sp=%016lx bsp=%016lx\n",
|
|
loglvl, ip, (void *)ip, sp, bsp);
|
|
} while (unw_unwind(info) >= 0);
|
|
}
|
|
|
|
void
|
|
show_stack (struct task_struct *task, unsigned long *sp, const char *loglvl)
|
|
{
|
|
if (!task)
|
|
unw_init_running(ia64_do_show_stack, (void *)loglvl);
|
|
else {
|
|
struct unw_frame_info info;
|
|
|
|
unw_init_from_blocked_task(&info, task);
|
|
ia64_do_show_stack(&info, (void *)loglvl);
|
|
}
|
|
}
|
|
|
|
void
|
|
show_regs (struct pt_regs *regs)
|
|
{
|
|
unsigned long ip = regs->cr_iip + ia64_psr(regs)->ri;
|
|
|
|
print_modules();
|
|
printk("\n");
|
|
show_regs_print_info(KERN_DEFAULT);
|
|
printk("psr : %016lx ifs : %016lx ip : [<%016lx>] %s (%s)\n",
|
|
regs->cr_ipsr, regs->cr_ifs, ip, print_tainted(),
|
|
init_utsname()->release);
|
|
printk("ip is at %pS\n", (void *)ip);
|
|
printk("unat: %016lx pfs : %016lx rsc : %016lx\n",
|
|
regs->ar_unat, regs->ar_pfs, regs->ar_rsc);
|
|
printk("rnat: %016lx bsps: %016lx pr : %016lx\n",
|
|
regs->ar_rnat, regs->ar_bspstore, regs->pr);
|
|
printk("ldrs: %016lx ccv : %016lx fpsr: %016lx\n",
|
|
regs->loadrs, regs->ar_ccv, regs->ar_fpsr);
|
|
printk("csd : %016lx ssd : %016lx\n", regs->ar_csd, regs->ar_ssd);
|
|
printk("b0 : %016lx b6 : %016lx b7 : %016lx\n", regs->b0, regs->b6, regs->b7);
|
|
printk("f6 : %05lx%016lx f7 : %05lx%016lx\n",
|
|
regs->f6.u.bits[1], regs->f6.u.bits[0],
|
|
regs->f7.u.bits[1], regs->f7.u.bits[0]);
|
|
printk("f8 : %05lx%016lx f9 : %05lx%016lx\n",
|
|
regs->f8.u.bits[1], regs->f8.u.bits[0],
|
|
regs->f9.u.bits[1], regs->f9.u.bits[0]);
|
|
printk("f10 : %05lx%016lx f11 : %05lx%016lx\n",
|
|
regs->f10.u.bits[1], regs->f10.u.bits[0],
|
|
regs->f11.u.bits[1], regs->f11.u.bits[0]);
|
|
|
|
printk("r1 : %016lx r2 : %016lx r3 : %016lx\n", regs->r1, regs->r2, regs->r3);
|
|
printk("r8 : %016lx r9 : %016lx r10 : %016lx\n", regs->r8, regs->r9, regs->r10);
|
|
printk("r11 : %016lx r12 : %016lx r13 : %016lx\n", regs->r11, regs->r12, regs->r13);
|
|
printk("r14 : %016lx r15 : %016lx r16 : %016lx\n", regs->r14, regs->r15, regs->r16);
|
|
printk("r17 : %016lx r18 : %016lx r19 : %016lx\n", regs->r17, regs->r18, regs->r19);
|
|
printk("r20 : %016lx r21 : %016lx r22 : %016lx\n", regs->r20, regs->r21, regs->r22);
|
|
printk("r23 : %016lx r24 : %016lx r25 : %016lx\n", regs->r23, regs->r24, regs->r25);
|
|
printk("r26 : %016lx r27 : %016lx r28 : %016lx\n", regs->r26, regs->r27, regs->r28);
|
|
printk("r29 : %016lx r30 : %016lx r31 : %016lx\n", regs->r29, regs->r30, regs->r31);
|
|
|
|
if (user_mode(regs)) {
|
|
/* print the stacked registers */
|
|
unsigned long val, *bsp, ndirty;
|
|
int i, sof, is_nat = 0;
|
|
|
|
sof = regs->cr_ifs & 0x7f; /* size of frame */
|
|
ndirty = (regs->loadrs >> 19);
|
|
bsp = ia64_rse_skip_regs((unsigned long *) regs->ar_bspstore, ndirty);
|
|
for (i = 0; i < sof; ++i) {
|
|
get_user(val, (unsigned long __user *) ia64_rse_skip_regs(bsp, i));
|
|
printk("r%-3u:%c%016lx%s", 32 + i, is_nat ? '*' : ' ', val,
|
|
((i == sof - 1) || (i % 3) == 2) ? "\n" : " ");
|
|
}
|
|
} else
|
|
show_stack(NULL, NULL, KERN_DEFAULT);
|
|
}
|
|
|
|
/* local support for deprecated console_print */
|
|
void
|
|
console_print(const char *s)
|
|
{
|
|
printk(KERN_EMERG "%s", s);
|
|
}
|
|
|
|
void
|
|
do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
|
|
{
|
|
if (fsys_mode(current, &scr->pt)) {
|
|
/*
|
|
* defer signal-handling etc. until we return to
|
|
* privilege-level 0.
|
|
*/
|
|
if (!ia64_psr(&scr->pt)->lp)
|
|
ia64_psr(&scr->pt)->lp = 1;
|
|
return;
|
|
}
|
|
|
|
#ifdef CONFIG_PERFMON
|
|
if (current->thread.pfm_needs_checking)
|
|
/*
|
|
* Note: pfm_handle_work() allow us to call it with interrupts
|
|
* disabled, and may enable interrupts within the function.
|
|
*/
|
|
pfm_handle_work();
|
|
#endif
|
|
|
|
/* deal with pending signal delivery */
|
|
if (test_thread_flag(TIF_SIGPENDING)) {
|
|
local_irq_enable(); /* force interrupt enable */
|
|
ia64_do_signal(scr, in_syscall);
|
|
}
|
|
|
|
if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
|
|
local_irq_enable(); /* force interrupt enable */
|
|
tracehook_notify_resume(&scr->pt);
|
|
}
|
|
|
|
/* copy user rbs to kernel rbs */
|
|
if (unlikely(test_thread_flag(TIF_RESTORE_RSE))) {
|
|
local_irq_enable(); /* force interrupt enable */
|
|
ia64_sync_krbs();
|
|
}
|
|
|
|
local_irq_disable(); /* force interrupt disable */
|
|
}
|
|
|
|
static int __init nohalt_setup(char * str)
|
|
{
|
|
cpu_idle_poll_ctrl(true);
|
|
return 1;
|
|
}
|
|
__setup("nohalt", nohalt_setup);
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
/* We don't actually take CPU down, just spin without interrupts. */
|
|
static inline void play_dead(void)
|
|
{
|
|
unsigned int this_cpu = smp_processor_id();
|
|
|
|
/* Ack it */
|
|
__this_cpu_write(cpu_state, CPU_DEAD);
|
|
|
|
max_xtp();
|
|
local_irq_disable();
|
|
idle_task_exit();
|
|
ia64_jump_to_sal(&sal_boot_rendez_state[this_cpu]);
|
|
/*
|
|
* The above is a point of no-return, the processor is
|
|
* expected to be in SAL loop now.
|
|
*/
|
|
BUG();
|
|
}
|
|
#else
|
|
static inline void play_dead(void)
|
|
{
|
|
BUG();
|
|
}
|
|
#endif /* CONFIG_HOTPLUG_CPU */
|
|
|
|
void arch_cpu_idle_dead(void)
|
|
{
|
|
play_dead();
|
|
}
|
|
|
|
void arch_cpu_idle(void)
|
|
{
|
|
void (*mark_idle)(int) = ia64_mark_idle;
|
|
|
|
#ifdef CONFIG_SMP
|
|
min_xtp();
|
|
#endif
|
|
rmb();
|
|
if (mark_idle)
|
|
(*mark_idle)(1);
|
|
|
|
safe_halt();
|
|
|
|
if (mark_idle)
|
|
(*mark_idle)(0);
|
|
#ifdef CONFIG_SMP
|
|
normal_xtp();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ia64_save_extra (struct task_struct *task)
|
|
{
|
|
#ifdef CONFIG_PERFMON
|
|
unsigned long info;
|
|
#endif
|
|
|
|
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
|
|
ia64_save_debug_regs(&task->thread.dbr[0]);
|
|
|
|
#ifdef CONFIG_PERFMON
|
|
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
|
|
pfm_save_regs(task);
|
|
|
|
info = __this_cpu_read(pfm_syst_info);
|
|
if (info & PFM_CPUINFO_SYST_WIDE)
|
|
pfm_syst_wide_update_task(task, info, 0);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
ia64_load_extra (struct task_struct *task)
|
|
{
|
|
#ifdef CONFIG_PERFMON
|
|
unsigned long info;
|
|
#endif
|
|
|
|
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
|
|
ia64_load_debug_regs(&task->thread.dbr[0]);
|
|
|
|
#ifdef CONFIG_PERFMON
|
|
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
|
|
pfm_load_regs(task);
|
|
|
|
info = __this_cpu_read(pfm_syst_info);
|
|
if (info & PFM_CPUINFO_SYST_WIDE)
|
|
pfm_syst_wide_update_task(task, info, 1);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Copy the state of an ia-64 thread.
|
|
*
|
|
* We get here through the following call chain:
|
|
*
|
|
* from user-level: from kernel:
|
|
*
|
|
* <clone syscall> <some kernel call frames>
|
|
* sys_clone :
|
|
* _do_fork _do_fork
|
|
* copy_thread_tls copy_thread_tls
|
|
*
|
|
* This means that the stack layout is as follows:
|
|
*
|
|
* +---------------------+ (highest addr)
|
|
* | struct pt_regs |
|
|
* +---------------------+
|
|
* | struct switch_stack |
|
|
* +---------------------+
|
|
* | |
|
|
* | memory stack |
|
|
* | | <-- sp (lowest addr)
|
|
* +---------------------+
|
|
*
|
|
* Observe that we copy the unat values that are in pt_regs and switch_stack. Spilling an
|
|
* integer to address X causes bit N in ar.unat to be set to the NaT bit of the register,
|
|
* with N=(X & 0x1ff)/8. Thus, copying the unat value preserves the NaT bits ONLY if the
|
|
* pt_regs structure in the parent is congruent to that of the child, modulo 512. Since
|
|
* the stack is page aligned and the page size is at least 4KB, this is always the case,
|
|
* so there is nothing to worry about.
|
|
*/
|
|
int
|
|
copy_thread_tls(unsigned long clone_flags, unsigned long user_stack_base,
|
|
unsigned long user_stack_size, struct task_struct *p,
|
|
unsigned long tls)
|
|
{
|
|
extern char ia64_ret_from_clone;
|
|
struct switch_stack *child_stack, *stack;
|
|
unsigned long rbs, child_rbs, rbs_size;
|
|
struct pt_regs *child_ptregs;
|
|
struct pt_regs *regs = current_pt_regs();
|
|
int retval = 0;
|
|
|
|
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
|
|
child_stack = (struct switch_stack *) child_ptregs - 1;
|
|
|
|
rbs = (unsigned long) current + IA64_RBS_OFFSET;
|
|
child_rbs = (unsigned long) p + IA64_RBS_OFFSET;
|
|
|
|
/* copy parts of thread_struct: */
|
|
p->thread.ksp = (unsigned long) child_stack - 16;
|
|
|
|
/*
|
|
* NOTE: The calling convention considers all floating point
|
|
* registers in the high partition (fph) to be scratch. Since
|
|
* the only way to get to this point is through a system call,
|
|
* we know that the values in fph are all dead. Hence, there
|
|
* is no need to inherit the fph state from the parent to the
|
|
* child and all we have to do is to make sure that
|
|
* IA64_THREAD_FPH_VALID is cleared in the child.
|
|
*
|
|
* XXX We could push this optimization a bit further by
|
|
* clearing IA64_THREAD_FPH_VALID on ANY system call.
|
|
* However, it's not clear this is worth doing. Also, it
|
|
* would be a slight deviation from the normal Linux system
|
|
* call behavior where scratch registers are preserved across
|
|
* system calls (unless used by the system call itself).
|
|
*/
|
|
# define THREAD_FLAGS_TO_CLEAR (IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID \
|
|
| IA64_THREAD_PM_VALID)
|
|
# define THREAD_FLAGS_TO_SET 0
|
|
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
|
|
| THREAD_FLAGS_TO_SET);
|
|
|
|
ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
|
|
|
|
if (unlikely(p->flags & PF_KTHREAD)) {
|
|
if (unlikely(!user_stack_base)) {
|
|
/* fork_idle() called us */
|
|
return 0;
|
|
}
|
|
memset(child_stack, 0, sizeof(*child_ptregs) + sizeof(*child_stack));
|
|
child_stack->r4 = user_stack_base; /* payload */
|
|
child_stack->r5 = user_stack_size; /* argument */
|
|
/*
|
|
* Preserve PSR bits, except for bits 32-34 and 37-45,
|
|
* which we can't read.
|
|
*/
|
|
child_ptregs->cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
|
|
/* mark as valid, empty frame */
|
|
child_ptregs->cr_ifs = 1UL << 63;
|
|
child_stack->ar_fpsr = child_ptregs->ar_fpsr
|
|
= ia64_getreg(_IA64_REG_AR_FPSR);
|
|
child_stack->pr = (1 << PRED_KERNEL_STACK);
|
|
child_stack->ar_bspstore = child_rbs;
|
|
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
|
|
|
/* stop some PSR bits from being inherited.
|
|
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
|
* therefore we must specify them explicitly here and not include them in
|
|
* IA64_PSR_BITS_TO_CLEAR.
|
|
*/
|
|
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
|
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
|
|
|
return 0;
|
|
}
|
|
stack = ((struct switch_stack *) regs) - 1;
|
|
/* copy parent's switch_stack & pt_regs to child: */
|
|
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
|
|
|
|
/* copy the parent's register backing store to the child: */
|
|
rbs_size = stack->ar_bspstore - rbs;
|
|
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
|
|
if (clone_flags & CLONE_SETTLS)
|
|
child_ptregs->r13 = tls;
|
|
if (user_stack_base) {
|
|
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
|
|
child_ptregs->ar_bspstore = user_stack_base;
|
|
child_ptregs->ar_rnat = 0;
|
|
child_ptregs->loadrs = 0;
|
|
}
|
|
child_stack->ar_bspstore = child_rbs + rbs_size;
|
|
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
|
|
|
/* stop some PSR bits from being inherited.
|
|
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
|
* therefore we must specify them explicitly here and not include them in
|
|
* IA64_PSR_BITS_TO_CLEAR.
|
|
*/
|
|
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
|
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
|
|
|
#ifdef CONFIG_PERFMON
|
|
if (current->thread.pfm_context)
|
|
pfm_inherit(p, child_ptregs);
|
|
#endif
|
|
return retval;
|
|
}
|
|
|
|
asmlinkage long ia64_clone(unsigned long clone_flags, unsigned long stack_start,
|
|
unsigned long stack_size, unsigned long parent_tidptr,
|
|
unsigned long child_tidptr, unsigned long tls)
|
|
{
|
|
struct kernel_clone_args args = {
|
|
.flags = (lower_32_bits(clone_flags) & ~CSIGNAL),
|
|
.pidfd = (int __user *)parent_tidptr,
|
|
.child_tid = (int __user *)child_tidptr,
|
|
.parent_tid = (int __user *)parent_tidptr,
|
|
.exit_signal = (lower_32_bits(clone_flags) & CSIGNAL),
|
|
.stack = stack_start,
|
|
.stack_size = stack_size,
|
|
.tls = tls,
|
|
};
|
|
|
|
return _do_fork(&args);
|
|
}
|
|
|
|
static void
|
|
do_copy_task_regs (struct task_struct *task, struct unw_frame_info *info, void *arg)
|
|
{
|
|
unsigned long mask, sp, nat_bits = 0, ar_rnat, urbs_end, cfm;
|
|
unsigned long uninitialized_var(ip); /* GCC be quiet */
|
|
elf_greg_t *dst = arg;
|
|
struct pt_regs *pt;
|
|
char nat;
|
|
int i;
|
|
|
|
memset(dst, 0, sizeof(elf_gregset_t)); /* don't leak any kernel bits to user-level */
|
|
|
|
if (unw_unwind_to_user(info) < 0)
|
|
return;
|
|
|
|
unw_get_sp(info, &sp);
|
|
pt = (struct pt_regs *) (sp + 16);
|
|
|
|
urbs_end = ia64_get_user_rbs_end(task, pt, &cfm);
|
|
|
|
if (ia64_sync_user_rbs(task, info->sw, pt->ar_bspstore, urbs_end) < 0)
|
|
return;
|
|
|
|
ia64_peek(task, info->sw, urbs_end, (long) ia64_rse_rnat_addr((long *) urbs_end),
|
|
&ar_rnat);
|
|
|
|
/*
|
|
* coredump format:
|
|
* r0-r31
|
|
* NaT bits (for r0-r31; bit N == 1 iff rN is a NaT)
|
|
* predicate registers (p0-p63)
|
|
* b0-b7
|
|
* ip cfm user-mask
|
|
* ar.rsc ar.bsp ar.bspstore ar.rnat
|
|
* ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
|
|
*/
|
|
|
|
/* r0 is zero */
|
|
for (i = 1, mask = (1UL << i); i < 32; ++i) {
|
|
unw_get_gr(info, i, &dst[i], &nat);
|
|
if (nat)
|
|
nat_bits |= mask;
|
|
mask <<= 1;
|
|
}
|
|
dst[32] = nat_bits;
|
|
unw_get_pr(info, &dst[33]);
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
unw_get_br(info, i, &dst[34 + i]);
|
|
|
|
unw_get_rp(info, &ip);
|
|
dst[42] = ip + ia64_psr(pt)->ri;
|
|
dst[43] = cfm;
|
|
dst[44] = pt->cr_ipsr & IA64_PSR_UM;
|
|
|
|
unw_get_ar(info, UNW_AR_RSC, &dst[45]);
|
|
/*
|
|
* For bsp and bspstore, unw_get_ar() would return the kernel
|
|
* addresses, but we need the user-level addresses instead:
|
|
*/
|
|
dst[46] = urbs_end; /* note: by convention PT_AR_BSP points to the end of the urbs! */
|
|
dst[47] = pt->ar_bspstore;
|
|
dst[48] = ar_rnat;
|
|
unw_get_ar(info, UNW_AR_CCV, &dst[49]);
|
|
unw_get_ar(info, UNW_AR_UNAT, &dst[50]);
|
|
unw_get_ar(info, UNW_AR_FPSR, &dst[51]);
|
|
dst[52] = pt->ar_pfs; /* UNW_AR_PFS is == to pt->cr_ifs for interrupt frames */
|
|
unw_get_ar(info, UNW_AR_LC, &dst[53]);
|
|
unw_get_ar(info, UNW_AR_EC, &dst[54]);
|
|
unw_get_ar(info, UNW_AR_CSD, &dst[55]);
|
|
unw_get_ar(info, UNW_AR_SSD, &dst[56]);
|
|
}
|
|
|
|
void
|
|
do_dump_task_fpu (struct task_struct *task, struct unw_frame_info *info, void *arg)
|
|
{
|
|
elf_fpreg_t *dst = arg;
|
|
int i;
|
|
|
|
memset(dst, 0, sizeof(elf_fpregset_t)); /* don't leak any "random" bits */
|
|
|
|
if (unw_unwind_to_user(info) < 0)
|
|
return;
|
|
|
|
/* f0 is 0.0, f1 is 1.0 */
|
|
|
|
for (i = 2; i < 32; ++i)
|
|
unw_get_fr(info, i, dst + i);
|
|
|
|
ia64_flush_fph(task);
|
|
if ((task->thread.flags & IA64_THREAD_FPH_VALID) != 0)
|
|
memcpy(dst + 32, task->thread.fph, 96*16);
|
|
}
|
|
|
|
void
|
|
do_copy_regs (struct unw_frame_info *info, void *arg)
|
|
{
|
|
do_copy_task_regs(current, info, arg);
|
|
}
|
|
|
|
void
|
|
do_dump_fpu (struct unw_frame_info *info, void *arg)
|
|
{
|
|
do_dump_task_fpu(current, info, arg);
|
|
}
|
|
|
|
void
|
|
ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
|
|
{
|
|
unw_init_running(do_copy_regs, dst);
|
|
}
|
|
|
|
int
|
|
dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
|
|
{
|
|
unw_init_running(do_dump_fpu, dst);
|
|
return 1; /* f0-f31 are always valid so we always return 1 */
|
|
}
|
|
|
|
/*
|
|
* Flush thread state. This is called when a thread does an execve().
|
|
*/
|
|
void
|
|
flush_thread (void)
|
|
{
|
|
/* drop floating-point and debug-register state if it exists: */
|
|
current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID);
|
|
ia64_drop_fpu(current);
|
|
}
|
|
|
|
/*
|
|
* Clean up state associated with a thread. This is called when
|
|
* the thread calls exit().
|
|
*/
|
|
void
|
|
exit_thread (struct task_struct *tsk)
|
|
{
|
|
|
|
ia64_drop_fpu(tsk);
|
|
#ifdef CONFIG_PERFMON
|
|
/* if needed, stop monitoring and flush state to perfmon context */
|
|
if (tsk->thread.pfm_context)
|
|
pfm_exit_thread(tsk);
|
|
|
|
/* free debug register resources */
|
|
if (tsk->thread.flags & IA64_THREAD_DBG_VALID)
|
|
pfm_release_debug_registers(tsk);
|
|
#endif
|
|
}
|
|
|
|
unsigned long
|
|
get_wchan (struct task_struct *p)
|
|
{
|
|
struct unw_frame_info info;
|
|
unsigned long ip;
|
|
int count = 0;
|
|
|
|
if (!p || p == current || p->state == TASK_RUNNING)
|
|
return 0;
|
|
|
|
/*
|
|
* Note: p may not be a blocked task (it could be current or
|
|
* another process running on some other CPU. Rather than
|
|
* trying to determine if p is really blocked, we just assume
|
|
* it's blocked and rely on the unwind routines to fail
|
|
* gracefully if the process wasn't really blocked after all.
|
|
* --davidm 99/12/15
|
|
*/
|
|
unw_init_from_blocked_task(&info, p);
|
|
do {
|
|
if (p->state == TASK_RUNNING)
|
|
return 0;
|
|
if (unw_unwind(&info) < 0)
|
|
return 0;
|
|
unw_get_ip(&info, &ip);
|
|
if (!in_sched_functions(ip))
|
|
return ip;
|
|
} while (count++ < 16);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
cpu_halt (void)
|
|
{
|
|
pal_power_mgmt_info_u_t power_info[8];
|
|
unsigned long min_power;
|
|
int i, min_power_state;
|
|
|
|
if (ia64_pal_halt_info(power_info) != 0)
|
|
return;
|
|
|
|
min_power_state = 0;
|
|
min_power = power_info[0].pal_power_mgmt_info_s.power_consumption;
|
|
for (i = 1; i < 8; ++i)
|
|
if (power_info[i].pal_power_mgmt_info_s.im
|
|
&& power_info[i].pal_power_mgmt_info_s.power_consumption < min_power) {
|
|
min_power = power_info[i].pal_power_mgmt_info_s.power_consumption;
|
|
min_power_state = i;
|
|
}
|
|
|
|
while (1)
|
|
ia64_pal_halt(min_power_state);
|
|
}
|
|
|
|
void machine_shutdown(void)
|
|
{
|
|
smp_shutdown_nonboot_cpus(reboot_cpu);
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
kexec_disable_iosapic();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
machine_restart (char *restart_cmd)
|
|
{
|
|
(void) notify_die(DIE_MACHINE_RESTART, restart_cmd, NULL, 0, 0, 0);
|
|
efi_reboot(REBOOT_WARM, NULL);
|
|
}
|
|
|
|
void
|
|
machine_halt (void)
|
|
{
|
|
(void) notify_die(DIE_MACHINE_HALT, "", NULL, 0, 0, 0);
|
|
cpu_halt();
|
|
}
|
|
|
|
void
|
|
machine_power_off (void)
|
|
{
|
|
if (pm_power_off)
|
|
pm_power_off();
|
|
machine_halt();
|
|
}
|
|
|
|
EXPORT_SYMBOL(ia64_delay_loop);
|