Sunday, August 27, 2006

System call in Blackfin - Cont' - resend


================================================
arch/blackfin/mach-common/ints-priority-sc.c:

int init_arch_irq()
{

#ifndef CONFIG_KGDB
*pEVT0 = evt_emulation;
#==endif
*pEVT2 = evt_evt2;
*pEVT3 = trap;
*pEVT5 = evt_ivhw;
*pEVT6 = evt_timer;
*pEVT7 = evt_evt7;
*pEVT8 = evt_evt8;
*pEVT9 = evt_evt9;
*pEVT10 = evt_evt10;
*pEVT11 = evt_evt11;
*pEVT12 = evt_evt12;
*pEVT13 = evt_evt13;
*pEVT14 = evt14_softirq;
*pEVT15 = evt_system_call;
__builtin_bfin_csync();
}
=======================================================
arch/blackfin/mach-common/entry.S:

ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor
mode)*/
/* Since the kernel stack can be anywhere, it's not guaranteed
to be
* covered by a CPLB. Switch to an exception stack; use RETN as
a
* scratch register (for want of a better option).
*/
/----------------------------------
#define ENTRY(name) .globl name; ALIGN; name:
-----------------------------------/

retn = sp;
/-------------------------------
Use RETN just as a tmp register?
--------------------------------/

sp.l = exception_stack_top;
sp.h = exception_stack_top;
/------------------------------------
In entry.S
/* Put this in the kernel data section - that should always be covered
by
* a CPLB.
*/
exception_stack:
.rept 1024
.long 0;
.endr
exception_stack_top:
----------------------------------------/

/* Try to deal with syscalls quickly. */
[--sp] = ASTAT;
[--sp] = (R7:6, P5:4);
r7 = SEQSTAT; /* reason code is in bit 5:0 */
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
p5.h = extable;
p5.l = extable;
p4 = r7;
p5 = p5 + (p4 << 2);
p4 = [p5];
jump (p4);
common_restore_context
/---------------------------------------------
What is the relationship between the extable and the exception handler
entry?? When is extable get initialized?
-----------------------------------------------/

badsys:
r7 = -ENOSYS; /* signextending enough */
[sp + PT_R0] = r7; /* return value from system call */
jump syscall_really_exit;

ENTRY(ex_syscall)
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
raise 15; /* invoked by TRAP #0, for sys call */
sp = retn;
rtx

==============================================
mach-comm/interrupt.S

/* interrupt routine for system_call - 15 */
ENTRY(_evt_system_call)
SAVE_CONTEXT_SYSCALL
#ifdef CONFIG_FRAME_POINTER
fp = 0;
#==endif
call system_call;
jump common_restore_context;
/------------------------------------
Note: It does not go through common_int_entry:
-------------------------------------/

==============================================
entry.S:

ENTRY(system_call)
/* Store IPEND */
p2.l = lo(IPEND);
p2.h = hi(IPEND);
csync;
r0 = [p2];
[sp + PT_IPEND] = r0;

/* Store RETS for now */
r0 = rets;
/--------------------------------
call system_call;
jump common_restore_context;
--------------------------------/

[sp + PT_RESERVED] = r0;
/* Set the stack for the current process */
r7 = sp;
r6.l = lo(ALIGN_PAGE_MASK);
r6.h = hi(ALIGN_PAGE_MASK);
r7 = r7 & r6; /*thread_info*/
p2 = r7;
p2 = [p2];

[p2+(TASK_THREAD+THREAD_KSP)] = sp;
#ifdef CONFIG_IPIPE
r0 = sp;
SP += -12;
call ___ipipe_syscall_root;
SP += 12;
cc = r0 == 1;
/--------------------------------
Should not pass to Linux, no tail work
------------------------------------/
if cc jump syscall_really_exit;
cc = r0 == -1;
/-------------------------------------
Should not pass to Linux, tail work (handling signal)
-------------------------------------/
if cc jump resume_userspace;
/-----------------------------------------
Should pass to Linux
------------------------------------------/
r3 = [sp + PT_R3];
r4 = [sp + PT_R4];
p0 = [sp + PT_ORIG_P0];
#==endif /* CONFIG_IPIPE */

/* Check the System Call */
r7 = __NR_syscall;
/*System call number is passed in P0 */
r5 = p0;
cc = r5 < r7;
if ! cc jump badsys;

/------------------------------------
Check whether the sys call is valid or not.
--------------------------------------/

/* Execute the appropriate system call */

p4 = r5;
p5.l = sys_call_table;
p5.h = sys_call_table;
p5 = p5 + (p4 << 2);
r0 = [sp + PT_R0];
r1 = [sp + PT_R1];
r2 = [sp + PT_R2];
p5 = [p5];

