Monday, April 6, 2015

Customizing VExpress qemu board

In this post we'll try to customize default vexpress qemu board. We'll add I2C controller and temperature sensor connected to it. First step will be to define I2C controller itself. I assume that you already have fresh copy of qemu and kernel source code somewhere around and you know how to build it and run it. If not, please refer to one of my previous posts.

Add I2C

Our file of interest will be qemu/hw/arm/vexpress.c. Open it. First step will be to define I2C controller in this big enum at the beginning of file. Let's add VE_I2C as last item. Next step will be to define memory region for this controller, let's say 0x50000000 (and hope that it won't overlap with others). Put it into motherboard_legacy_map and motherboard_aseries_map.
[VE_I2C] = 0x50000000
legacy_map is used in a9_daughterboard, which we're using in our simulation, adding this to the other one will just simplify things. Now, initialization. To complete this we need to modify vexpress_common_init function to proper add I2C bus to our board. We need to create new versatile_i2c device. Put following line of code:
dev = sysbus_create_simple("versatile_i2c", map[VE_I2C], NULL);
somewhere near other calls  to this function, for example after creating pl111 device. We're done with bus.

Add sensor

Next step is to attach temperature sensor to I2C bus. This is as simple as adding i2c bus itself. Define I2CBus *i2c variable and put following line to obtain pointer to bus object:
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
Now we are able to attach our sensor to this bus by calling
i2c_create_slave(i2c, "tmp105", 0x68);
Where 0x68 is our I2C device address. To make this compile properly we need to include hw/i2c/i2c.h. Type make and make install. You're now done with this part.

Kernel support

In order to support tmp105 device we need a driver in kernel. Driver responsible for this sensor is LM75 driver: linux-stable/drivers/hwmon/lm75.c. Quick look at linux-stable/drivers/hwmon/Makefile reveals that we need to set CONFIG_SENSORS_LM75 in kernel configuration. Go to buildroot and type make linux-menuconfig, find proper entry (for example type "/" to open search box and type LM75, press "1" as it will be the first search result, press two times space to mark this entry as "*" - we don't need module). Now we can rebuild kernel by make linux-rebuild.

Device tree

This is first time when we use device tree. Device tree is form of describing hardware on the board. I won't go much into details, you can read something about it at http://www.devicetree.org/Device_Tree_Usage. First we need to edit linux-stable/arch/arm/boot/dts/vexpress-v2p-ca9.dts. Add following entry:
i2c: i2c@50000000 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "arm,versatile-i2c";
        reg = <0x50000000 0x1000>;

        tmp105@68 {
            compatible = "ti,tmp105";
            reg = <0x68>;
        };
    };
Now run make menuconfig in buildroot directory. Go to Kernel, check Build a Device Tree Blob and put "vexpress-v2p-ca9" into Device Tree Source file names. Save and exit. Run make linux-rebuild. Now you should have vexpress-v2p-ca9.dtb file created in your output/images directory of buildroot.

Running

Now we should be able to run our custom qemu with our custom kernel and custom device tree file. Something like this would be sufficient (just remember to pass correct paths):
~/opt/bin/qemu-system-arm -M vexpress-a9 -kernel images/zImage -drive file=./images/rootfs.ext2,if=sd -append "console=ttyAMA0,115200 root=/dev/mmcblk0" -serial stdio -dtb images/vexpress-v2p-ca9.dtb
Login as root and check out following directories:
  • /sys/devices/50000000.i2c/
  • /sys/devices/50000000.i2c/i2c-0/0-0068/
 You should be able to read temperature by
cat /sys/devices/50000000.i2c/i2c-0/0-0068/hwmon/hwmon0/temp1_input
 And check device type by
 cat /sys/devices/50000000.i2c/i2c-0/0-0068/hwmon/hwmon0/name
That's enough hacking for today ;).

Homework: Figure out how this driver and device is working. You can pass new value for temperature via "-device tmp105,temperature=11" qemu command line. This will call tmp105_set_temperature from hw/misc/tmp105.c. Have fun!

Custom kernel for ARM under qemu

In previous post we built full Linux system for our emulated qemu ARM machine. The next step is to tell buildroot that we want to use our custom kernel source tree. This will be the same kernel as in default buildroot configuration just in external directory. This will enable us to change it and test under qemu environment. First step is to fetch kernel, we can grab tarball from https://www.kernel.org/ or clone repository with git.
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
cd linux-stable
git checkout -b 3.18.4 v3.18.4
To check which kernel version was previously in use you can simply check configuration or look what's in buildroot/output/build directory. After that we simply go to buildroot directory and type:
make menuconfig
Go to Kernel -> Kernel version, choose Local Directory and put (full) path to your local kernel copy. Exit and save configuration. Now you can rebuild your Linux, to be 100% sure that you use your custom kernel, you can remove old copy from output directory
rm -rf output/build/linux-3.18.4/
now just type make. Buildroot will sync sources from local copy and do build. You can now try out to change something in your kernel to feel that you have "power" to make changes. E.g. open init/main.c file from your kernel source tree and put something like:
pr_info("Hello world from custom kernel!\n");
in start_kernel() function, just after
pr_notice("Kernel command line: %s\n", boot_command_line);
Save your changes, go back to buildroot directory and type
make linux-rebuild
Now you're done with your very own custom version of Linux kernel. Start qemu
qemu-system-arm -M vexpress-a9 -kernel output/images/zImage -drive file=output/images/rootfs.ext2,if=sd -append "console=ttyAMA0,115200 root=/dev/mmcblk0" -serial stdio 
Login as root and open /var/log/message for example with vi editor
vi /var/log/messages
You should find your print in log
Apr  6 17:38:26 buildroot kern.notice kernel: Kernel command line: console=ttyAMA0,115200 root=/dev/mmcblk0
Apr  6 17:38:26 buildroot kern.info kernel: Hello world from custom kernel!
Apr  6 17:38:26 buildroot kern.info kernel: PID hash table entries: 512 (order: -1, 2048 bytes)
Have fun!

Building simple Linux distro for ARM qemu

Building

The simplest way to build Linux for qemu machine is to use some automated build tool. I'm aware of at least two such projects:
In this post we'll use buildroot. First download buildroot tarball from download section of buildroot home page. I'm using buildoot-2015.02 release. Extract it somewhere in your home directory. Now you'll prepare default rootfs and kernel for versatile express emulated board. Extract buildroot and prepare default configuration for this target:
wget http://buildroot.uclibc.org/downloads/buildroot-2015.02.tar.gz
tar xzf buildroot-2015.02.tar.gz
cd buildroot-2015.02/
make qemu_arm_vexpress_defconfig
Now you should be able to build your first, default, Linux distribution for qemu emulated ARM based board.
make
Note that buildroot will automatically download all needed dependencies such as cross compiler, libc, kernel, busybox etc. This will take a while when run for the first time. For example, buildroot will compile brand new gcc for cross compilation and this is relative long process. Coffee break!

Running

After completing the build you can try out how it works in conjunction with previously built qemu. All needed files are placed in output/images directory. For now we have only two files there:
  • zImage - kernel image
  • rootfs.ext2 - ext2 filesystem image with root filesystem (all userland applications, configuration files etc)
Simply run:
qemu-system-arm -M vexpress-a9 -kernel output/images/zImage -drive file=output/images/rootfs.ext2,if=sd -append "console=ttyAMA0,115200 root=/dev/mmcblk0" -serial stdio
After few seconds you should see login prompt in your terminal. Type root and press enter. Now you are in your emulated ARM Versatile Express board running your custom Linux system. Press Ctrl+C to exit VM.

This is all for today. :) Enjoy.

