Monday, October 12, 2015

git rebase eats my commits when the rebase fail... (no, not really)

I'm playing with git --rebase for the first time. The first patch of the series was fixing coding style, but it was also introducing some coding style errors related to continuation lines. Instead of adding two additional tabs, I was aligning the continuation lines with the previous opening parenthesis. Oops. So I needed to edit the first commit of the series to be able to resent all of them. git makes it quite convenient, right?

     $ git --rebase --interactive <commit hash>^

Then git will show a text file for you to edit and choose what to do with each commits. Everything fine until now. My workflow is

     $ vi path/to/file.c
     $ make path/to/file.o
     $ git add path/to/file.c
     $ git commit --amend
     $ git rebase --continue

Well this is awesome, and I had fun rebasing all my 11 commits and it was looking very good. Even when the automatic rebase doesn't work it is very intuitive and great to work with. This is one sample output when the automatic rebase fails:

     $ git rebase --continue
     [detached HEAD a22bcda] usb/host/fotg210: Remove useless else statement
      1 file changed, 2 insertions(+), 3 deletions(-)
     error: could not apply d13e859... usb/host/fotg210: Add function: output_buf_tds_dir()

     When you have resolved this problem, run "git rebase --continue".
     If you prefer to skip this patch, run "git rebase --skip" instead.
     To check out the original branch and stop rebasing, run "git rebase --abort".
     Could not apply d13e8595018d5eb2f2eea19df7b20916f3862004... usb/host/fotg210: Add function: output_buf_tds_dir()

So I just repeated the same workflow as before:

     $ vi path/to/file.c
     $ make path/to/file.o
     $ git add path/to/file.c
     $ git commit --amend
     $ git rebase --continue

And... AHHHH! Where is my commit? Why did git merged two commits? I have to start again, why didn't I create a new branch before playing with rebase? And then, like Bart being tempted by trying again until it works, I tried again just creating some branches to avoid having to do all the work again. How boring is to adjust where the first character should be in 100 lines... I even made a script that detect continuation lines and print it for me. But when the rebase failed, and I used my proven workflow, the result was exactly the same: AHHHHH!

The issue is that when the rebase fails, you are not supposed to commit, unless you want to merge two commits! But, but, where is that information available? git tells you that. Try git status after git add and you see. So the workflow when rebase fails should be:

     $ vi path/to/file.c
     $ make path/to/file.o
     $ git add path/to/file.c
     $ git status # Read what git has to tell you
     $ git rebase --continue

And no more mysterious merges!

Thursday, September 17, 2015

bool b = -1; if(b) printk("Yes, -1 maps to true!");

 /* Assign -1 to bool variable */
 bool b = -1;  
 if(b)  
     printk("Yes, -1 maps to true!");  
  
 /* Return -1 in bool function */
 bool f(void)  
 {  
        return -1;  
 }  
  
 if (f())  
     printk("Yes, -1 maps to true!");  

Yesterday I was reading this interesting discussion about the boolean type in C. The most interesting sentence was:

"0 is false, 1 is true, any other value is *undefined behavior*."

Then I started to look for abuses of the bool type in the Linux kernel. I wrote simple semantic patches for getting cases in which a negative values are being returned by bool functions:

 @@  
 identifier f, ret;  
 constant C;  
 typedef bool;  
 @@  
 bool f (...){  
 <+...  
 ret = -C;  
 ...  
 * return ret;  
 ...+>  
 }  

and

 @@  
 identifier f;  
 constant C;  
 typedef bool;  
 @@  
 bool f (...){  
 <+...  
 * return -C;  
 ...+>  
 }  

The first search for boolean functions that assign negative value to a variable, and return the variable, while the second search for boolean functions returning negative values.

Coccinelle found 3 candidates for me:

 diff -u -p ./tools/testing/selftests/powerpc/pmu/lib.c /tmp/nothing/tools/testing/selftests/powerpc/pmu/lib.c  
 --- ./tools/testing/selftests/powerpc/pmu/lib.c  
 +++ /tmp/nothing/tools/testing/selftests/powerpc/pmu/lib.c  
 @@ -248,6 +248,5 @@ bool require_paranoia_below(int level)  
  out_close:  
     fclose(f);  
  out:  
 -    return rc;  
  }  
 diff -u -p ./arch/powerpc/kernel/module_64.c /tmp/nothing/arch/powerpc/kernel/module_64.c  
 --- ./arch/powerpc/kernel/module_64.c  
 +++ /tmp/nothing/arch/powerpc/kernel/module_64.c  
 @@ -160,7 +160,6 @@ bool is_module_trampoline(u32 *p)  
     BUILD_BUG_ON(sizeof(ppc64_stub_insns) != sizeof(ppc64_stub_mask));  
     if (probe_kernel_read(insns, p, sizeof(insns)))  
 -        return -EFAULT;  
     for (i = 0; i < ARRAY_SIZE(ppc64_stub_insns); i++) {  
         u32 insna = insns[i];  
 diff -u -p ./tools/perf/util/util.c /tmp/nothing/tools/perf/util/util.c  
 --- ./tools/perf/util/util.c  
 +++ /tmp/nothing/tools/perf/util/util.c  
 @@ -709,7 +709,6 @@ bool find_process(const char *name)  
     dir = opendir(procfs__mountpoint());  
     if (!dir)  
 -        return -1;  
     /* Walk through the directory. */  
     while (ret && (d = readdir(dir)) != NULL) {  

Finding probable bugs with Coccinelle is easy, verifying which of the candidates are real bugs demands a little bit more work. I was mostly interested in the arch/powerpc/kernel/module_64.c file as is the only candidate that is kernel code, the other two are inside tools directory. I created test code based on examples from the article about booleans. As the file is part of the PowerPC architecture, some cross compiling is needed, but that's not a problem for Fedora. For installing the cross compiler for PowerPC:

 $ sudo dnf install gcc-powerpc64-linux-gnu  

Now that I have the compiler I want to see how does the compiler maps -1 when returning from a boolean function. Two toy examples:

 t.c:  
 _Bool main(void)  
 {  
     return -1;  
 }  
  
 t2.c:  
 int main(void)  
 {  
     return -1;  
 }  

for compiling:

 $ powerpc64-linux-gnu-gcc -O2 -S -fomit-frame-pointer <c file>  

which genarated t.s and t2.s. What is interesting is that the assembly code for t1.c returns 1 while the assembly code for t2.c returns -1.

 t.s:  
 ...  
 .L.main:  
     li 3,1  
     blr  
  
 t2.s:  
 ...  
 .L.main:  
     li 3,-1  
     blr  

This convinced me that -1 was being mapped to true, but it would be better to see the impact of my change on the real source file. For cross compiling two environment variables should be set, and before compiling the kernel should be configured. After that, creating the .s file is easy:

 $ export ARCH=powerpc CROSS_COMPILE=powerpc64-linux-gnu-  
 $ make allyesconfig  
 $ make arch/powerpc/kernel/module_64.s  

I did one make arch/powerpc/kernel/module_64.s for the original file returning -EFAULT, for a patched file returning false instead of -EFAULT, and for a patched file returning true instead of -EFAULT. Comparing this two files made it clear that -EFAULT is being mapped to true! The diff between the original .s file and the .s file returning false instead of -EFAULT starts with:

 440,441c440,441  
 < .L43:  
 <    li 3,1  # D.25775,  
 ---  
 > .L42:  
 >    li 3,0  # D.25775,  
 ...  

while the diff is empty comparing the original file and the version returning true instead of -EFAULT.

I sent patches earlier today. Let's see how it goes:



Tuesday, April 14, 2015

Adding a serial console to the Buffalo Air Station WZR-1750DHP

Playing with custom kernels and OpenWRT... It took me more than a month to flash a kernel that won't boot, but I did it yesterday. So the only way was to open the case, and connect the cables to access the serial console. Here is what I learned.

Before you start

Tools

For opening the case you will need to remove two screws that are similar to those of mobile phones. I don't know the type name nor the size, so I recommend you to check type and size before starting. The screws are under the adhesive on the back of the router near the power plug and the mode button. UPDATE: Matt Sealey posted a comment saying that the screws are Torx T6. Thank you Matt!


USB to Serial

You will need something to connect your computer to the serial interface of the router. The most common solution is to use an USB to serial adapter. There are plenty of options, but I'm using this one that I bought on Ebay for less than € 10. Search for 'FT232RL' on your favorite online store.




Cables

You will also need 3 female to female breadboard wires, which are also easy to find in your favorite online store. I found a pack of 40 wires for less than € 2.



Opening the case and connecting wires

Opening the case


This is more complicated than usual for two reasons:

  • There are two hidden screws on the back of the router. One near the power connector and other near the mode button. Remove the adhesive with care so you can put it back later. The screws are similar to the ones used on cell phones, so before you start, make sure you have the tools to open it properly.
  • There is a heat sink on the part you are going to remove, and it is glued to some metallic case on the PCB. Open the case slowly and with care.
But it is not a big deal, the case is not fragile, so you can apply a little bit of brute force when opening it.


3.3V Serial vs 5V Serial

The Serial to USB usually have the option to be 3.3V or 5V. Configure it to 3.3V. On my adapter the configuration is done on the back side of the PCB.


Connecting the wires

This is easier than opening the case. The pins on the router are near the reset button on the top right. You need to connect GND, RX and TX. The labels on the picture shows where the pins of the Serial to USB adapter should be connected to. TX on the Serial to USB should be connected to the pin with the label TX on the picture below.


Connecting 3.3V pin of the router to the VCC pin of the Serial to USB adapter causes the Serial to USB adapter to power off with the router. This can make your life harder as powering off the router may freeze the terminal emulation software on your PC.


Software

How to find the device node of the serial port?

After connecting the USB to serial adapter to your computer use dmesg and look for the ttyUSB string:

$ dmesg
...
[ 7174.272428] usb 6-1.3.4: Product: FT232R USB UART
[ 7174.272434] usb 6-1.3.4: Manufacturer: FTDI
[ 7174.321779] ftdi_sio 6-1.3.4:1.0: FTDI USB Serial Device converter detected
[ 7174.321873] usb 6-1.3.4: Detected FT232RL
[ 7174.322740] usb 6-1.3.4: FTDI USB Serial Device converter now attached to ttyUSB1

So in this case it is /dev/ttyUSB1


Do my user has permission to use the serial port?

Your distro may not allow your user to access the serial port. It is easy to check with ls and groups if you have rights:

$ ls -la /dev/ttyUSB1
crw-rw---- 1 root dialout 188, 1 Apr 14 16:55 /dev/ttyUSB1

$ groups
peter wheel dialout wireshark

As the grouop dialout has read and write permission at /dev/ttyUSB1 and as I'm member of the group dialout, everything is fine. If your user is not on the correct group, fix it before continuing. Simple dirty and dangerous hack: call minicom with sudo


Which software to use?

As I'm using Linux, I tried moserial and minicom. I could made it work only with minicom. There are two magical commands to minicom: Ctrl-A Z for showing all options and Ctrl-A X to exit in a clean way.


How to configure the serial port?

Configuring the serial port to 115200 8N1 is enough to read from the console, but not to write to it. For writing, it is necessary to disable Hardware Flow Control and disable Software Flow Control.


For minicom:
$ minicom -s -D /dev/ttyUSB1

Then navigate to Sertial port setup and make sure the configuration matches the description.



After that you could Exit to go to the console or save the configuration.


Interacting with the console

I follow this order:
  1. Connect the USB to Serial to your computer
  2. Start minicom
  3. Power on the router and wait for the initialization messages
  4. Press Ctrl-C some times until you see the prompt CFE> 
Now you can start fixing your router.



Now what?

Check this out: http://wiki.openwrt.org/doc/techref/bootloader/cfe


Sources / References

Wednesday, March 4, 2015

Buffalo WZR-1750DHP + OpenWRT

This is the first of a series of posts about Linux kernel development for ARM. There will be one or two more posts before we get into the Kernel, describing tasks as building, and configuring OpenWRT. Maybe I'll also write about de-bricking devices if I manage to do something silly with my router. :-)





What I like about the Buffalo WZR-1750DHP are the specs and the case without external antennas. It has a dual core ARM CPU with 512MB of RAM, and two wifi adapters: One for 2.4GHz and other for 5GHz.

I was expecting that to install OpenWRT in a device like the WZR-1750DHP, it would require to find and use a serial port. Luckily that's not necessary. All the procedure was done over friendly web interfaces.

As the router do not allow to install random firmware images, there are some workarounds to install OpenWRT. The steps I used are:

1 - Install and upgrade DD-WRT

  • Download the latest versions of the files factory-to-dd-wrt.bin and buffalo-wzr-1750dhp-webflash.bin from: ftp://ftp.dd-wrt.com/betas/2015/02-24-2015-r26348/buffalo_wzr-1750dhp/ As the directories names are build dates, you can browse the directories to see if there is a newer version available.
  • Update the firmware to the file factory-to-dd-wrt.bin using the web interface of the router. Be patient as this process takes some time.
  • Now your router is running DD-WRT. Using DD-WRT web interface update the firmware to the file buffalo-wzr-1750dhp-webflash.bin. Be patient as this process takes some time.
2 - Install OpenWRT
  • Download the latest version of the file openwrt-bcm53xx-bcm4708-buffalo-wzr-1750dhp-squashfs.trx from http://wiki.openwrt.org/toh/buffalo/wzr-1750dhp
  • Using DD-WRT web interface update the firmware to the file openwrt-bcm53xx-bcm4708-buffalo-wzr-1750dhp-squashfs.trx. Be patient as this process takes some time.
3 - Connect to OpenWRT
  • OpenWRT is beautifully simple, with a similar spirit of Arch Linux. This means that by default there is no web interface, and the wifi is disabled. So the first connection is made trough telnet over one of the Ethernet LAN ports. Probably you will be good with: $ telnet 192.168.1.1
  • Configure a root password with passwd: $ passwd
  • Exit telnet and login again with ssh.




Sources: