File: arch.c

package info (click to toggle)
rtlinux 3.1pre3-3
  • links: PTS
  • area: non-free
  • in suites: etch, etch-m68k
  • size: 4,896 kB
  • ctags: 4,228
  • sloc: ansic: 26,204; sh: 2,069; makefile: 1,414; perl: 855; tcl: 489; asm: 380; cpp: 42
file content (349 lines) | stat: -rw-r--r-- 10,362 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
 * (C) Finite State Machine Labs Inc. 1999-2001 <business@fsmlabs.com>
 *
 * Released under the terms of GPL 2.
 * Open RTLinux makes use of a patented process described in
 * US Patent 5,995,745. Use of this process is governed
 * by the Open RTLinux Patent License which can be obtained from
 * www.fsmlabs.com/PATENT or by sending email to
 * licensequestions@fsmlabs.com
 */

#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/irq.h>
#include <asm/irq.h>

#include "arch.h"
#include <rtl_core.h>
#include <rtl_sync.h>

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,1)
struct int_control_struct rtl_hard_int_control;
#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,1) */

irq_desc_t rtl_hard_irq_desc[NR_IRQS];
unsigned long hard_timer_interrupt, hard_do_IRQ, hard_do_IRQ2, hard_do_IRQ3;
int (*hard_get_irq)(struct pt_regs *);
int bogus_get_irq(struct pt_regs *);
unsigned int (*timer_handler)(struct pt_regs *r);
extern ulong ret_to_user_hook;
extern void flush_icache_range(ulong, ulong);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,1)
ulong ppc_cli_block[11], ppc_sti_block[11], ppc_save_flags_ptr_block[11],
	ppc_restore_flags_block[11];
#endif

void rtl_local_intercept(struct pt_regs *regs);

int rtl_irq_set_affinity (unsigned int irq, const unsigned long *mask, unsigned long *oldmask)
{
	return -1;
}

void rtl_hard_pic_end(unsigned int irq_nr)
{
	rtl_irqstate_t flags;

	rtl_no_interrupts(flags);
	if ( rtl_hard_irq_desc[irq_nr].handler && rtl_hard_irq_desc[irq_nr].handler->end )
		rtl_hard_irq_desc[irq_nr].handler->end(irq_nr);
	rtl_restore_interrupts(flags);
}

hw_irq_controller rtl_fake_pic =
{ " RTLinux PIC  ", NULL, NULL, rtl_virt_enable, rtl_virt_disable, NULL, /*rtl_hard_pic_end*/0 };

void ppc_rtl_soft_restore_flags(unsigned long x)
{
	rtl_soft_restore_flags(x&MSR_EE);
}

void rtl_set_lost(unsigned long irq)
{
	rtl_global_pend_irq(irq);
}

struct {
	atomic_t waiting;
	atomic_t done;
} sync_data = {{0},{0}};

/*
 * Make a copy of the hard version of these functions in the kernel, then write over them
 * with a jump to our soft version.
 * -- Cort <cort@fsmlabs.com>
 */
void write_func( ulong *hard_func, ulong *hard_func_end, ulong *copy_to, ulong *copy_to_end, ulong call_addr )
{
	unsigned long block[11] = {
		/* r3-r13 are caller save - so we use them */
		0x3821fff8, /* subi r1,r1,8 */
		0x7da802a6, /* mflr r13 */
		0x91a10000, /* stw r13,0(r1) */
		0x3d80dead, /* lis r12,0xdeadbeef@h */
		0x618cbeef, /* ori r12,r12,0xdeadbeef@l */
		0x7d8803a6, /* mtlr r12 */
		0x4e800021, /* blrl */
		0x81a10000, /* lwz r13,0(r1) */
		0x38210008, /* addi r1,r1,8 */
		0x7da803a6, /* mtlr r13 */
		0x4e800020  /* blr */
	};

	/* check to make sure the hard_func size is big enough to hold our patch-up */
	if ( (((ulong)hard_func_end) - ((ulong)hard_func)) < (sizeof(ulong)*11) )
	{
		printk( "write_func(): Cannot patch function at %p, too small\n",
			hard_func );
		return;
	}

	/*
	 * These blocks are allocated statically now, so we don't need to
	 * do checks at run-time.
	 * -- Cort <cort@fsmlabs.com>
	 */
#if 0	
	/*
	 * Check to make sure the copy_to func size is big enough to hold
	 * the hard version of the function - but only the amount that
	 * we need to copy (11 longs).
	 */
	if ( (((ulong)copy_to_end) - ((ulong)copy_to)) <
	     (((ulong)hard_func_end) - ((ulong)hard_func)) )
	{
		printk("write_func(): Cannot copy hard function at %p, "
		       "too large %lu <= %lu\n",
		       hard_func,
		       (((ulong)copy_to_end) - ((ulong)copy_to)),
		       (((ulong)hard_func_end) - ((ulong)hard_func)));
		return;
	}
#endif	
	
	/* make a copy of the function that we're going to over-write */
	memcpy( copy_to, hard_func, sizeof(long)*11 );
	flush_icache_range( (ulong)copy_to, (ulong)copy_to + (sizeof(ulong)*11) );

	/* over-write that function with a call to our version */
	block[3] = (block[3]&0xffff0000) | (((ulong)call_addr>>16)&0xffff);
	block[4] = (block[4]&0xffff0000) | ((ulong)call_addr&0xffff);
	memcpy( (ulong *)hard_func, &block[0], sizeof(ulong)*11 );
	flush_icache_range( (ulong)hard_func, ((ulong)hard_func)+(sizeof(ulong)*11));
}