Building qemu for arm architecture

Obtaining qemu source code

This is quite simple task. Just go to qemu homepage and get latest tarball. Other way is to clone qemu repository:
 git clone git://git.qemu-project.org/qemu.git
This command should fetch whole qemu repository into qemu directory. In case git clone you should switch to desired version which will be marked with appropriate tag. E.g.:
git checkout -b v2.3.0-rc2 v2.3.0-rc2
This command will create new branch (v2.3.0-rc2) from specified tag (v2.3.0-rc2) and automatically switch into it. Personally I prefer the git-way because I can track every changes I made in qemu source code.

Building

Requirements:
  • gcc compiler
  • GNU make
  • libfdt
  • ... [TODO: fill this list]
Building is simple, but first we need to choose whether we want to install qemu for whole system or just for current user. In second case we should prepare some directory in our home for additional software, in my case this directory will be ~/opt. Now, we should run configure script as follows:
./configure --target-list="arm-linux-user arm-softmmu" --prefix=$HOME/opt
If you want to install qemu for everyone in the system just omit --prefix argument. Now simply run make command followed by make install:
make && make install
Done! 

Friday, December 12, 2014

main function in C

As this is my first post, let's start with main function which is entry point of every C program. Let's try to do something fun with it and write program without any "real" function. Only one limitation is environment: GCC and Linux. But what's interesting about program "entry point"? GCC compiler (linker to be precise) is searching for main symbol and symbol is not necessarily a function. We can try it in simple program:
We see that this program compiles, runs and... well, crashes. The reason of crash is that "main" is variable and it's put into non executable part of our program. Jump to this location cause segmentation fault. When using gcc you can cheat a little bit and tell compiler to move your variable into .text (code) section by annotating it with section attribute. Still segmentation fault. It happens because 0x0 isn't really an useful (at least in this context) CPU instruction. We can tune this piece of software by changing 0 to "0xFEEB". After compiling and running you should see.. nothing. This is what was expected. Integer value 0xFEEB translates to "jump two bytes back" (0xEB - jmp [offset], 0xFE - minus 2) - in simple words it's never-ending loop, while(true); ;). Note that this code is extremely not portable, it depends almost on anything: compiler, linker, OS, endianness and CPU architecture. So please, don't use it. :)