Commit 687b12ba authored by Aurelien Jacquiot's avatar Aurelien Jacquiot Committed by Mark Salter

C6X: process management

Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>
Signed-off-by: default avatarAurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: default avatarMark Salter <msalter@redhat.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 14aa7e8b
/*
* Port on Texas Instruments TMS320C6x architecture
*
* Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
* Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
*
* Updated for 2.6.34: Mark Salter <msalter@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ASM_C6X_PROCESSOR_H
#define _ASM_C6X_PROCESSOR_H
#include <asm/ptrace.h>
#include <asm/page.h>
#include <asm/current.h>
/*
* Default implementation of macro that returns current
* instruction pointer ("program counter").
*/
#define current_text_addr() \
({ \
void *__pc; \
asm("mvc .S2 pce1,%0\n" : "=b"(__pc)); \
__pc; \
})
/*
* User space process size. This is mostly meaningless for NOMMU
* but some C6X processors may have RAM addresses up to 0xFFFFFFFF.
* Since calls like mmap() can return an address or an error, we
* have to allow room for error returns when code does something
* like:
*
* addr = do_mmap(...)
* if ((unsigned long)addr >= TASK_SIZE)
* ... its an error code, not an address ...
*
* Here, we allow for 4096 error codes which means we really can't
* use the last 4K page on systems with RAM extending all the way
* to the end of the 32-bit address space.
*/
#define TASK_SIZE 0xFFFFF000
/*
* This decides where the kernel will search for a free chunk of vm
* space during mmap's. We won't be using it
*/
#define TASK_UNMAPPED_BASE 0
struct thread_struct {
unsigned long long b15_14;
unsigned long long a15_14;
unsigned long long b13_12;
unsigned long long a13_12;
unsigned long long b11_10;
unsigned long long a11_10;
unsigned long long ricl_icl;
unsigned long usp; /* user stack pointer */
unsigned long pc; /* kernel pc */
unsigned long wchan;
};
#define INIT_THREAD \
{ \
.usp = 0, \
.wchan = 0, \
}
#define INIT_MMAP { \
&init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, \
NULL, NULL }
#define task_pt_regs(task) \
((struct pt_regs *)(THREAD_START_SP + task_stack_page(task)) - 1)
#define alloc_kernel_stack() __get_free_page(GFP_KERNEL)
#define free_kernel_stack(page) free_page((page))
/* Forward declaration, a strange C thing */
struct task_struct;
extern void start_thread(struct pt_regs *regs, unsigned int pc,
unsigned long usp);
/* Free all resources held by a thread. */
static inline void release_thread(struct task_struct *dead_task)
{
}
/* Prepare to copy thread state - unlazy all lazy status */
#define prepare_to_copy(tsk) do { } while (0)
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
#define copy_segments(tsk, mm) do { } while (0)
#define release_segments(mm) do { } while (0)
/*
* saved PC of a blocked thread.
*/
#define thread_saved_pc(tsk) (task_pt_regs(tsk)->pc)
/*
* saved kernel SP and DP of a blocked thread.
*/
#ifdef _BIG_ENDIAN
#define thread_saved_ksp(tsk) \
(*(unsigned long *)&(tsk)->thread.b15_14)
#define thread_saved_dp(tsk) \
(*(((unsigned long *)&(tsk)->thread.b15_14) + 1))
#else
#define thread_saved_ksp(tsk) \
(*(((unsigned long *)&(tsk)->thread.b15_14) + 1))
#define thread_saved_dp(tsk) \
(*(unsigned long *)&(tsk)->thread.b15_14)
#endif
extern unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) (task_pt_regs(task)->pc)
#define KSTK_ESP(tsk) (task_pt_regs(task)->sp)
#define cpu_relax() do { } while (0)
extern const struct seq_operations cpuinfo_op;
#endif /* ASM_C6X_PROCESSOR_H */
/*
* Port on Texas Instruments TMS320C6x architecture
*
* Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
* Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
*
* Updated for 2.6.3x: Mark Salter <msalter@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ASM_C6X_THREAD_INFO_H
#define _ASM_C6X_THREAD_INFO_H
#ifdef __KERNEL__
#include <asm/page.h>
#ifdef CONFIG_4KSTACKS
#define THREAD_SIZE 4096
#define THREAD_SHIFT 12
#define THREAD_ORDER 0
#else
#define THREAD_SIZE 8192
#define THREAD_SHIFT 13
#define THREAD_ORDER 1
#endif
#define THREAD_START_SP (THREAD_SIZE - 8)
#ifndef __ASSEMBLY__
typedef struct {
unsigned long seg;
} mm_segment_t;
/*
* low level task data.
*/
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
unsigned long flags; /* low level flags */
int cpu; /* cpu we're on */
int preempt_count; /* 0 = preemptable, <0 = BUG */
mm_segment_t addr_limit; /* thread address space */
struct restart_block restart_block;
};
/*
* macros/functions for gaining access to the thread information structure
*
* preempt_count needs to be 1 initially, until the scheduler is functional.
*/
#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
.exec_domain = &default_exec_domain, \
.flags = 0, \
.cpu = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
}
#define init_thread_info (init_thread_union.thread_info)
#define init_stack (init_thread_union.stack)
/* get the thread information struct of current task */
static inline __attribute__((const))
struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
asm volatile (" clr .s2 B15,0,%1,%0\n"
: "=b" (ti)
: "Iu5" (THREAD_SHIFT - 1));
return ti;
}
#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
/* thread information allocation */
#ifdef CONFIG_DEBUG_STACK_USAGE
#define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO)
#else
#define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK)
#endif
#define alloc_thread_info_node(tsk, node) \
((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))
#define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_ORDER)
#define get_thread_info(ti) get_task_struct((ti)->task)
#define put_thread_info(ti) put_task_struct((ti)->task)
#endif /* __ASSEMBLY__ */
#define PREEMPT_ACTIVE 0x10000000
/*
* thread information flag bit numbers
* - pending work-to-be-done flags are in LSW
* - other flags in MSW
*/
#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */
#define TIF_SIGPENDING 2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_RESTORE_SIGMASK 4 /* restore signal mask in do_signal() */
#define TIF_POLLING_NRFLAG 16 /* true if polling TIF_NEED_RESCHED */
#define TIF_MEMDIE 17 /* OOM killer killed process */
#define TIF_WORK_MASK 0x00007FFE /* work on irq/exception return */
#define TIF_ALLWORK_MASK 0x00007FFF /* work on any return to u-space */
#endif /* __KERNEL__ */
#endif /* _ASM_C6X_THREAD_INFO_H */
/*
* Port on Texas Instruments TMS320C6x architecture
*
* Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated
* Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/init_task.h>
#include <linux/tick.h>
#include <linux/mqueue.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
#include <asm/syscalls.h>
/* hooks for board specific support */
void (*c6x_restart)(void);
void (*c6x_halt)(void);
extern asmlinkage void ret_from_fork(void);
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
/*
* Initial thread structure.
*/
union thread_union init_thread_union __init_task_data = {
INIT_THREAD_INFO(init_task)
};
/*
* Initial task structure.
*/
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);
/*
* power off function, if any
*/
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
static void c6x_idle(void)
{
unsigned long tmp;
/*
* Put local_irq_enable and idle in same execute packet
* to make them atomic and avoid race to idle with
* interrupts enabled.
*/
asm volatile (" mvc .s2 CSR,%0\n"
" or .d2 1,%0,%0\n"
" mvc .s2 %0,CSR\n"
"|| idle\n"
: "=b"(tmp));
}
/*
* The idle loop for C64x
*/
void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
while (1) {
local_irq_disable();
if (need_resched()) {
local_irq_enable();
break;
}
c6x_idle(); /* enables local irqs */
}
tick_nohz_restart_sched_tick();
preempt_enable_no_resched();
schedule();
preempt_disable();
}
}
static void halt_loop(void)
{
printk(KERN_EMERG "System Halted, OK to turn off power\n");
local_irq_disable();
while (1)
asm volatile("idle\n");
}
void machine_restart(char *__unused)
{
if (c6x_restart)
c6x_restart();
halt_loop();
}
void machine_halt(void)
{
if (c6x_halt)
c6x_halt();
halt_loop();
}
void machine_power_off(void)
{
if (pm_power_off)
pm_power_off();
halt_loop();
}
static void kernel_thread_helper(int dummy, void *arg, int (*fn)(void *))
{
do_exit(fn(arg));
}
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
/*
* copy_thread sets a4 to zero (child return from fork)
* so we can't just set things up to directly return to
* fn.
*/
memset(&regs, 0, sizeof(regs));
regs.b4 = (unsigned long) arg;
regs.a6 = (unsigned long) fn;
regs.pc = (unsigned long) kernel_thread_helper;
local_save_flags(regs.csr);
regs.csr |= 1;
regs.tsr = 5; /* Set GEE and GIE in TSR */
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, -1, &regs,
0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
void flush_thread(void)
{
}
void exit_thread(void)
{
}
SYSCALL_DEFINE1(c6x_clone, struct pt_regs *, regs)
{
unsigned long clone_flags;
unsigned long newsp;
/* syscall puts clone_flags in A4 and usp in B4 */
clone_flags = regs->orig_a4;
if (regs->b4)
newsp = regs->b4;
else
newsp = regs->sp;
return do_fork(clone_flags, newsp, regs, 0, (int __user *)regs->a6,
(int __user *)regs->b6);
}
/*
* Do necessary setup to start up a newly executed thread.
*/
void start_thread(struct pt_regs *regs, unsigned int pc, unsigned long usp)
{
/*
* The binfmt loader will setup a "full" stack, but the C6X
* operates an "empty" stack. So we adjust the usp so that
* argc doesn't get destroyed if an interrupt is taken before
* it is read from the stack.
*
* NB: Library startup code needs to match this.
*/
usp -= 8;
set_fs(USER_DS);
regs->pc = pc;
regs->sp = usp;
regs->tsr |= 0x40; /* set user mode */
current->thread.usp = usp;
}
/*
* Copy a new thread context in its stack.
*/
int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long ustk_size,
struct task_struct *p, struct pt_regs *regs)
{
struct pt_regs *childregs;
childregs = task_pt_regs(p);
*childregs = *regs;
childregs->a4 = 0;
if (usp == -1)
/* case of __kernel_thread: we return to supervisor space */
childregs->sp = (unsigned long)(childregs + 1);
else
/* Otherwise use the given stack */
childregs->sp = usp;
/* Set usp/ksp */
p->thread.usp = childregs->sp;
/* switch_to uses stack to save/restore 14 callee-saved regs */
thread_saved_ksp(p) = (unsigned long)childregs - 8;
p->thread.pc = (unsigned int) ret_from_fork;
p->thread.wchan = (unsigned long) ret_from_fork;
#ifdef __DSBT__
{
unsigned long dp;
asm volatile ("mv .S2 b14,%0\n" : "=b"(dp));
thread_saved_dp(p) = dp;
if (usp == -1)
childregs->dp = dp;
}
#endif
return 0;
}
/*
* c6x_execve() executes a new program.
*/
SYSCALL_DEFINE4(c6x_execve, const char __user *, name,
const char __user *const __user *, argv,
const char __user *const __user *, envp,
struct pt_regs *, regs)
{
int error;
char *filename;
filename = getname(name);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, argv, envp, regs);
putname(filename);
out:
return error;
}
unsigned long get_wchan(struct task_struct *p)
{
return p->thread.wchan;
}
/*
* Copyright (C) 2011 Texas Instruments Incorporated
* Author: Mark Salter (msalter@redhat.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#define SP B15
/*
* void __switch_to(struct thread_info *prev,
* struct thread_info *next,
* struct task_struct *tsk) ;
*/
ENTRY(__switch_to)
LDDW .D2T2 *+B4(THREAD_B15_14),B7:B6
|| MV .L2X A4,B5 ; prev
|| MV .L1X B4,A5 ; next
|| MVC .S2 RILC,B1
STW .D2T2 B3,*+B5(THREAD_PC)
|| STDW .D1T1 A13:A12,*+A4(THREAD_A13_12)
|| MVC .S2 ILC,B0
LDW .D2T2 *+B4(THREAD_PC),B3
|| LDDW .D1T1 *+A5(THREAD_A13_12),A13:A12
STDW .D1T1 A11:A10,*+A4(THREAD_A11_10)
|| STDW .D2T2 B1:B0,*+B5(THREAD_RICL_ICL)
#ifndef __DSBT__
|| MVKL .S2 current_ksp,B1
#endif
STDW .D2T2 B15:B14,*+B5(THREAD_B15_14)
|| STDW .D1T1 A15:A14,*+A4(THREAD_A15_14)
#ifndef __DSBT__
|| MVKH .S2 current_ksp,B1
#endif
;; Switch to next SP
MV .S2 B7,SP
#ifdef __DSBT__
|| STW .D2T2 B7,*+B14(current_ksp)
#else
|| STW .D2T2 B7,*B1
|| MV .L2 B6,B14
#endif
|| LDDW .D1T1 *+A5(THREAD_RICL_ICL),A1:A0
STDW .D2T2 B11:B10,*+B5(THREAD_B11_10)
|| LDDW .D1T1 *+A5(THREAD_A15_14),A15:A14
STDW .D2T2 B13:B12,*+B5(THREAD_B13_12)
|| LDDW .D1T1 *+A5(THREAD_A11_10),A11:A10
B .S2 B3 ; return in next E1
|| LDDW .D2T2 *+B4(THREAD_B13_12),B13:B12
LDDW .D2T2 *+B4(THREAD_B11_10),B11:B10
NOP
MV .L2X A0,B0
|| MV .S1 A6,A4
MVC .S2 B0,ILC
|| MV .L2X A1,B1
MVC .S2 B1,RILC
ENDPROC(__switch_to)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment