给RaspberryPi增加一个系统调用

浙江大学嵌入式系统课程

安装交叉编译工具,并设置NAS

1
2
3
4
sudo apt-get install gcc-arm-linux-gnueabi
cd ~
mkdir pi
sudo mount 192.168.0.4:/home/pi /home/neo/pi

详情见搭建RaspberryPi的交叉编译环境

下载Raspberry Pi的源码

1
2
3
4
5
6
# PC
cd ~/pi
mkdir kernel
cd kernel
git clone https://github.com/raspberrypi/linux.git
git clone https://github.com/raspberrypi/firmware.git

因为网络原因这一步耗时较长,需要耐心等待。也可以直接在github上下载zip压缩包自行解压,不过无论是使用RPi解压或是通过NAS解压速度都不快,可以考虑取下SD卡在PC上直接解压。

提取原有的内核配置文件配置新内核

1
2
3
4
5
# RPi
cd ~/kernel/linux
sudo zcat /proc/config.gz > .config
# PC
make ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- oldconfig

因为不需要修改内核配置,直接一路回车结束配置即可。

增加新的系统调用

  • 在linux/arch/arm/kernel/目录下新建mysyscall.c文件,输入以下内容
1
2
3
4
#include <linux/kernel.h>
void hello(void) {
printk("Hello world!\tFrom Neo\n");
}
  • 在linux/arch/arm/kernel/call.S中添加新的系统调用,这里我替换的是原本为sys_ni_syscall的0x900000+223号系统调用,如下图所示。

Lab_04_01

  • 修改arch/arm/kernel/目录下的Makefile文件,在obj-y后面添加mysyscall.o,如下图。

Lab_04_02

编译内核

1
2
# PC
make ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- -k

之所以选择交叉编译是因为RPi的主频和PC比起来相差太多,若是直接用RPi编译内核耗时太长了。

编译新lib

1
2
3
4
5
# PC
cd ~/pi/kernel
mkdir modules
cd linux
make modules_install ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- INSTALL_MOD_PATH=../modules

备份 firmware

1
2
3
4
5
# RPi
cd ~/kernel
mkdir firmware_ori
cd /boot
cp *.elf *.bin ~/kernel/firmware_ori

更新内核、lib和firmware

1
2
3
4
5
6
# RPi
cd ~/kernel
sudo cp linux/arch/arm/boot/Image /boot/kernel_new.img
sudo cp modules/lib /
cd firmware/boot
sudo cp bootcode.bin fixup.dat fixup_cd.dat start.elf /boot

修改RPi的配置文件/boot/config.txt

1
2
# RPi
sudo nano /boot/config.txt

在首行加上“kernel=kernel_new.img”即可。若原来已经存在了kernel字段,直接修改就可以了。

Lab_04_03

重启,测试新内核

1
2
# RPi
sudo reboot

先看看原内核信息,如下图。

Lab_04_04

新内核信息如下图。

Lab_04_05

可以看到内核已经从3.2.27+升级到了3.6.11。

测试新增加的系统调用

  • 编写测试文件hello.c,输入以下内容。
1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define sys_hello() {__asm__ __volatile__ ("swi 0x900000+223\n\t");} while(0)
int main(void) {
sys_hello();
printf("Type \"dmesg | tail\" to see the result.\n");
return 0;
}
  • 编译运行,查看结果。
1
2
3
4
# RPi
gcc hello.c -o hello
./hello
dmesg | tail

结果如下图。

Lab_04_06

在内核信息的最后一行可以看到我们的hello()函数已经成功执行了。