- ERROR! at line 4: [E0004] Immediate cannot be greater than 16-bits
MOV r0, #0x4020FFCC
Assembler code:
.global _relocateVectorBaseRAM
_relocateVectorBaseRAM:
; set Vector base to the RAM exception table
MOV r0, #0x4020FFCC
MCR p15, #0, r0, c12, c0, #0
BX lr - What's the correct adress to relocate the exception vector for the OMAP35? Is 0x4020FFCC correct?
u cannot give "MOV r0, #0x4020FFCC" as single instr as it will be greater than 32 bits.
ARM has fixed instruction size. max size is 32 bits.
You need to use barrel shifter to input a 32bit value into the r0.
for eg:
mov r5, #0x40
mov r0, r5, lsl#24 ; we left *** r5 by 24 bits, then save that value into r0
mov r5, #0x20 ; r5 = 0x20
mov r5, r5, lsl#16 ; we left shift r5 by 15bits and save the value into r5 itself
orr r0, r0, r5 ; logical OR r0 and r5 and save the result in r0.
r0 will now contain 0x40200000
linx中断之中断上半部处理分析:
asm_do_IRQ函数
/*
* do_IRQ handles all hardware IRQ's. Decoded IRQs should not
* come via this function. Instead, they should provide their
* own 'handler'
*/
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter(); //设置一些中断的变量等,如 preempt_count++;
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= NR_IRQS)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq); //中断处理
}
/* AT91 specific workaround */
irq_finish(irq);
irq_exit(); //中断退出,设置退出标志。另外,这个函数会调用 软中断。
set_irq_regs(old_regs); //恢复reg
}
generic_handle_irq函数
/*
* Architectures call this to let the generic IRQ layer
* handle an interrupt. If the descriptor is attached to an
* irqchip-style controller then we call the ->handle_irq() handler,
* and it calls __do_IRQ() if it's attached to an irqtype-style controller.
*/
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq, desc);
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc); //调用
else
__do_IRQ(irq);
#endif
}
调用desc->handle_irq函数。
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret;
raw_spin_lock(&desc->lock);
mask_ack_irq(desc, irq);
if (unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
/*
* If its disabled or no action available
* keep it masked and get out of here
*/
action = desc->action;
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock;
desc->status |= IRQ_INPROGRESS;
raw_spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
raw_spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS;
if (unlikely(desc->status & IRQ_ONESHOT))
desc->status |= IRQ_MASKED;
else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
desc->chip->unmask(irq);
out_unlock:
raw_spin_unlock(&desc->lock);
}
这个函数调用handle_IRQ_event函数,来调用action。 最后实现action的调用。另外:由于 有中断共享现象,所以可能会有好几个中断共享一个中断,则action指针指向一个list,list中存放所有的中断 处理函数。当发生中断后,会循环调用所有的这个list中的处理函数。
linx中断之中断下半部处理分析:
linux中断下半部分即是软中断,这里以http://bbs.chinaunix.net/thread-2333484-1-1.html文章(作者 九贱)做参考。
open_softirq向内核注册一个软中断,其实质是设置软中断向量表相应槽位,注册其处理函数:
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
每一个软中断对应一个softirq_action的值。
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
/* softirq mask and active fields moved to irq_cpustat_t in
* asm/hardirq.h to get better cache usage. KAO
*/
struct softirq_action
{
void (*action)(struct softirq_action *);
};
所有的软中断在linux中 有个位图来表示。当这个软中断 enable的时候,将对应的于该中断的相应bit置1.当disable的时候,则清0。
操作软中断的响应的操作:
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)
置1相应的software irq。
#define local_softirq_pending() \
__IRQ_STAT(smp_processor_id(), __softirq_pending)
读出整个位图。
raise_softirq:
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags); //先disable中断
raise_softirq_irqoff(nr); //主要这个函数来激活软中断
local_irq_restore(flags); //restore interrupt
}
raise_softirq函数用来enable and wake up software interrupt。
raise_softirq_irqoff:
/*
* This function must run with irqs disabled! //这就是为什么在调用该函数的时候要disable interrupt
*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*///检测现在是否在中断中,如果在的话就返回。如果不在则唤醒ksoftirqd,ksoftirqd是一个内核线程。 他会调用do_softirq函数来执行软中断。
if (!in_interrupt()) //in_interrupt函数会调用preempt_count()这个函数,他会检测记录中断状态的字段,来确认是否是中断中。
wakeup_softirqd();
}
raise_softirq_irqoff什么时候被调用呢?
A、当do_IRQ完成了I/O中断时调用irq_exit:
#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
# define invoke_softirq() __do_softirq()
#else
# define invoke_softirq() do_softirq()
#endif
void irq_exit(void)
{
account_system_vtime(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq(); //调用软中断
B、如果系统使用I/O APIC,在处理完本地时钟中断时:
void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
{
……
irq_exit();
……
}
C、local_bh_enable
local_bh_enable就是打开下半部,当然重中之中就是软中断了:
void local_bh_enable(void)
{
_local_bh_enable_ip((unsigned long)__builtin_return_address(0));
}
static inline void _local_bh_enable_ip(unsigned long ip)
{
……
if (unlikely(!in_interrupt() && local_softirq_pending()))
do_softirq();
……
}
D、在SMP中,当CPU处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时:
唔,对多核中CPU的之间的通信不熟,不太清楚这个机制…… .
do_irq函数就不分析,没什么鸟用。
tasklet机制
另外:在linux系统中,其实用 software riq的中断是很少的,这里只是分析了software irq,很多下半部用的是tasklet机制,tasklet是一个基于软中断上的机制,他将软中断封装,使更加容易使用等。
tasklet机制可以参照http://blog.csdn.net/sailor_8318/archive/2008/07/13/2645186.aspx学习。
所有评论(0)