/*
 * Architecture specific function to take over the handling of interrupts.
 * -- Cort
 */
int arch_takeover(void)
{
	int i;
	struct task_struct *p;
	void rtl_soft_sti_no_emulation(void);
#ifdef CONFIG_SMP	
	int timeout, cpus = smp_num_cpus - 1;
#endif /* CONFIG_SMP */	

	if ( timer_interrupt_intercept != (unsigned long)timer_interrupt )
	{
		printk("RTL: Timer interrupt already being intercepted!\n");
		return -1;
	}

#ifdef CONFIG_SMP	
	smp_call_function( sync_takeover, 0, 0 /*atomic */,0 /*don't wait*/ );
	/* everyone else is now starting to exec sync_function */
	timeout = jiffies + HZ;
	while ((atomic_read(&sync_data.waiting) != cpus)
			&& time_before(jiffies, timeout));
	if(atomic_read(&sync_data.waiting) != cpus)
	{
	       printk("rtl_smp_synchronize timed out\n");
       	       return -1;
	}
#endif /* CONFIG_SMP */
	
	__cli();

	/*
	 * Put our trampoline code, for the ret_from_int call to rtl_soft_sti(), at
	 * KERNELBASE. -- Cort
	 */
	{
		long trash[50];
		write_func( (ulong *)KERNELBASE, (ulong *)KERNELBASE+0x30, trash, &trash[50],
			    (ulong)rtl_soft_sti );
	}
	ret_to_user_hook = 0x48000000 | 1 |
		(((KERNELBASE+0x0) - (ulong)&ret_to_user_hook)&0x03fffffc);
	flush_icache_range((ulong)&ret_to_user_hook, (ulong)&ret_to_user_hook);
	
	/* copy the current (hard) versions from the kernel... */
	hard_timer_interrupt = timer_interrupt_intercept;
	hard_do_IRQ = do_IRQ_intercept;
	hard_get_irq = ppc_md.get_irq;

	memcpy(rtl_hard_irq_desc, irq_desc, sizeof(irq_desc_t)*NR_IRQS);
	
	/*
	 * Take over cli/sti and restore/save flags, then replace
	 * them with our own versions.
	 *
	 * __sti/__cli and friends changed with v2.4.2 of Linux/PPC to
	 * outlined functions instead of function pointers.
	 *   -- Cort Dougan <cort@fsmlabs.com>
	 */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,1)
	rtl_hard_int_control = int_control;
	int_control.int_cli = rtl_soft_cli;
	int_control.int_sti = rtl_soft_sti;
	int_control.int_save_flags = rtl_soft_save_flags;
	int_control.int_restore_flags = ppc_rtl_soft_restore_flags;
	int_control.int_set_lost = rtl_set_lost;
#else
	write_func( (ulong *)__cli, (ulong *)&__cli_end, &ppc_cli_block[0], &ppc_cli_block[11],
		    (ulong)rtl_soft_cli );
	write_func( (ulong *)__sti, (ulong *)&__sti_end, &ppc_sti_block[0], &ppc_sti_block[11],
		    (ulong)rtl_soft_sti );
	write_func( (ulong *)__save_flags_ptr, (ulong *)&__save_flags_ptr_end,
		    &ppc_save_flags_ptr_block[0], &ppc_save_flags_ptr_block[11],
		    (ulong)rtl_soft_save_flags );
	write_func( (ulong *)__restore_flags, (ulong *)&__restore_flags_end,
		    &ppc_restore_flags_block[0], &ppc_restore_flags_block[11],
		    (ulong)ppc_rtl_soft_restore_flags );