/* are we tracing syscalls?*/
r7 = sp;
r6.l = lo(ALIGN_PAGE_MASK);
r6.h = hi(ALIGN_PAGE_MASK);
r7 = r7 & r6;
/----------------------------
Get thread_info:
see asm-blackfin/thread_info.h:
/* Given a task stack pointer, you can find it's task structure
* just by masking it to the 8K boundary.
*/
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
__asm__("%0 = sp;": "=&d"(ti):
);
return (struct thread_info *)((long)ti & ~8191UL);
}
-----------------------------/
p2 = r7;
r7 = [p2+TI_FLAGS];
CC = BITTST(r7,TIF_SYSCALL_TRACE);
if CC JUMP sys_trace;

[--sp] = r4;
[--sp] = r3;
SP += -12;
call (p5);
SP += 20;
[sp + PT_R0] = r0;
/-----------------------------
Call the real syscall: r0 is the return value??
-----------------------------/

resume_userspace:
r7 = sp;
r4.l = lo(ALIGN_PAGE_MASK);
r4.h = hi(ALIGN_PAGE_MASK);
r7 = r7 & r4; /*thread_info->flags*/
p5 = r7;
resume_userspace_1:
/* Disable interrupts. */
[--sp] = reti;
reti = [sp++];

/----------------------------------------------
To disable nesting interrupts - IRQ15

Instructions that access the RETI register do have an implicit site
effect — reading the RETI register enables interrupt nesting. Writing to
it disables nesting again.
------------------------------------------------/

r7 = [p5 + TI_FLAGS];
r4.l = lo(_TIF_WORK_MASK);
r4.h = hi(_TIF_WORK_MASK);
r7 = r7 & r4;

syscall_resched:
cc = BITTST(r7, TIF_NEED_RESCHED);

/-----------------------
The only exit for "syscall_resched"
------------------------/
if !cc jump syscall_sigpending;

/--------------------------------------
OK. Need reschedule
-----------------------------------------/
/* Reenable interrupts. */
[--sp] = reti;
r0 = [sp++];

SP += -12;
call _schedule;
SP += 12;

jump resume_userspace_1;
/-----------------------------------
Check again - until there is not need to do reschedule
------------------------------------/

syscall_sigpending:
cc = BITTST(r7, TIF_RESTORE_SIGMASK);
if cc jump syscall_do_signals;
cc = BITTST(r7, TIF_SIGPENDING);
if !cc jump syscall_really_exit;
syscall_do_signals:
/* Reenable interrupts. */
[--sp] = reti;
r0 = [sp++];

r0 = sp;
SP += -12;
call _do_signal;
SP += 12;

syscall_really_exit:
#ifdef CONFIG_IPIPE
[--sp] = reti;
r5 = [sp++];
#==endif /* CONFIG_IPIPE */
r5 = [sp + PT_RESERVED];
rets = r5;
rts;
/-----------------------------
Return to common_restore_context:

Then "rti" to exit from IRQ15, then "rtx" to exit from exception.

"The difference between a JUMP and a CALL is that
a CALL automatically loads the return address into the RETS register.
The return address is the next sequential address after the CALL
instruction."

-------------------------------/

sys_trace:
[--sp] = r3;
[--sp] = r2;
[--sp] = r1;
[--sp] = r0;
[--sp] = p5;
[--sp] = p2;
[--sp] = p1;
[--sp] = p0;
r1 = 0;
call _syscall_trace;
p0 = [sp++];
p1 = [sp++];
p2 = [sp++];
p5 = [sp++];
r0 = [sp++];
r1 = [sp++];
r2 = [sp++];
r3 = [sp++];

[--sp] = r4;
[--sp] = r3;
SP += -12;
call (p5);
SP += 20;
[sp + PT_R0] = r0;

[--sp] = r3;
[--sp] = r2;
[--sp] = r1;
[--sp] = r0;
[--sp] = p5;
[--sp] = p2;
[--sp] = p1;
[--sp] = p0;
r1 = 1;
call _syscall_trace;
p0 = [sp++];
p1 = [sp++];
p2 = [sp++];
p5 = [sp++];
r0 = [sp++];
r1 = [sp++];
r2 = [sp++];
r3 = [sp++];

jump resume_userspace;

