Thursday, July 3, 2014

Atomic function implementation in ARM architecture

LDREX and STREX are the instructions on which atomic functions built on. Let's look briefly at those two instructions.



LDREX
 

LDREX loads data from memory.
  • If the physical address has the Shared TLB attribute, LDREX tags the physical address as exclusive access for the current processor, and clears any exclusive access tag for this processor for any other physical address.
  • Otherwise, it tags the fact that the executing processor has an outstanding tagged physical address.


STREX

STREX performs a conditional store to memory. The conditions are as follows:
  • If the physical address does not have the Shared TLB attribute, and the executing processor has an outstanding tagged physical address, the store takes place and the tag is cleared.
  • If the physical address does not have the Shared TLB attribute, and the executing processor does not have an outstanding tagged physical address, the store does not take place.
  • If the physical address has the Shared TLB attribute, and the physical address is tagged as exclusive access for the executing processor, the store takes place and the tag is cleared.
  • If the physical address has the Shared TLB attribute, and the physical address is not tagged as exclusive access for the executing processor, the store does not take place.


LDREX{size}{cond} Rd, {Rd2,} [Rn {, #offset}]
STREX{size}{cond} Rd, Rm, {Rm2,} [Rn {, #offset}]

Rd
is the destination register. After the instruction, this contains:
  • for LDREX, the data loaded from memory
  • for STREX, either:
    • 0: if the instruction succeeds
    • 1: if the instruction is locked out.


For more information about the above mentioned ARM instructions, refer the following link:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204g/Cihbghef.html


The following discussion is based on the assumption that the you are aware of the inline assembly in gcc. If you are not aware of the inline assembly in gcc, check out the follwoing links.
http://www.ethernut.de/en/documents/arm-inline-asm.html
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html


There are various atomic functions like:
  • atomic_add( )
  • atomic_sub( )
  • atomic_cmpxchg( )
  • atomic_clear_mask( )
  • atomic_inc()
  • atomic_dec()
  • atomic_inc_and_test()
  • atomic_dec_and_test() etc


All of them are based on the same principle of using LDREX and STREX. Let's look at the implementation of atomic_add() and you can check the rest of the fucntions in arch/arm/include/asm/atomic.h which are almost similar in implementation.

/*
 * ARMv6 UP and SMP safe atomic ops.  We use load exclusive and
 * store exclusive to ensure that these are atomic.  We may loop
 * to ensure that the update happens.
 */
static inline void atomic_add(int i, atomic_t *v)
{
        unsigned long tmp;
        int result;

        __asm__ __volatile__("@ atomic_add\n"
"1:     ldrex   %0, [%3]\n"
"       add     %0, %0, %4\n"
"       strex   %1, %0, [%3]\n"
"       teq     %1, #0\n"
"       bne     1b"
        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
        : "r" (&v->counter), "Ir" (i)
        : "cc");
}



Here is the typedef for atomic variable:

typedef struct {
        int counter;
} atomic_t;


Let's go through instruction by instruction.


Scenario 1: atomic_add() succeeded in one go as no one else is accessing that variable which has to be incremented atomically.

ldrex   %0, [%3]
The address of the variable that has to incremented is in %3. The value at this address is copied into %0 i.e. 'result'.
LDREX tags the physical address as exclusive access for the current processor, and clears any exclusive access tag for this processor for any other physical address.

add     %0, %0, %4
Adds i to the vaiable which we want to increment.

strex   %1, %0, [%3]
Stores the incremented value in the memory and writes 0 into %1 i.e. 'tmp'.

teq     %1, #0
Sets the Z bit in CPSR as 'tmp' is 0.

bne     1b
As the Z bit in CPSR is set, this doesn't branch to the label '1:' and proceeds further.



Scenario 2: atomic_add() is not succeeded in first pass, but may succeed in one of the later passes. This happens when some one else is atomically accessing this variable.

First pass:

ldrex   %0, [%3]
The address of the variable that has to incremented is in %3. The value at this address is copied into %0 i.e. 'result'.
In the 1st pass, it tags the fact that the executing processor has an outstanding tagged physical address and LDREX can't tag the physical address as exclusive access for the current processor.

add     %0, %0, %4
Adds i to the vaiable which we want to increment.

strex   %1, %0, [%3]
Fails to store the incremented value in the memory as  the physical address is not tagged as exclusive access for the executing processor and writes 1 into %1 i.e. 'tmp'.

teq     %1, #0
Clears the Z bit in CPSR as 'tmp' is 1.

bne     1b
As the Z bit in CPSR is cleared, this branches to the label '1:' and repeats tha above instuctions.


2nd or later pass:

If no one is accessing the vaiable that we want to increment atomically then the code flow is same as Scenario 1, other wise the code flow is same as Scenraio 2: First pass.

ARM Linux: Switching page tables during context switch

Every process (except kernel threads) in Linux has a page table of it's own.

For 32-bit ARM without LPAE, 2-level page table is used.
For 32-bit ARM with LPAE, 3-level page table is used.
 
In arch/arm/mm/proc-v7.S:
#ifdef CONFIG_ARM_LPAE
#include "proc-v7-3level.S"
#else
#include "proc-v7-2level.S"
#endif


 

Let's look at the case of 32-bit ARM without LPAE, which uses 2-Level page table
  • Level-1: pgd: page global directory
  • Level-2: pte: page table entry

pgd base for each process is embedded in task_struct as shown below:

struct task_struct {   :include/linux/sched.h
    ...
    struct mm_struct *mm
    ...
    }

struct mm_struct{      :include/linux/mm_types.h
    ...
    pgd_t * pgd;
    ...
    }

TTBR: Translation Table Base Register - Check ARM spec for more details about this register
LAPE: Large Physical Address Extension

When ever context switch happens in Linux, the pgd base of the next process has to be stored in TTBR (note that this is not done while switching to kernel threads as the kernel threads doesn't have a mm struct of it's own). Here is the code flow for how it is done.

schedule() :kernel/sched/core.c
     __schedule()

__schedule() :kernel/sched/core.c
    context_switch()

context_switch() :kernel/sched/core.c
    switch_mm()


switch_mm() :arch/arm/include/asm/mmu_context.h
    cpu_switch_mm(next->pgd, next);

    #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
    #define cpu_do_switch_mm processor.switch_mm

    processor.switch_mm is cpu_v7_switch_mm() in case of ARMv7

        struct processor { :arch/arm/include/asm/proc-fns.h
        ...
        /*
         * Set the page table
         */
        void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm);
        ...
        }
 
    Check arch/arm/include/asm/glue-proc.h
       #define CPU_NAME cpu_v7
       #define cpu_do_switch_mm   __glue(CPU_NAME,_switch_mm)

Here is some info from ARM spec for accessing TTBR register:
CP15 register: c2
Register name: TTBR0, Translation Table Base Register 0
Affected operation: MCR p15, 0, <Rd>, c2, c0, 0
     
    cpu_v7_switch_mm(mm->pgd, mm) :arch/arm/mm/proc-v7-2level.S
        ...
        mcr     p15, 0, r0, c2, c0, 0           @ set TTB 0

    This is the instruction which sets TTBR0 to point to the page table of the next process. 

    [Based on Linux kernel v3.4]

    pm-cpuidle: High level code flow from bootup

    When the kernel boots up, it starts as a single thread for which PID = 0. It is named as "swapper". This eventually calls cpu_idle() (which is architecture specific function) and this task is nothing but idle task.

    init/main.c
    start_kernel(void)
    {
      ...
      rest_init(); 

      This is the last function that is called after all inits are called.
    }


    rest_init()
    {
      ...
      /* We need to spawn init first so that it obtains pid 1, however
       * the init task will end up wanting to create kthreads, which, if
       * we schedule it before we create kthreadd, will OOPS.
       */
      kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);    PID = 1
      ...
      pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);   PID = 2
      ...
      /*
       * The boot idle thread must execute schedule()
       * at least once to get things moving:
       */
      init_idle_bootup_task(current);
      schedule_preempt_disabled();
      /* Call into cpu_idle with preempt disabled */
      cpu_idle();     PID = 0

    }

    arch/arm/kernel/process.c
    cpu_idle()
     
    {
      ...
      /* endless idle loop with no priority at all */
      while (1) {

        ...
        while (!need_resched()) {
          ...
          if (cpuidle_idle_call())     
            pm_idle()
          If CPU_IDLE is enabled, in non-error case, cpuidle_idle_call() 
          returns 0. Thereby pm_idle() is not called. Determining and 
          entering the sleep state is done in cpuidle_idle_call() 
          itself.
          ...
        }
      }
    }

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    pm_idle()
     cpu_do_idle() => cpu_v7_do_idle

    arch/arm/mm/proc-v7.S
     ENTRY(cpu_v7_do_idle)
        dsb           @ WFI may enter a low-power mode
        wfi
        mov     pc, lr
     ENDPROC(cpu_v7_do_idle)
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    drivers/cpuidle/cpuidle.c
    int cpuidle_idle_call(void)
    {
      ...
      /* ask the governor for the next state */
      
      next_state = cpuidle_curr_governor->select(drv, dev);
      Current cpuidle governer identifies the appropriate idle
      state. Platform data is used by the governor to select the 
      approprite sleep state.
      ...
      entered_state = cpuidle_enter_ops(dev, drv, next_state);

      Here we enter the idle state suggested by current cpuidle 
      governor. Platform specific code comes into picture to set 
      the appropriate idle state. 
      ...
    }

    Sunday, April 27, 2014

    Show “Hello World” on Qemu

    Show “Hello World” on Qemu

    Before we continue, make sure your Linux already has Qemu inside. If you want to install, you can refer to my previous posting, Install Qemu on Ubuntu. You also need to check whether your Linux already has GCC cross compiler or not. To check whether the compiler already installed on your linux, you can check by using:
    $ arm-linux-gnueabi-gcc --version
    And it will show, example:
    arm-linux-gnueabi-gcc (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3
    Copyright (C) 2012 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    If your Linux doesn’t has the compiler, you can install it:
    $ sudo apt-get install gcc-arm-linux-gnueabi
    Then, you need to prepare directory for your project. Example:
    $ cd
    $ mkdir Project
    $ cd Project
    Now we are in Project directory. In this directory, we will create sample code using C language to print “Hello World”, and compile linux source code. Then we will launch our software on Qemu.
    Create our sample code, and name it as init.c. This is the code:
    #include <stdio.h>
    
    void main() {
      printf("Hello World!\n");
      while(1);
    }
    Compile the code use gcc cross compiler and put in ramdisk:
    $ arm-linux-gnueabi-gcc -static init.c -o init
    $ echo init|cpio -o --format=newc > initramfs
    The next step is preparing directory for our Linux source code.
    $ mkdir Sources
    $ cd Sources
    Now we already in directory ~/Project/Sources.
    To get the latest Linux source code you can download from this is link https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.9.tar.xz. Extract the file:
    $ tar -xJvf linux-3.9.tar.xz
    Go to Linux soure code directory and compile the code:
    $ cd linux-3.9
    $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
    $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all
    It will take times depend on how fast your computer. The result of compilation is zImage. You can copy that file to our Project directory.
    $ cp arch/arm/boot/zImage ~/Project
    $ cd ~/Project
    Then let’s try to launch our code on Qemu:
    $ qemu-system-arm -M vexpress-a9 -kernel zImage -initrd initramfs -serial stdio -append "console=tty1"
    If everything is smooth, you will see text “Hello World” will be shown on Qemu console.

    Busybox run on Qemu

    Preparation
    1. Install developer ncurses library. It will be used during linux kernel configuration.
    2. Install latest Qemu from Linaro repository. You can refer to my previous posting, Install Qemu on Ubuntu.
    3. Download arm cross compiler from CodeSourcery. Make sure you already become member of mentor.com website. Currently latest version of compiler is 2012.09-64. But I already tried that version and when always be fail. I also tried previous version, it is 2012.03-57, and the result is same. And I have been successfully compile linux kernel and install busybox using version 2011.09-70.
    4. Download latest linux source code from kernel.org. Currently I am using linux version 3.9. File name should be linux-3.9.tar.xz.
    5. Download latest busybox from busybox.net. Currently, latest version of busybox is busybox-1.21.0.
    Install Developer ncurses Library
    $ sudo apt-get install libncurses5-dev
    Files and Folders Arrangement
    1. Create folder for our project.
      # cd
      # mkdir Project
      # cd Project
    2. Create folder for our source code that we already downloaded previously.
      # mkdir Sources
      # cd Sources
    3. Copy all files (encrypted files) to Sources folder. Make sure we already collected these files,
      linux-3.9.tar.xz, busybox-1.21.0.tar.bz2.tar.bz2, and arm-2011.09-70-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2.tar.bz2.
    4. Extract all those files.
      # tar -xJvf linux-3.9.tar.xz
      # tar -xjvf busybox-1.21.0.tar.bz2.tar.bz2
      # tar -xjvf arm-2011.09-70-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2.tar.bz2
      Now we already have 3 new folders: busybox-1.21.0, linux-3.9, and arm-2011.09.
    Make Path for The Compiler
    1. Open ~/.bashrc file and add text in order to inform OS that compiler will be added to path.
      $ nano ~/.bashrc
      After open .bashrc file, add this text,
      export PATH=/home/your_user/Project/arm-2011.09/bin:$PATH
      Exit from editor, then refresh .bashrc file,
      $ source ~/.
    2. Test whether your compiler already successfully added to path or not.
      $ arm-none-linux-gnueabi-gcc --version
    Compile Linux Kernel
    1. Go to linux source code folder.
      $ cd ~/Project/Sources/linux-3.9
    2. Preparation before compile linux source code.
      $ make mrproper
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- versatile_defconfig
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- menuconfig
      In linux kernel configuration window, go to “Enable loadable module support”. Press space or N key button, to un-check this feature. Next is go to Kernel Features, then activate ARM EABI compilation feature.
    3. Compile linux kernel.
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
      In this process, it will take a few minutes, depend on your computer speed.
    4. After compilation finished, we will get linux kernel image (zImage) from folder ./arch/arm/boot. Copy the image into your project folder.
      $ cp ./arch/arm/boot/zImage ~/Project
      Now, our linux kernel image is ready to be used.
    Install BusyBox
    1. Go to busybox source code folder.
      $ cd ~/Project/Sources/busybox-1.21.0
    2. Preparation before install BusyBox.
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- defconfig
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- menuconfig
      In Busybox Configuration window, go to Busybox Setting -> Build Options -> check Build BusyBox as a static binary. Then go to Busybox Setting -> Installation Options -> Installation Prefix. Remember that folder name, because that folder will be destination of our installation files.
    3. Install BusyBox.
      $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- install
    4. Go to _install folder and add some new folders.
      $ cd _install
      $ mkdir proc sys dev etc etc/init.d
    5. Add rcS file in folder init.d, and add code to mount proc and sysfs when launch kernel on Qemu.
      $ touch etc/init.d/rcS
      $ nano etc/init.d/rcS
      Add this code,
      #!/bin/sh
      mount -t proc none /proc
      mount -t sysfs none /sys
      /sbin/mdev -s
      Change permission of rcS file,
      $ chmod +x etc/init.d/rcS
    6. Convert _install folder to rootfs.img, and copy to Project folder.
      $ find . | cpio -o --format=newc > ..rootfs.img
      $ cd ..
      $ gzip -c rootfs.img > rootfs.img.gz
      $ cp rootfs.img.gz ~/Project
    Launch Qemu
    $ cd ~/Project
    $ qemu-system-arm -M versatilepb -m 128M -kernel zImage -initrd rootfs.img.gz -append "root=/dev/ram rdinit=/bin/sh"
    After qemu launched, suppose it will show # sign. And you can type some commands same as linux commands.

    Saturday, June 1, 2013

    Utilities: Linux System Troubleshooting

    Source: https://sites.google.com/site/al271828183x/techblog/utilitieslinuxsystemtroubleshooting




    When you need to make some investigation or troubleshooting of your System running Linux OS, check the list below of useful utilities that can help you to make your live easier.

    How-To setup SSH Server on Embedded Linux Targets


    Source : https://sites.google.com/site/al271828183x/techblog/dropbearhow-tosetupsshserveronembeddedlinuxtargets


    'm using SSH Server on Embedded Linux Targets for my development purposes. To be more specific I'm using Eclipse with Remote System Explorer extension, which requires SSH connection to be established between Development Station and Embedded Linux Target.
    Below is a simple step-by-step guide to setup SSH Server (Dropbear) on your Embedded Linux Target.

    • You need to prepare "sftp-server" for Embedded Linux Target, so you need to cross-compile OpenSSH
      • Download and unzip OpenSSH source code (for example: openssh-6.0p1.tar.gz)
      • In OpenSSH directory create "build" sub-directory
      • In the "build" directory create following "build.sh" script (you need to modify path to your cross tools, gcc and strip):
        #!/bin/bash
        export PATH=/opt/crosstool/gcc-3.4.5-glibc-2.3.6/arm-cirrus-linux-gnu/bin:$PATH
        ../configure --prefix=$($PWD) CC=arm-linux-gcc --host=arm
        make sftp-server
        arm-linux-strip -v sftp-server

      • Run "build.sh" (don't forget "chmod a+x build.sh")
      • "sftp-server" executable will be created, copy it to your Embedded Linux Target and place it to the "/usr/libexec" directory
    • Now you need to create SSH key pair on your Development Station (I'm running on Fedora distribution)
    • In directory "~/.ssh" execute "ssh-keygen"
    • Copy "id_rsa.pub" file to Embedded Linux Target and place it to the "~/.ssh" directory
    • On Embedded Linux Target from directory "~/.ssh" execute following command:
    cat id_rsa.pub >> authorized_keys
    • Dropbear, you need to cross-compile Dropbear for your Embedded Linux Target
      • Download and unzip Dropbear source code (for example: dropbear-2012.55.tar.gz)
      • In Dropbear directory create "build" sub-directory
      • In the "build" directory create following "build.sh" script (you need to modify path to your cross tools, gcc and strip):
    #!/bin/bash
    export PATH=/opt/crosstool/gcc-3.4.5-glibc-2.3.6/arm-cirrus-linux-gnu/bin:$PATH
    ../configure --prefix=$($PWD) CC=arm-linux-gcc --host=arm
    make
    arm-linux-strip -v dropbear
    arm-linux-strip -v dropbearkey
    • "dropbear" and "dropbearkey" executables will be created, copy them to Embedded Linux Target and place them in "/bin" directory
    • Now you need to create SSH host keys for Dropbear
    • On Embedded Linux Target create directory "/etc/dropbear/"
    • In the "/etc/dropbear/" directory execute following lines:
    dropbearkey -t dss -f dropbear_dss_host_key
    dropbearkey -t rsa -f dropbear_rsa_host_key
    • Running SSH Server
    • On Embedded Linux Target execute following line:

    dropbear -s -g