1.请写出汇编指令要ldr的三种功能。
1)把立即数放入rd寄存器 。注意立即数没限制, ldr不能用于寄存器给寄存器赋值。
2)ldr rd, [rm, rn/#立即数] //把rm寄存器上值加上rn值的结果作为地址,再把地址上值放入rd 。
2.请说明要lr,sp,PC,cpsr的寄存器的作用
R13: 别名sp, 此寄存器用于装载栈顶的地址
R14: 别名lr, 此寄存器用于装载返回地址
R15: 别名pc, 此寄存器用于装载当前取指令的内存地址 (老是忘记。拿你的pc)
cpsr: 当前程序状态寄存器, 记录当前工作模式,条件位状态等
spsr: 备份的程序状态寄存器, 用于保存cpsr的值
3.请说明在arm程序里,返回值放在哪个寄存器,函数参数是怎么传递?
调用函数时,传的参数从r0寄存器开始存放第1个参数, 直到r3寄存器存放第4个参数. 如还有参数则放入栈里.
被调用的函数会自动从栈里取超出的参数.
4.请说明printf函数体内参数的值是怎么样取出来的
1)printf函数的第一个参数是一个字符串的地址,通过遍历字符串里的’%’字符,可以得知后面还带有多少个参数。
2)参数是从r0寄存器开始传参数,直到r3寄存器存放要传递的第四个参数,再多的参数就需压栈了。
3)函数的参数也是局部变量在栈里分配空间。全部参数的地址都是连续的,只要获取其中一个参数的地址,即可通过偏移获取其它所有参数的值
5.
6.请说明栈溢出攻击实现的原理
溢出攻击实现原理:
数组的越界访问
如上面的代码, buf数组足够大, 接收用户输入时如果不判断长度, 就可以把要执行的指令存入buf数组, 最后通过数组的越界访问,把栈里存放的原lr寄存器的内容改成buf数组的首地址, 那么buf数组里存放的指令就可以执行了.
而已当main函数是管理员执行时, 那么在buf数组里存放的指令执行就具有管理员的权限了, 手机的root用的就是这种方法
防止溢出攻击的方法:
1). 接用户输时判断数据长度, 防止数组越界访问
2). 尽量在堆里分配空间
3). gcc 编译时加上-fstack-protection选项(man gcc里查看
7.请说明b跳转与ldr跳转指令的区别,以及在什么场合使用?
8.请说明在uboot上加入自定义命令的步骤以及具体实现的原理?
具体做法:
0.只要实现一个功能函数,再调用 U_BOOT_CMD宏即可.放在cmd目录.
1.uboot里命令统一放在源码目录里的cmd目录.每个文件表示一类或一个命令.
2.修改cmd目录里的Makefile, 在第16行:
obj-y += cmd_mytest.o
3.回到orangepi_sdk目录下:
make uboot
make install_uboot sdcard=/dev/sdb
3.重新启动后,在uboot上输入 "help"命令:
可以查看到:
mytest - mytest - usage of mytest
4. 执行命令:
5.在uboot里每个命令都有一个功能函数,我们可以在自己实现的命令函数里调用其它命令的功能函数来实现相应的功能.
9.当前目录下有文件hello.c aa.c bb.c start.S,要求写一个makefile,最终生成test.bin
///
CROSS_COMPILE ?= arm-linux-gnueabihf-
OBJS += main.o
OBJS += a.o
OBJS += b.o
TARGET ?= test
$(TARGET).bin : $(TARGET)
$(CROSS_COMPILE)objcopy -O binary $< $@
$(TARGET) : $(OBJS)
$(CROSS_COMPILE)ld $^ -o $@
%.o : %.S
$(CROSS_COMPILE)gcc $< -c -o $@
%.o : %.c
$(CROSS_COMPILE)gcc $< -c -o $@
10.请说明MMU的工作原理
管理地址单元,解决多个程序链接同一个地址。
⑴.MMU的使用
MMU是存储器管理单元的缩写,是用来管理虚拟内存系统的器件。 MMU通常是CPU的一部分,本身有少量存储空间存放从虚拟地址到物理地址的匹配表。此表称作TLB(转换旁置缓冲区)。所有数据请求都送往MMU,由 MMU决定数据是在RAM内还是在大容量存储器设备内。如果数据不在存储空间内,MMU将产生页面错误中断。
MMU的两个主要功能是:
1.将虚地址转换成物理地址。
2.控制存储器存取允许。MMU关掉时,虚地址直接输出到物理地址总线。
有MMU单元是ARM芯片和单片机的重要区别之一。
1、将虚拟地址转化为物理地址
2、访问权限管理
11.请描arm的异常向量表及每种异常是在什么情况下发生的?
12.请描述控制器的用途及时钟信号的用途
13.请描述按键的中断事件的流程,怎么最终调用中断的处理函数
按键按下->产生IO口中断->进入中断处理函数halKeyPort1Isr->判断中断位标志?
14.请说明编译uboot源码前:make sp56818-drone_config这具体做了什么
15.请描述裁剪uboot的方法
16.请描述uboot环境变量(bootargs.bootcmd) 在作用
bootcmd // 倒计时到0后,自动执行里面的语句
bootargs // 是用于提供给内核的启动参数语句
如果想要开发板开机后自动引导内核, 可以修改bootcmd环境变量来完成
setenv bootcmd "fatload mmc 0:1 0x43000000 /script.bin; fatload mmc 0:1 0x42000000 /uimage; bootm 0x42000000"
17.请描述uboot启动,过程越详细越好
uboot启动?下面不对。
总结下uboot的配置过程:
1). script/orangepi.conf里的环境变量生效, 及进入uboot源码目录
2). make distclean
3). make orangepi_linux_defconfig
4). make menuconfig
总结下编译主要过程:
1).进入uboot源码目录
2). make -j8
3). 生成boot.src环境变量文件
4). 生成script.bin文件
18.请描述设备驱动的作业及用户程序之间的关系
1). 驱动是硬件与用户进程之间的通信桥梁
用户进程是不可以直接访问硬件的.
如:hexdump /dev/input/event4 //这样,程序hexdump从设备文件里接收数据并按十六进制打印出来
数据是驱动先接收到硬件反馈的数据处理后再移交给用户进程
驱动不属于任何一个用户进程, 可以给多个用户进程调用.
驱动是常驻于内存里,等待用户进程调用.
2). 用户进程如访问越界则会发生段错误,但对其它进程没有影响. 用户进程在cpu的usr模式下工作 ?
驱动是在svc模式下工作(特权模式), 驱动发生错误,会影响整个系统(像windows系统蓝屏).
写应用程序有bug仅仅是影响该进程,但写驱动时,哈哈…
3). 驱动加载到内核后(即驱动工作), 除了初始化工作以外,一般情况下是不会自动去执行操作的, 驱动是被用户进程调用才会触发操作的. 所以也得注意驱动的接口函数的可重入性 。
也就是驱动是实现硬件的各种功能,但怎么去使用由用户进程调用来决定。
驱动本身是不休眠,只能是调用的进程或线程才可以休眠.
4)./dev”目录里提供一个设备文件, 然后用户进程就可以通过操作设备文件来调用驱动.
5)设备驱动实现好功能后,基本上由用户进程通过系统调用后进来调用的.
19.请描述linux内核怎样操作io,列出具体的函数并说明用途
1.配置寄存器的地址.(麻烦,移植性差,落后的做法)void *ioremap(cookie,size),
ioread32(地址)/readl() //读32位值
iowrite32(值, 地址) / writel //把32位的值写到指定的地址上
2.linux内核里有标准的GPIO操作方法。驱动人员可以用内核提供的gpio标准操作函数通过gpiolib来调用控制芯片的io口.
#include //里面声明io口的操作函数
int gpio_request(unsigned gpio, const char *label);//每个io只能被请求一次,可防止多个驱动来控制同一个IO口
void gpio_free(unsigned gpio); //释放已请求的io口
int gpio_direction_input(unsigned gpio); //把指定的IO口作输入功能, gpio用于指定具体哪个io口
int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平
int gpio_get_value(unsigned gpio); //获取指定IO口的电平
void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)
int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)
20.请描述,当我们把一个写的设备驱动加入内核代码里徐一起配置编译时的具体实体步骤。
21.请描述设备号有什么组成?设备号的作用是什么?
1)设备号由主设备号与次设备号组成。
在linux内核里用类型”dev_t”来表示一个设备号. 其实就是一个unsigned int.
dev_t类型有32位数, 其中高12位用于存放主设备号,低20位用于存放次设备号.
在”include/linux/kdev_t.h”里有提供设备号的操作宏。
2)提供一个设备文件,用户进程就可以通过操作设备文件来调用驱动.
22.请描述linux字符设备驱动的实现步骤及应用程序的调用过程?
1. 申请设备号 register_chrdev_region(...);
2. 声明一个cdev对象
struct cdev mycdev;
声明一个file_operations的文件操作对象
struct file_operations fops = {
.owner = THIS_MODULE,
.read = 读函数地址
....
};
3. 初始化cdev对象,并把fops对象与cdev对象关联起来
cdev_init(&mycdev, &fops); //mycdev.ops = &fops;
mycdev.owner = THIS_MODULE;
4. 把cdev对象加入内核里cdev_map(字符设备驱动的哈希表), 并指定该驱动对象的设备号
cdev_add(&mycdev, 设备号, 次设备号的个数);
5. 卸载模块时, 要把设备驱动对象从内核里移除, 并把设备号反注册
unregister_chrdev_region(..);
cdev_del(&mycdev);
23.请你描述对vfs调用接口的认识?
用户进程操作设备驱动,网络通信等与普通的文本文件操作的编程接口基本一样(open, read, write, ioctl…).
这套接口就是所谓的VFS(虚拟文件系统)。
(Virtual File System)
//////////////////////////////////////
//在linux内核,用一个inode节点对象描述一个要操作的文件/设备文件, 包括权限,设备号等信息. 就是描述一个要操作的文件的属性. 一个文件可以打开很多次, 但都是共用一个inode对象来描述属性的. 文件描述符属于一个进程的资源,不同进程里有可能相同的文件描述符.
24.请描述互斥量,自旋锁,信号量的用法和区别
25.请描述中段顶半部与底半部的好处的及底半部实现方法?
26.请描述linux驱动设备模型的用途
27.请描述uart的工作原理
28.请描述i2c的工作原理和linux内核的i2c设备驱动模型实现过程
29.描述pwm工作原理及内核的调用借口