#endif	
	
	atomic_set(&ppc_n_lost_interrupts,0);
	for ( i = 0 ; i < NR_MASK_WORDS; i++ )
		ppc_lost_interrupts[i] = 0;
	do_IRQ_intercept = (unsigned long)rtl_intercept;
	timer_interrupt_intercept = (unsigned long)rtl_local_intercept;

	/*
	 * This should never be called since it's only used in the
	 * linux version of do_IRQ() but just in case...
	 * -- Cort
	 */
	ppc_md.get_irq = bogus_get_irq;

	/* replace all the interrupt controller pointers with our fake one */
	for ( i = 0 ; i < NR_IRQS; i++ )
		if ( irq_desc[i].handler )
			irq_desc[i].handler = &rtl_fake_pic;

	/*
	 * Now we need to correct for any tasks that may have
	 * MSR_EE cleared (interrupts disabled) in their task
	 * struct so they don't disable when switching back to
	 * them.
	 *  -- Cort
	 */
	/* have to do init_task separately */
	((struct pt_regs *)(init_task.thread.ksp+STACK_FRAME_OVERHEAD))->msr |= MSR_EE;
	for_each_task(p)
	{
		((struct pt_regs *)(p->thread.ksp+STACK_FRAME_OVERHEAD))->msr |= MSR_EE;
		if ( p->thread.regs )
			p->thread.regs->msr |= MSR_EE;
	}
	/*
	 * This hard sti matches the above cli() since the cli()
	 * was hard (RTLinux wasn't loaded yet).
	 *  -- Cort
	 */
	rtl_hard_sti();
	__sti();
#ifdef CONFIG_SMP
	atomic_set( &sync_data.done, 1);
#endif /* CONFIG_SMP */

	return 0;
}

void arch_giveup(void)
{
	int i;
	
	rtl_hard_cli();

	timer_interrupt_intercept = hard_timer_interrupt;

	do_IRQ_intercept = hard_do_IRQ;
	ppc_md.get_irq = hard_get_irq;

	/* clear out our call to to the rtl_soft_sti trampoline code in the ret from int path */
	ret_to_user_hook = 0x60000000; /* nop */
	flush_icache_range((ulong)&ret_to_user_hook, (ulong)&ret_to_user_hook);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,1)
	/* put the old copies of __cli/__sti and such back */
	memcpy( (void *)__cli, &ppc_cli_block[0], sizeof(ulong)*11 );
	flush_icache_range( (ulong)__cli, ((ulong)__cli)+(sizeof(ulong)*11));
	
	memcpy( (void *)__sti, &ppc_sti_block[0], sizeof(ulong)*11 );
	flush_icache_range( (ulong)__sti, ((ulong)__sti)+(sizeof(ulong)*11));
	
	memcpy( (void *)__save_flags_ptr, &ppc_save_flags_ptr_block[0], sizeof(ulong)*11 );
	flush_icache_range( (ulong)__save_flags_ptr, ((ulong)__save_flags_ptr)+(sizeof(ulong)*11));
	
	memcpy( (void *)__restore_flags, &ppc_restore_flags_block[0], sizeof(ulong)*11 );
	flush_icache_range( (ulong)__restore_flags, ((ulong)__restore_flags)+(sizeof(ulong)*11));
#else
	int_control = rtl_hard_int_control;
#endif	

	/* give Linux back the hard irq handlers */
	for ( i = 0; i < NR_IRQS; i++ )
		irq_desc[i].handler = rtl_hard_irq_desc[i].handler;

	/*
	 * This is safe since we have turned off RTLinux and
	 * the RTLinux __sti() will behave just like the normal
	 * linux __sti().  -- Cort
	 */
	__sti();
}

int bogus_get_irq(struct pt_regs *regs)
{
	printk("BOGUS_GET_IRQ! from %p\n", __builtin_return_address(0));
	*(unsigned long *)(0) = 1;
	return -1;
}

void dispatch_rtl_local_irq(int irq)
{
	struct pt_regs r;
	if ( !(unsigned long)timer_handler )
		printk("No timer handler in dispatch_rtl_local_irq!\n");
	else
		timer_handler(&r);
}

int rtl_free_local_irq(int i, unsigned int cpu)
{
	if ( (unsigned long)timer_handler )
	{
		(unsigned long)timer_handler = 0;
		clear_bit(0,&rtl_local[smp_processor_id()].rtirq);
	}
	else
		return -EINVAL;
	return 0;
}

int rtl_request_local_irq(int i, unsigned int (*handler)(struct pt_regs *r),
			  unsigned int cpu)
{
	if ( !(unsigned long)timer_handler )
	{
		timer_handler = handler;
		set_bit(0,&rtl_local[smp_processor_id()].rtirq);
		return 0;
	}
	else
		return -EINVAL;
}