ipipe-root.c
====================================================
asmlinkage int __ipipe_syscall_root(struct pt_regs *regs)
{
/---------------------------------------
r0 = sp; -- kernel stack
----------------------------------------/

ipipe_declare_cpuid;
unsigned long flags;

/*
* This routine either returns:
* 0 -- if the syscall is to be passed to Linux;
* 1 -- if the syscall should not be passed to Linux, and no
* tail work should be performed;
* -1 -- if the syscall should not be passed to Linux but the
* tail work has to be performed (for handling signals etc).
*/

/-----------------------------------------------------
#define IPIPE_EVENT_SYSCALL (IPIPE_FIRST_EVENT)
#define IPIPE_EVENT_SCHEDULE (IPIPE_FIRST_EVENT + 1)
#define IPIPE_EVENT_SIGWAKE (IPIPE_FIRST_EVENT + 2)
#define IPIPE_EVENT_SETSCHED (IPIPE_FIRST_EVENT + 3)
#define IPIPE_EVENT_EXIT (IPIPE_FIRST_EVENT + 4)
-------------------------------------------------------/

if (__ipipe_event_pipelined_p(IPIPE_EVENT_SYSCALL) &&
__ipipe_dispatch_event(IPIPE_EVENT_SYSCALL,regs) > 0) {

/------------------------------------------
Do not propagate the event to Linux
-------------------------------------------/
/*
* We might enter here over a non-root domain and exit
* over the root one as a result of the syscall
* (i.e. by recycling the register set of the current
* context across the migration), so we need to fixup
* the interrupt flag upon return too, so that
* __ipipe_unstall_iret_root() resets the correct
* stall bit on exit.
*/
if (ipipe_current_domain == ipipe_root_domain && !
in_atomic()) {

/-------------------------------------------
???
--------------------------------------------/

/*
* Sync pending VIRQs before _TIF_NEED_RESCHED
* is tested.
*/
ipipe_lock_cpu(flags);
if
((ipipe_root_domain->cpudata[cpuid].irq_pending_hi &
IPIPE_IRQMASK_VIRT) != 0)
__ipipe_sync_stage(IPIPE_IRQMASK_VIRT);
ipipe_unlock_cpu(flags);
return -1;
}
return 1;
}

return 0;
}

kernel/ipipe/core.c
==================================================================
/* __ipipe_dispatch_event() -- Low-level event dispatcher. */

int fastcall __ipipe_dispatch_event (unsigned event, void *data)
{
struct ipipe_domain *start_domain, *this_domain, *next_domain;
struct list_head *pos, *npos;
unsigned long flags;
ipipe_declare_cpuid;
int propagate = 1;

ipipe_lock_cpu(flags);

start_domain = this_domain = ipipe_percpu_domain[cpuid];

list_for_each_safe(pos,npos,&__ipipe_pipeline) {
/-----------------------------------------
Starting from the highest priority domain
------------------------------------------/

next_domain = list_entry(pos,struct
ipipe_domain,p_link);

/*
* Note: Domain migration may occur while running
* event or interrupt handlers, in which case the
* current register set is going to be recycled for a
* different domain than the initiating one. We do
* care for that, always tracking the current domain
* descriptor upon return from those handlers.
*/
if (next_domain->evhand[event] != NULL) {
/--------------------------------------------
#define rthal_catch_taskexit(hdlr) ipipe_catch_event(ipipe_root_domain,IPIPE_EVENT_EXIT,hdlr)
#define rthal_catch_sigwake(hdlr) ipipe_catch_event(ipipe_root_domain,IPIPE_EVENT_SIGWAKE,hdlr)
#define rthal_catch_schedule(hdlr) ipipe_catch_event(ipipe_root_domain,IPIPE_EVENT_SCHEDULE,hdlr)
#define rthal_catch_setsched(hdlr) ipipe_catch_event(&rthal_domain,IPIPE_EVENT_SETSCHED,hdlr)
#define rthal_catch_losyscall(hdlr) ipipe_catch_event(ipipe_root_domain,IPIPE_EVENT_SYSCALL,hdlr)
#define rthal_catch_hisyscall(hdlr) ipipe_catch_event(&rthal_domain,IPIPE_EVENT_SYSCALL,hdlr)
#define rthal_catch_exception(ex,hdlr) ipipe_catch_event(&rthal_domain,ex|IPIPE_EVENT_SELF,hdlr)

---------------------------------------------/

ipipe_percpu_domain[cpuid] = next_domain;
ipipe_unlock_cpu(flags);
propagate = !
next_domain->evhand[event](event,start_domain,data);
ipipe_lock_cpu(flags);
if (ipipe_percpu_domain[cpuid] != next_domain)
this_domain =
ipipe_percpu_domain[cpuid];
}

if (next_domain != ipipe_root_domain && /* NEVER sync
the root stage
here. */
next_domain->cpudata[cpuid].irq_pending_hi != 0 &&
!
test_bit(IPIPE_STALL_FLAG,&next_domain->cpudata[cpuid].status)) {
ipipe_percpu_domain[cpuid] = next_domain;
__ipipe_sync_stage(IPIPE_IRQMASK_ANY);
ipipe_load_cpuid();
if (ipipe_percpu_domain[cpuid] != next_domain)
this_domain =
ipipe_percpu_domain[cpuid];
}

ipipe_percpu_domain[cpuid] = this_domain;

if (next_domain == this_domain || !propagate)
break;
}

ipipe_unlock_cpu(flags);

return !propagate;
}

No comments:

Blog Archive