精华内容
下载资源
问答
  • 如图8所示,AAPCS为ARM的R0~R15寄存器做了定义,明确了它们在函数中的职责:图 8 AAPCS关于ARM寄存器的定义一、函数调用时的规则如下:1、 父函数与子函数间的入口参数依次通过R0~R3这4个寄存器传递。父函数在调用子...

    AAPCS对ARM结构的一些标准做了定义,在这里我们只重点介绍函数调用部分,如图8所示,AAPCS为ARM的R0~R15寄存器做了定义,明确了它们在函数中的职责:

    1929c116a1cb3367d95e7f3a69667b73.png

    图 8 AAPCS关于ARM寄存器的定义

    一、函数调用时的规则如下:

    1、 父函数与子函数间的入口参数依次通过R0~R3这4个寄存器传递。父函数在调用子函数前先将参数存入到R0~R3中,若只有一个参数则使用R0传递,2个则使用R0和R1传递,依次类推,当超过4个参数时,其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。

    2、子函数通过R0寄存器将返回值传递给父函数。子函数返回时,将返回值存入R0,当返回到父函数时,父函数读取R0获得返回值。

    3、发生函数调用时,R0~R3是传递参数的寄存器,即使是父函数没有参数需要传递,子函数也可以任意更改R0~R3寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。AAPCS规定,发生函数调用前,由父函数将R0~R3中有用的数据压栈,然后才能调用子函数,以防止父函数R0~R3中的有用数据被子函数破坏。

    4、 R4~R11为普通的通用寄存器,若子函数需要使用这些寄存器,则需要将这些寄存器先压栈然后再使用,以免破坏了这些寄存器中保存的父函数的数值,子函数返回父函数前需要先出栈恢复其数值,然后再返回父函数。AAPCS规定,发生函数调用时,父函数无需对这些寄存器进行压栈处理,若子函数需要使用这些寄存器,则由子函数负责压栈,以防止父函数R4~R11中的数据被破坏。

    5、编译器在编译时就确定了函数间的调用关系,它会使函数间的调用遵守3、4条规定。但编译器无法预知中断函数的调用,被中断的函数无法提前对R0~R3进行压栈处理,因此需要在中断函数里对它所使用的R0~R11压栈。对于中断函数,不遵守第3条规定,遵守第5条规定。

    6、R12寄存器在某些版本的编译器下另有它用,用户程序不能使用,因此我们在编写汇编函数时也必须对它进行压栈处理,确保它的数值不能被破坏。

    7、R13寄存器是堆栈寄存器(SP),用来保存堆栈的当前指针。

    8、R14寄存器是链接寄存器(LR),用来保存函数的返回地址。

    9、R15寄存器是程序寄存器(PC),指向程序当前的地址。

    上述只介绍了本手册中使用到的情形,具体的情况在编写操作系统代码时会涉及到,其它规则请请读者自行查找资料。

    二、例子

    接下来我们再通过几个小例子熟悉一下C函数与汇编函数的调用过程。下面的C函数TestFunc1与汇编函数TestFunc2的功能是一样的。

    U8 TestFunc1(void)

    {

    U8 ucPara1;

    U8 ucPara2;

    U8 ucPara3;

    U8 ucPara4;

    U8 ucPara5;

    U8 ucPara6;ucPara1 = 1;

    ucPara2 = 2;

    ucPara3 = 3;

    ucPara4 = 4;

    ucPara5 = 5;

    ucPara6 = 6;

    return ucPara1 + ucPara2 + ucPara3 + ucPara4 + ucPara5 + ucPara6;12345678

    }

    .func TestFunc2

    TestFunc2:STMDB R13!, {R5 - R6, R10}    @R5,R6,R10寄存器压栈

    LDR R1, =1

    LDR R3, =2

    LDR R4, =3

    LDR R5, =4

    LDR R6, =5

    LDR R10, =6

    ADD R0, R1, R3

    ADD R0, R0, R4

    ADD R0, R0, R5

    ADD R0, R0, R6

    ADD R0, R0, R10

    LDMIA R13!, {R5 - R6, R10}    @R5,R6,R10寄存器出栈

    .endfunc1234567891011121314151617

    TestFunc2函数使用了R0、R1、R3、R4、R5、R6、R10共7个寄存器,遵循AAPCS规则,在使用R0、R1和R3之前并没有对它们压栈,但对R5、R6和R10寄存器进行了压栈保存,在函数返回前又出栈还原了这3个寄存器,这样TestFunc2函数返回到它的父函数之后,R5、R6和R10寄存器的数值是没有改变的,而R0、R1和R3则分别被改写为了1、2和3。

    下面我们再来看看C函数TestFunc3调用汇编函数TestFunc4完成1+2的运算。

    U8 TestFunc3(void)

    {

    return TestFunc4(1, 2);

    }.func TestFunc41

    TestFunc4:ADD R0, R0, R1

    BX R14;

    .endfunc1234

    TestFunc3函数在调用TestFunc4函数前已经将参数1和2分别存入R0和R1,并将返回地址存入到R14中,然后才跳转到TestFunc4函数,发生函数调用。这时程序将运行TestFunc4函数,它将R0和R1相加,将结果放入R0,需要通过R0将返回值返回给TestFunc3函数。此时R14中保存的就是返回TestFunc3函数的返回地址,最后TestFunc4函数跳转到R14就返回到了TestFunc3函数,TestFunc3函数从R0就可以取出TestFunc4函数计算的结果了。

    下面我们再来看看汇编函数TestFunc5调用C函数TestFunc6完成1+2的运算。

    .func TestFunc5

    TestFunc5:MOV R0, #1

    MOV R1, #2

    SUB R13, R13, #4

    STR R14, [R13]

    BL TestFunc6

    LDR R14, [R13]

    ADD R13, R13, #4

    BX R14

    .endfunc12345678910

    U8 TestFunc6(U8 ucPara1, U8 ucpara2)

    {

    return ucPara1 + ucPara2;

    }

    TestFunc5函数先将参数1和2存入R0和R1寄存器,准备调用TestFunc6函数并传递入口参数,然后将R14寄存器压栈,以防止使用BL指令时存入的R14返回地址破坏R14原有的数据,然后调用TestFunc6函数。在调用TestFunc6函数时BL指令会自动将“LDR R14, [R13]”这条指令的地址存入R14,这样就开始运行TestFunc6函数了。TestFunc6函数会自动从R0和R1寄存器中取出参数,将计算结果存入R0,通过R0将返回值返回给TestFunc5函数。TestFunc6函数跳转回TestFunc5函数后,TestFunc5函数从栈中恢复原有的R14寄存器,完成函数调用,此时R0中的数值就是TestFunc6函数的计算结果。

    当函数比较简单,不需要压栈仅使用寄存器便可以完成运算的时候,那么下面的TestFunc7函数,它的返回值是多少?

    U8* TestFunc7(void)

    {

    U8 ucPara1;ucPara1 = 1;

    return &ucPara1;123

    }

    按照上面的分析,对于这个简单的函数,编译器是不会为局部变量ucPara1分配内存空间的,ucPara1只会保存在寄存器中,因此无从谈起它的地址。但这个这么简单的函数却偏偏要获取这个仅在寄存器中的局部变量的地址,遇到这种情况,编译器在编译时会特别为ucPara1专门在栈中分配内存,因此也就可以获取到它的地址了。

    当然,这个函数没有任何意义,仅是举一个例子,而且写C语言时要避免发生这种情况,因为TestFunc7函数返回的是栈内局部变量的地址,当TestFunc7函数运行完后,ucPara1这个局部变量所在的栈空间已经被释放,这个栈空间很可能已经被其它变量占用,如果这时候还使用这个地址的话就可能会导致系统崩溃,新手要避免产生这个错误。

    展开全文
  • 中断系统流程解析:asm_do_IRQ(unsigned int irq, struct pt_regs *regs)handle_IRQ(irq, regs);generic_handle_irq(irq);/*Garmen:进行一般的中断处理*/struct irq_desc *desc = irq_to_desc(irq); /*#define irq_...

    中断系统流程解析:

    asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

    handle_IRQ(irq, regs);

    generic_handle_irq(irq);/*Garmen:进行一般的中断处理*/

    struct irq_desc *desc = irq_to_desc(irq);  /*#define irq_to_desc(irq)    (&irq_desc[irq])      Garmen:他是以irq为下标的一个全局数组项*/

    generic_handle_irq_desc(irq, desc);

    desc->handle_irq(irq, desc);  /*Garmen : 那么究竟是谁调用handle_irq ???  下面进行分析*/

    ---> 问:所以是谁调用handle_irq ?

    答:进行搜索handle_irq然后进入kernel\irq\Chip.c

    __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,

    struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);/*Garmen : 以irq为索引,找到一项*/

    desc->handle_irq = handle;          /*Garmen : 这里进行设置,把上面以irq为索引找到的那项的handle_irq指定为传进来的参数handle*/

    然后我们再搜索__irq_set_handler被谁调用:

    irq_set_handler(unsigned int irq, irq_flow_handler_t handle)

    __irq_set_handler(irq, handle, 0, NULL);

    而且我们在linux-3.4.2\arch\arm\plat-s3c24xx\Irq.c 发现:

    void __init s3c24xx_init_irq(void)     /*Garmen: 其实下面这些就相当于初始化函数*/

    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)

    irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq);     /*Garmen : 再跟进去*/

    /*Garmen:irqno对应的就是上面irq_desc的irq索引

    *然后你会惊人的发现这个handle_edge_irq 就是handle就是上面desc->handle_irq = handle 中断处理函数

    */

    irq_set_chip_and_handler_name(irq, chip,handle, NULL);

    irq_set_chip(irq, chip);

    desc->irq_data.chip = chip;   /*Garmen:chip函数也是一样,有跟进来的irq索引和chip*/

    __irq_set_handler(irq, handle, 0, name);     /*Garmen : 于是我们惊人的发现这个函数被调用了*/

    再研究一下handle_edge_irq

    handle_edge_irq:

    /* Start handling the irq */

    desc->irq_data.chip->irq_ack(&desc->irq_data);

    handle_irq_event(desc);

    struct irqaction *action = desc->action;      /*Garmen : 取出desc中action成员*/

    ret = handle_irq_event_percpu(desc, action);          /*Garemn : 执行action相关函数*/

    小结:问:按下按键之后怎么处理

    答:1、进入异常模式:

    b vector_irq + 偏移值

    2、调用列表,比如调用到irq_usr,保持现场什么的工作

    3、调用asm_do_irq

    4、调用irq_desc[irq]->handle_irq /*Garmen: 以中断号为下标,取出一项处理函数*/

    5、上面的handle_irq 就是一个中断处理函数 , 好比如handle_edge_irq

    里面做了什么事情呢?参考上面handle_edge_irq的研究

    irq_desc结构体定义及其说明:他是一个全局数组

    ①:chip是一些芯片底层硬件相关的操作(比如响应中断、清中断等等),在linux3.4.2内核。他在irq_data这个数组里面,这里没有列出来

    ②:handle_irq是中断处理函数,将aciton链表中的成员一一取出来,然后执行action->handler

    /**

    * struct irq_desc - interrupt descriptor

    * @irq_data:        per irq and chip data passed down to chip functions

    * @timer_rand_state:    pointer to timer rand state struct

    * @kstat_irqs:        irq stats per cpu

    * @handle_irq:        highlevel irq-events handler

    * @preflow_handler:    handler called before the flow handler (currently used by sparc)

    * @action:        the irq action chain

    * @status:        status information

    * @core_internal_state__do_not_mess_with_it: core internal status information

    * @depth:        disable-depth, for nested irq_disable() calls

    * @wake_depth:        enable depth, for multiple irq_set_irq_wake() callers

    * @irq_count:        stats field to detect stalled irqs

    * @last_unhandled:    aging timer for unhandled count

    * @irqs_unhandled:    stats field for spurious unhandled interrupts

    * @lock:        locking for SMP

    * @affinity_hint:    hint to user space for preferred irq affinity

    * @affinity_notify:    context for notification of affinity changes

    * @pending_mask:    pending rebalanced interrupts

    * @threads_oneshot:    bitfield to handle shared oneshot threads

    * @threads_active:    number of irqaction threads currently running

    * @wait_for_threads:    wait queue for sync_irq to wait for threaded handlers

    * @dir:        /proc/irq/ procfs entry

    * @name:        flow handler name for /proc/interrupts output

    */

    struct irq_desc {

    struct irq_data        irq_data;

    struct timer_rand_state *timer_rand_state;

    unsigned int __percpu    *kstat_irqs;

    irq_flow_handler_t    handle_irq;

    #ifdef CONFIG_IRQ_PREFLOW_FASTEOI

    irq_preflow_handler_t    preflow_handler;

    #endif

    struct irqaction    *action;    /* IRQ action list */

    unsigned int        status_use_accessors;

    unsigned int        core_internal_state__do_not_mess_with_it;

    unsigned int        depth;        /* nested irq disables */

    unsigned int        wake_depth;    /* nested wake enables */

    unsigned int        irq_count;    /* For detecting broken IRQs */

    unsigned long        last_unhandled;    /* Aging timer for unhandled count */

    unsigned int        irqs_unhandled;

    raw_spinlock_t        lock;

    struct cpumask        *percpu_enabled;

    #ifdef CONFIG_SMP

    const struct cpumask    *affinity_hint;

    struct irq_affinity_notify *affinity_notify;

    #ifdef CONFIG_GENERIC_PENDING_IRQ

    cpumask_var_t        pending_mask;

    #endif

    #endif

    unsigned long        threads_oneshot;

    atomic_t        threads_active;

    wait_queue_head_t       wait_for_threads;

    #ifdef CONFIG_PROC_FS

    struct proc_dir_entry    *dir;

    #endif

    struct module        *owner;

    const char        *name;

    } ____cacheline_internodealigned_in_smp;

    struct irqaction {

    irq_handler_t        handler;

    unsigned long        flags;

    void            *dev_id;

    void __percpu        *percpu_dev_id;

    struct irqaction    *next;

    int            irq;

    irq_handler_t        thread_fn;

    struct task_struct    *thread;

    unsigned long        thread_flags;

    unsigned long        thread_mask;

    const char        *name;

    struct proc_dir_entry    *dir;

    } ____cacheline_internodealigned_in_smp;

    4a2e2354c3826656ce76cc8c86752508.png

    分析request_irq函数

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

    return request_threaded_irq(irq, handler, NULL, flags, name, dev);

    int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)

    struct irqaction *action;

    struct irq_desc *desc;

    action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);    /*Garmen : 分配设置了action结构*/

    if (!action)

    return -ENOMEM;

    action->handler = handler;

    action->thread_fn = thread_fn;

    action->flags = irqflags;

    action->name = devname;

    action->dev_id = dev_id;

    desc = irq_to_desc(irq);     /*Garmen : 以中断号为下标找到了这个desc全局数组项,将这个desc传递给下面的setup_irq */

    retval = __setup_irq(irq, desc, action);     /*Garmen : 然后调用__setup_irq 设置中断*/

    struct irqaction *old, **old_ptr; /*定义old指针做判断*/

    old_ptr = &desc->action;

    old = *old_ptr

    /*Garmen : 你知道什么是共享中断吗?*/

    ...

    /*Garmen : 对action做一系列判断,比如是否是共享中断啊等等,假如不是,就将我们传进来的action加入aciton链表(具体看上面的图)

    *然后desc->chip->settype       将引脚配置中断引脚,还有那些flag什么进行定义

    *然后desc->->startup/enable    使能引脚

    */

    ...

    插一小部分相关操作

    打开设备: exec 5

    查看设备: ps知道当前设备 -sh是771 然后 ls -l /proc/771/fd

    关闭设备: exec 5

    删除进程:kill -0 PID号

    退出进程:kill -9 PID号

    1、中断申请函数:

    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

    函数原型:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

    第一个参数:unsigned int irq 可例 : IRQ_EINT0

    在Irq.h进行宏定义(include\asm-arm)

    在Irq.c 的s3c24xx_init_irq函数中有进行初始化;

    中断号是去Irq.h 将该宏展开得到的一个数字

    第二个参数:irq_handler_t handler 可例 : buttons_irq 是一个处理函数

    第三个参数:unsigned long irqflags 可例:IRQT_BOTHEDGE 双边缘触发

    在Irq.h进行宏定义

    在Irq.c 的s3c_irqext_type中进行初始化

    第四个参数:随意

    第五个参数:void *dev_id

    它是void型的指针。所以不必顾虑太多;

    它只不过是用来在free_irq卸载时候,与fre_irq的参数结合在一起,确定卸载哪一个irq_action 结构

    小结:当发生IRQ_EINT0这个中断的时候,就去调用这个buttons_irq中断处理函数,

    这个函数static irqreturn_t buttons_irq(int irq, void *dev_id),有两个参数

    第一个参数是中断号等于IRQ_EINT0 ,第二个参数是dev_id 等于pins_desc[0]

    敲黑板:

    后面引入这样的一个方法:将irq、*name、pin、key_val写成一个结构体dev_id

    例如

    定义一个pin_desc结构体原型:

    struct pin_desc{

    int irq;

    char *name;

    unsigned int pin;

    unsigned int key_val;

    };

    编写我们自己想要的结构体:

    struct pin_desc pins_desc[4] = {

    {IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},

    {IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},

    {IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},

    {IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},

    };

    我们的注册函数就可以这么写了:

    for (i = 0; i < 4; i++)

    {

    request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);

    }

    里面所做的事情:

    ①:分配一个irqaction

    ②:把这个结构体放入irq_des[irq] <==action链表

    ③:设置引脚

    ④:中断使能

    2、中断清除函数

    free_irq(irq, dev_id)

    函数原型:void free_irq(unsigned int irq, void *dev_id)

    里面所做的事情

    ①:出链

    ②:禁止中断

    3、系统函数:s3c2410_gpio_getpin

    如何使用:

    直接拿GPIO口来判断就行!

    if (s3c2410_gpio_getpin (s3c2410_GPG10) == 0)

    对寄存器的读取和判断

    4、休眠函数!

    休眠函数:

    函数原型:

    #define wait_event_interruptible(wq, condition)                \

    ({                                    \

    int __ret = 0;                            \

    if (!(condition))                        \

    __wait_event_interruptible(wq, condition, __ret);    \

    __ret;                                \

    })

    里面所做的事情:

    先判断condition,假如condition为0的话,就调用__wait_event_interruptible(wq, condition, __ret);  让应用程序进行休眠;

    假如!condition,不等于0的话,函数就跳过wait_event_interruptible,向下运行__wait_event_interruptible了!!!

    所以我们运用该休眠函数的时候,要在wait_event_interruptible 后面,将condition标志位置1;

    例函:

    /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */

    wait_event_interruptible(button_waitq, ev_press);

    还要定义static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    意思是:定义一个等待队列头结构体;

    唤醒函数:

    ev_press = 1;               Garmen: 表示中断发生了

    wake_up_interruptible(&button_waitq);   Garmen::唤醒休眠的进程

    参考驱动函数:

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    static struct class *thirddrv_class;

    static struct class_device    *thirddrv_class_dev;

    volatile unsigned long *gpfcon;

    volatile unsigned long *gpfdat;

    volatile unsigned long *gpgcon;

    volatile unsigned long *gpgdat;

    /*Garmen : 中断队列头*/

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

    /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */

    static volatile int ev_press = 0;

    struct pin_desc{

    unsigned int pin;

    unsigned int key_val;

    };

    /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */

    /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */

    static unsigned char key_val;

    struct pin_desc pins_desc[4] = {

    {S3C2410_GPF0, 0x01},

    {S3C2410_GPF2, 0x02},

    {S3C2410_GPG3, 0x03},

    {S3C2410_GPG11, 0x04},

    };

    /*

    * 确定按键值

    */

    static irqreturn_t buttons_irq(int irq, void *dev_id)

    {

    struct pin_desc * pindesc = (struct pin_desc *)dev_id;

    unsigned int pinval;

    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)

    {

    /* 松开 */

    key_val = 0x80 | pindesc->key_val;

    }

    else

    {

    /* 按下 */

    key_val = pindesc->key_val;

    }

    ev_press = 1;                  /* 表示中断发生了 */

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

    return IRQ_RETVAL(IRQ_HANDLED);

    }

    static int third_drv_open(struct inode *inode, struct file *file)

    {

    /* 配置GPF0,2为输入引脚 */

    /* 配置GPG3,11为输入引脚 */

    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

    request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);

    request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);

    request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);

    return 0;

    }

    ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

    {

    if (size != 1)

    return -EINVAL;

    /* 如果没有按键动作, 休眠 */

    wait_event_interruptible(button_waitq, ev_press);

    /* 如果有按键动作, 返回键值 */

    copy_to_user(buf, &key_val, 1);

    /*Garmen : 同时将休眠标志位至0 这样又可以继续休眠*/

    ev_press = 0;

    return 1;

    }

    int third_drv_close(struct inode *inode, struct file *file)

    {

    free_irq(IRQ_EINT0, &pins_desc[0]);

    free_irq(IRQ_EINT2, &pins_desc[1]);

    free_irq(IRQ_EINT11, &pins_desc[2]);

    free_irq(IRQ_EINT19, &pins_desc[3]);

    return 0;

    }

    static struct file_operations sencod_drv_fops = {

    .owner    =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open     =  third_drv_open,

    .read     =    third_drv_read,

    .release  =  third_drv_close,

    };

    int major;

    static int third_drv_init(void)

    {

    major = register_chrdev(0, "third_drv", &sencod_drv_fops);

    thirddrv_class = class_create(THIS_MODULE, "third_drv");

    thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);

    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);

    gpgdat = gpgcon + 1;

    return 0;

    }

    static void third_drv_exit(void)

    {

    unregister_chrdev(major, "third_drv");

    class_device_unregister(thirddrv_class_dev);

    class_destroy(thirddrv_class);

    iounmap(gpfcon);

    iounmap(gpgcon);

    return 0;

    }

    module_init(third_drv_init);

    module_exit(third_drv_exit);

    MODULE_LICENSE("GPL");

    在测试程序中调用:

    #include

    #include

    #include

    #include

    /* thirddrvtest

    */

    int main(int argc, char **argv)

    {

    int fd;

    unsigned char key_val;

    fd = open("/dev/buttons", O_RDWR);

    if (fd < 0)

    {

    printf("can't open!\n");

    }

    while (1)

    {

    read(fd, &key_val, 1);

    printf("key_val = 0x%x\n", key_val);

    }

    return 0;

    }

    展开全文
  • linux和macos的汇编中通过 int 80h 软中断来调用系统函数,但是给函数传入的参数方法不同。以下说明32位程序的调用方式。Linux对于linux,如果我要调用如下的C语言函数:sys_write(unsigned int fd, const char * buf...

    linux和macos的汇编中通过 int 80h 软中断来调用系统函数,但是给函数传入的参数方法不同。以下说明32位程序的调用方式。

    Linux

    对于linux,如果我要调用如下的C语言函数:

    sys_write(unsigned int fd, const char * buf, size_t count);

    函数参数和寄存器对应关系为:

    ebx: fd

    ecx: buf

    edx: count

    所以汇编中调用sys_write这个函数来显示一个字符串对应的汇编如下:

    section .data

    msg db 'Hello'

    section .text

    __display:

    mov edx, 5 ;字符串长度, 对应count参数

    mov cdx, msg ;字符串所在地址, 对应buf参数

    mov ebx, 1 ;对应fd参数,fd为1时表示stdout, 显示屏

    mov eax, 4 ;对应sys_write在linux系统中调用好为4

    int 80h

    macOS

    在macos中,C语言函数的参数不使用寄存器来存放,而是使用stack来存放,参数顺序倒序入栈,总共支持16字节。比如同样的显示字符串功能,在macos中的C函数为:

    write(int fd, user_addr_t cbuf, user_size_t nbyte);

    因此,函数参数入栈顺序为:

    push nbyte

    push cbuf

    push fd

    sub esp 4 ;参数总共要占用16字节,因为这个函数需要3个参数,所以实际占用了 3*4=12个字节,为了填满16字节,这里将stack地址再减去4个字节(stack在内存中由高到底存放,所以是减去)

    所以汇编中调用write这个函数来显示一个字符串对应的汇编如下:

    section .data

    msg db 'Hello'

    section .text

    __display:

    push dword 5 ;字符串长度, 对应nbyte参数

    push dword msg ;字符串所在地址, 对应cbuf参数

    push dword 1 ;对应fd参数,fd为1时表示stdout, 显示屏

    sub esp, 4 ;填满16字节

    mov eax, 4 ;对应sys_write在macos系统中调用好为4

    int 80h

    无论在linux还是macos中,函数的返回值都最后保存在eax寄存器中。

    参考:

    展开全文
  • 说明: ...  接下来要进行函数参数传递的调试。   参数传递方法根据架构、语言、编译器的不同而不同。 参数的存储位置   整型和指针型的参数会从左至右依次保存到rdi、rsi、rdx、rcx、r8、r9

    说明
      本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
      QQ 群 号:513683159 【相互学习】
    内容来源
      《Debug Hack 中文版》#10
      
    实验环境
      ubuntu16.04,64位

    内容

      接下来要进行函数参数传递的调试。
      参数传递方法根据架构、语言、编译器的不同而不同。
    参数的存储位置
      整型和指针型的参数会从左至右依次保存到rdi、rsi、rdx、rcx、r8、r9中。
      浮点型参数会保存到xmm0、xmm1……中。
      多于这些寄存器的参数会被保存到栈上。
    因此,利用GDB在希望确认的函数开头中断之后,查看寄存器或栈即可获得参数内容。
    函数前加星号(*)与不加的区别?
      *:断点会设置到汇编语言层次的函数开头、
      不加*:断点会设置到地址偏后一点的源代码级别的开头。
    由于参数也可能保存到栈上,若在break命令中不加*直接使用函数名,就无法用于参数确认。
      

    实践

      ①创建源文件vim func_call.c内容如下;

    #include<stdio.h>
    #include<stdlib.h>
    
    int v1 = 1;
    float v2 = 0.01;
    
    void func(int a,long b,short c,char d,long long e,
              float f,double g,int *h,float *i,char *j)
    {
            printf("a:%d,b:%ld,c:%d,d:%c,e:%lld\n"
                   "f:%.3e,g:%.3e\n"
                   "h:%p,i:%p,j:%p\n",a,b,c,d,e,f,g,h,i,j);
    }
    
    int main(void)
    {
            func(100,35000L,5,'A',123456789LL,3.14,2.99792458e8,&v1,&v2,"string");
            return EXIT_SUCCESS;
    }
    

      ②编译源程序gcc func_call.c -o func -g-g包含调试信息。
      ③执行可执行程序./func,内容如下:

    a:100,b:35000,c:5,d:A,e:123456789
    f:3.140e+00,g:2.998e+08
    h:0x601038,i:0x60103c,j:0x4006b2
    

      ④启动gdb调试gdb func,如下表示成功:

    ...
    Reading symbols from func...done.
    

      ⑤设置断点b func.(b = break)在该函数设置断点

    Breakpoint 1 at 0x400550: file func_call.c, line 10.
    

      ⑥查看断点信息i b(i = info ,b = break)

    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x0000000000400550 in func at func_call.c:10
    	breakpoint already hit 1 time
    

      ⑦运行程序r(r =run)

    Starting program: /home/xsndz/Desktop/func 
    
    Breakpoint 1, func (a=100, b=35000, c=5, d=65 'A', e=123456789, f=3.1400001, g=299792458, h=0x601038 <v1>, i=0x60103c <v2>, j=0x4006b2 "string") at func_call.c:10
    10		printf("a:%d,b:%ld,c:%d,d:%c,e:%lld\n"
    

      ⑧继续执行程序c(c = continue)

    Continuing.
    a:100,b:35000,c:5,d:A,e:123456789
    f:3.140e+00,g:2.998e+08
    h:0x601038,i:0x60103c,j:0x4006b2
    [Inferior 1 (process 2610) exited normally]
    

      ⑨删除所有断点:d break(d = delete),y

    Delete all breakpoints? (y or n) y
    

      ①确认是否删除i b(i = info ,b = break)

    No breakpoints or watchpoints.
    

      ②设置断点b func(b = break)在该函数设置断点

    Breakpoint 2 at 0x400526: file func_call.c, line 9.
    

      ③查看断点i b(i = info ,b = break)

    Num     Type           Disp Enb Address            What
    2       breakpoint     keep y   0x0000000000400526 in func at func_call.c:9
    

      ④运行程序r(r =run)

    Starting program: /home/xsndz/Desktop/func 
    Breakpoint 2, func (a=0, b=140737488346510, c=0, d=0 '\000', e=1, f=5.87970963e-39, g=7.7939996923098536e-317, h=0x7ffff7ffe168, i=0x60103c <v2>, j=0x4006b2 "string") at func_call.c:9
    9	{
    

      ⑤查看一下func的汇编程序disas func

    Dump of assembler code for function func:
       0x0000000000400526 <+0>:	push   %rbp
       0x0000000000400527 <+1>:	mov    %rsp,%rbp
       0x000000000040052a <+4>:	sub    $0x30,%rsp
       ...
    

      该三句完成开辟栈空间设置为一个栈帧
      ⑥查看参数:a、b、c、d、e(保存在寄存器中),故查看一下所有寄存器:i r(i = info ,r = registers)

    rax            0x400598	4195736
    rbx            0x0	0
    rcx            0x41	65										//参数d
    rdx            0x5	5										//参数c
    rsi            0x88b8	35000								//参数b
    rdi            0x64	100										//参数a
    rbp            0x7fffffffdd90	0x7fffffffdd90
    rsp            0x7fffffffdd78	0x7fffffffdd78
    r8             0x75bcd15	123456789						//参数e
    r9             0x601038	6295608								//参数h
    r10            0x846	2118
    r11            0x7ffff7a2d750	140737348032336
    r12            0x400430	4195376
    r13            0x7fffffffde70	140737488346736
    r14            0x0	0
    r15            0x0	0
    rip            0x400526	0x400526 <func>
    eflags         0x246	[ PF ZF IF ]
    cs             0x33	51
    ss             0x2b	43
    ds             0x0	0
    es             0x0	0
    fs             0x0	0
    gs             0x0	0
    

      从上可看出:参数:a、b、c、d、e分别保存在rdi 、rsi、rdx 、rcx 、 r8
      ⑦查看参数f、g(分别保存在xmm0、xmm1寄存器) 的数值:
      p $xmm0.v4_float[0]

    $1 = 3.1400001
    

      p $xmm1.v2_double[0]

    $2 = 299792458
    

      为什么xmm0/xmm1还需加上后缀?这是因为GDB将这些寄存器看作下面的联合:

    union {
    float		v4_float[4];
    double		v2_double[2];
    int8_t		v16_int8[16];
    int16_t		v8_int16[8];
    int32_t		v4_int32[8];
    int64_t		v2_int64[8];
    int128_t	unit128;
    }xmm0;
    
    

      ⑧查看参数h、i、j【h处理方法与整形相同,保存到r9中[在上面就可看到参数h],由于i、j也为指针,参数少会存在寄存器中,但多了则不够用,这两个则存入栈中进行传递】
      查看内存栈空间中内容:x /3g $rsp

    0x7fffffffdd78:	4195807	6295612
    0x7fffffffdd88:	4196018
    

      g=giant word双字【整数和指针等带下均为g】,3代表要数量,即显示3个参数,但不是仅想要查看i、j,这是因为栈开头保存了函数的返回地址。简易图示如下:
    在这里插入图片描述
      若想确认它们指向的值或字符串可进行如下操作:
      1.printf"%.2f\n",*(float*)6295612printf"%.2f\n",*(float*)(*(unsigned long*)($rsp+0x8))

    0.01
    

      2.p(char*)4196018p (char*)(*(unsigned long*)($rsp+0x10))

    $3 = 0x4006b2 "string"
    

      ⑨继续执行程序c(c = continue)

    Continuing.
    a:100,b:35000,c:5,d:A,e:123456789
    f:3.140e+00,g:2.998e+08
    h:0x601038,i:0x60103c,j:0x4006b2
    [Inferior 1 (process 2614) exited normally]
    

    i386环境

      看书
      原则上参数全部放在栈中。
      i3876中寄存器调用,即:
        也可放在将部分参数放在寄存器中进行函数调用。这种方式称为:fastcall(快速调用)。
      gcc在函数声明中添加__attribute_((regparm(3))),即可进行这种调用。这样即可使用eax、edx、ecx传递开头的是哪个参数

    C++篇

      看书

    展开全文
  • 1.定义一个空的指针函数 指针函数参数是uint8_t 类型chtypedef void (* usart_recv_callback)(uint8_t ch);2.声明这个类型usart_recv_callback usart1_recv_cb;3.串口配置时,一个形参为串口中断接收回调void ...
  • 嵌入式LINUX驱动开发(中断处理函数)2020年08月11日|萬仟网网络运营 |我要评论嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一、函数、头文件及说明二、编译举例:一、函数、头文件及说明//头文件位置:include/...
  • ZYNQ 定时器中断函数

    2021-04-25 00:42:56
    一,中断底层原理 部分 PL 到 PS 部分的中断,经过中断控制分配器(ICD),同时进入CPU1 和 CPU0。查询下面表格,可以看到 PL 到 PS 部分一共有 20 个中断可以使用。4 个快速中断(PPI),即IRQF2P[19:16];16 个...
  • 按清除参数的方式分类3.Linux平台下常见的函数调用约定3.1.Linux下x86平台3.2.Linux下x86-64平台4.Win平台下常见的函数调用约定4.1.Win下x86平台4.1.1.cdecl4.1.2.stdcall常见的x86浮点数指令:4.1.3.fastcall**1问...
  • 两者的根本区别主要表现在服务时间与服务对象一样上。 首先,调用子程序过程发生的时间是已知和固定的,即在主程序中的调用指令(CALL)执行时发生主程序调用子程序,调用指令所在位置是已知和固定的。而中断过程...
  • 芯片设计固化了产生哪些中断,那么每个中断对应的处理地址(这个可以由用户自己指定,一般放在代码的最前面,意思是该中断一旦产生,程序无条件pc指针直接跳转到该处理地址执行,那么就需要解决一个问题,如何跳转...
  • 接下来的任务是实现在C语言中调用Python函数。...这里面要用到的一个特殊的方法是mp_sched_schedule,提供个C调用Python的方法,但是此方法目前好像只能传一个参数过去,还没来得及传入多个参数用那个方法。废话多...
  • 模板参数传递的区别(针对于函数模板的自动类型推导) 按值传递 按值传递的后果是:导致函数模板的自动类型推导退化(decay),何为退化,下面就让我仔细的说一下: ① 按值传递会导致传入参数失去const和volatile...
  • request_irq的作用是...request_irq()函数的原型如下:/* kernel/irq/manage.c */intrequest_irq(unsignedintirq,irqreturn_t(*handler)(int,void*,structpt_regs*),unsignedlongirqflags,constchar*devname,void*...
  • c51编译器支持在c源程序中直接开发中断...他是函数定义式的一个选项,加上这个选项即可以将一个函数定义成中断服务函数。定义中断服务函数的语法格式如下:返回值类型函数名()interrupt m [using n]{函数体}关键词...
  • 函数是一个命名了的代码块,通过调用相应的函数来执行相应的代码,函数可以有0或多个参数,通常会产生一个结果,也可以重载函数,即同一个名字可以对应几个不同的函数函数基础 函数包括: 返回类型,函数名字...
  • linux中断相关函数

    2021-05-15 12:17:41
    #include int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags... request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
  • C51编译器支持在C源程序中直接开发中断程序,从而...他是函数定义式的一个选项,加上这个选项即可以将一个函数定义成中断服务函数。定义中断服务函数的语法格式如下:返回值类型 函数名( )interrupt m [using n]{函...
  • LINUX系统调用原理-既应用层如何调用内核层函数之软件中断SWI:software interrupt 软件中断ARMLinux系统利用SWI指令来从用户空间进入内核空间,还是先让我们了解下这个SWI指令吧。SWI指令用于产生软件中断,从而...
  • 一、 函数参数传递机制的基本理论  函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递机制有两种:值传递和引用传递。以下讨论称调用其他函数的...
  • ZYNQ PL触发PS中断函数

    2021-04-24 09:03:56
    部分 PL 到 PS 部分的中断,经过中断控制分配器(ICD),同时进入CPU1和CPU0。查询下面表格,可以看到PL到PS部分一共有20个中断可以使用。4 个快速中断(PPI),即IRQF2P[19:16]; 16个共享中断(SPI),即 IRQF2P[7:0],...
  • GPIO及中断API函数

    2021-01-14 16:15:41
    获得并占有 GPIO port 的使用权,由参数 gpio 指定具体 port,非空的lables指针有助于诊断。主要是告诉内核这地址被占用了。当其它地方调用同一地址的gpio_request就会报告错误,该地址已被申请。在/proc/mem应该会...
  • 使用Scanner作为构造函数参数的Java总之,我在主要方法(Scanner stdin = new Scanner(System.in);是行)中使用Stdin制作扫描仪,从程序运行时指定的txt中读取数据。这种扫描仪按预期工作为主,不过,我需要用它在...
  • 从逻辑上讲,栈帧就是一个函数执行的环境:函数调用框架、函数参数函数的局部变量、函数执行完后返回到哪里等等。栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所...
  • 使和屏蔽中断: CPU一般都具有屏蔽中断和打开中断的功能,这项功能可以保证正在执行的内核执行路径中断处理程序所抢占,防止某些竞态的发生。 屏蔽中断的使用方法: local_irq_disable() /* 屏蔽中断 */ ....
  • 中断函数中断服务函数是编写单片机应用程序可缺少的。中断服务函数只有在中断源请求响应中断时才会被执行,这在处理突发事件和实时控制是十分有效的。例如:电路中一个按钮, 要求按钮后 LED 点亮,这个按钮何时会...
  • 这里我们不再赘述系统调用的基本原理以及系统调用产生时的函数调用以及系统调用表,直接看arm中的软中断产生及返回时如何保存寄存器上下文、如何陷入内核再返回、如何传参,模式切换做了哪些处理。 这里mark一下: ...
  • handler是向系统登记的中断处理函数,是一个回调函数中断发生时,系统调用这个函数,dev参数将被传递; flags是中断处理的属性,若设置SA_INTERRUPT,标明中断处理程序是快速处理程序,快速处理程序被调用时屏蔽...
  • (2)函数调用是在程序运行时处理,为形参分配临时的内存单元,可以有返回值:而宏展开是在编译前进行的,展开时并为形参分配内存单元,进行值的传递,没有返回值的概念 (3)调用函数最多得到一个返回值,而用宏定义可以...
  • 设备树中的中断节点以及相关函数 设备树中的中断节点。 ​ 如果一个设备需要用到中断功能,开发人员就需要在设备树中配置好中断属性信息,因为设备树是用来描述硬件信息的,然后Linux内核通过设备树配置的中断属性...
  • 开门见山,先看一段简单代码,从这个简单代码着手,我们一步一步解开C语言函数调用与参数传递的谜团…(鉴于本人也在不断探索学习,如有错误,愿虚心请教,相互学习) Add(int x, int y) { int z = 0; z = x + y;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 115,980
精华内容 46,392
关键字:

中断函数不能传递参数