Thursday, May 30, 2013

Internals of Executing a user program.

My first C program was hello world!!.  It was written in a Unix Environment. In this post I will try to cover, as what happens when that binary executable is executed on a shell prompt.

This blog will mostly look in to code journey(path) taken when a program is executed by an user.

As we all know, all binary executable is executed at the the shell(directly or indirectly).  Their is reason for this, shell is a command-line interpreter that provides an user interface for an OS.

To execute an executable program, some preparation is needed and most of it done by kernel. To interact with kernel user needs to invoke specific system call.

When we run a simple program like hello_wold at the shell prompt, the command line interpreter invokes  execve() //system call SYS_execve.

All the argc, argv and envp gets copied from userspace to kernel space.

sys_execve()                                                                     arch/x86/kernel/process.c

do_execve()                                                                       fs/exec.c

do_execve_common()                                                        fs/exec.c
       prepare struct linux_binprm                                        include/linux/binfmts.h
       it holds all the information required when loading binary file.


search_binary_handler()                                                       fs/exec.c

          Lookup for load_binary function callback based on executable format (ELF, COEFF)
          Iterate and match against linux_binprm thru' all supported format.
          Invoke fmt->load_binary callback, since we know the format type is ELF.

load_elf_binary()                                                                  fs/binfmt_elf.c
           // More information needs to be added w.r.t stack initialization/memory for bss
          // and elf format.
           start_thread() macro calls _dl_start() i.e invoke dynamic linker /lib/

Wednesday, May 22, 2013

How to Reserve RAM

Determine total memory available.

cat /proc/meminfo dumps the memory information

krmohan@krmohan:~$ cat /proc/meminfo
MemTotal:        4040084 kB  (~ 3.8 G)
MemFree:         3125056 kB  (~ 2.9 G) free

Locate grub file on your system.
1) Ubuntu
Add or append an entry to /etc/default/grub

Rebuild grub.cfg with your changes
krmohan@krmohan:~$ sudo update-grub


memmap=64M$1024M in grub file reserve 64MB at offset 1G

Reboot your system

 krmohan@krmohan:~$ cat /proc/meminfo
MemTotal:        2001116 kB  (~1.8 G)  2 G gone after your changes
MemFree:         1318796 kB

Driver Changes
In your init or probe function

      /* Tell the kernel you have reserved this resource, thus preventing other driver to do the same */
       request_mem_region(2000*1024*1024, size, "who r u");
       /* get the virtual address of the physical address */
     virt_addr =  ioremap_nocache(2000*1024*1024, size);

Make sure, you release the resource in _exit or release
       /* Tell kernel that you are done with the resource */
       release_mem_region(2000*1024*1024, size);
      /* unmap io memory */

           printk(KERN_INFO "ioread32 %x",  ioread32(virt_addr + i));

In Userspace
#define RAM_START       (2000 * 1024 * 1024)
#define MAP_SIZE        (unsigned)48*1024*1024
#define MAP_MASK        (MAP_SIZE - 1)

volatile        char *hw = (volatile char *)RAM_START;
memfd = open("/dev/mem", O_RDWR|O_SYNC);
membase = mmap(0,  size, ROT_READ|PROT_WRITE, MAP_SHARED, memfd, (unsigned) hw & ~MAP_MASK);
(unsigned int *)membase[i] = rand();

fd = open("my_driver", O_RDONLY);
read(fd, x, 1);   // verify with kernel message, what u wrote was seen by driver.

In kernel
 #define OFFSET 0x40000000   //1G

       unsigned long vsize = vma->vm_end - vma->vm_start;
     /* mmap physical memory @ 1G */
        if (remap_pfn_range(vma, vma->vm_start,
                        //virt_to_phys(virt_addr) >> PAGE_SHIFT,
                        OFFSET >> PAGE_SHIFT,
                vsize, vma->vm_page_prot)) {
                printk(KERN_INFO "Failed ...\n");
                return -EAGAIN;
        printk(KERN_INFO "Size mmap is %ld\n", vsize);
        return (0);