Agregar una nueva llamada al sistema al kernel de
linux 3.2.2 para un CPU x86 de 32 bits


12I, enero 2012

Introducción

El  presente documento es una guía simplificada para agregar una llamada al sistema en el kernel de linux versión 3.2.2. Una llamada al sistema es utilizada por una aplicación (programa de usuario) para solicitarle un servicio al sistema operativo.... más información en la clase y notas de curso



Requerimientos

Se recomienda tener experiencia en la compilación del kenel de linux. Por ejemplo, se podría haber compilado el kernel para soportar directamente el sistema de archivos ext3 o ext4.

Por supuesto, es necesario tener las fuentes del kernel de linux.



Archivos a modificar/crear

Las siguientes rutas son relativas a /usr/src/linux. La ruta anterior podría ser un enlace simbólico a la ruta /usr/src/linux-3.2.2


#vim arch/x86/kernel/syscall_table_32.S

#vim arch/x86/include/asm/unistd_32.h


#vim arch/x86/include/asm/syscalls.h



Archivo en el espacio de usuario, para realizar la llamada al sistema:

$vim som_llamada.c
 



Tabla de llamadas al sistema

El archivo arch/x86/kernel/syscall_table_32.S contiene la tabla de las llamadas al sistema, la cual consiste en la definición de sus nombres. El número de la llamada se da de manera implicita de acuerdo a su posición en la tabla.  Agregamos al final de la tabla el nombre de nuestra llamada utilizando el mismo formato:

ENTRY(sys_call_table)
    .long sys_restart_syscall
    .long sys_exit
    ...
    ...
    .long sys_process_vm_writev  /*348*/
    .long sys_sumar              /*349, nueva llamada*/

La llamada al sistema que implementaremos será la suma de dos parámetros enteros.


Asociación de tabla y funciones

Para asociar una entrada de la tabla anterior con una función, es necesario definir un símbolo. La tabla de asociación se encuentra en el archivo arch/x86/include/asm/unistd_32.h en el cual se puede apreciar la definición de nombres y números. De igual forma agregamos la definición nuestra llamada:


...

#define __NR_restart_syscall       0
#define __NR_exit                  1
...
#define __NR_process_vm_writev   348
#define __NR_sumar               349


El tamaño de la tabla está definido en el mismo archivo con el símbolo:

#define __NR_syscalls             349


y evidentemente cambiamos por:

#define __NR_
syscalls             350


Definición de la llamada

El archivo que contendrá la definición de la llamada dependerá de lo que hace la misma llamada, es decir, si es una llamada relacionada con el tiempo deberá de estar en el kernel/time.c. Si la llamada tiene que ver con el acceso a procesos podría estar en kernel/sched.c.

Las llamadas genéricas se definen en el archivo
kernel/sys.c. A patir de la versión 3.0 del kernel, las definiciones se crean por medio de interfaces constantes. La interfaz sirve para ejecutar las llamadas de manera similar a como lo hace la llamada al sistema execve.  Con una interfaz constante, cada arquitectura crea su propia interfaz y llama a una sola implementación de la llamada. La interfaz para el x86 se puede ver en el archivo arch/x86/kernel/sys_i386_32.c, dicha interfaz se llama kernel_execve.

La definición real de la llamada se puede hacer en el archivo
kernel/sys.c. Se han creado 3 tipos de interfaz de acuerdo con el número de parámetros que requiera la llamada a implementar. Como nuestra llamada tendrá dos parámetros utilizamos la interfaz SYSCALL_DEFINE2, de la siguiente forma:

SYSCALL_DEFINE2(sumar,int,a,int,b)
{
    return a + b;
}


Se observa que el primero parámetro es el nombre de la llamada, y los siguientes es tanto el tipo de dato del parámetro como el identificador.


Compilación del kernel

Procedemos a compilar el kernel. Es importante recordar que es necesario tener un archivo de configuración para compilar el kernel, es decir, un archivo .config. Puedes utilizar la configuración del kernel que está en ejecución. Para detalles ver la compilación del kernel.

#uname -a
Linux drmoriarti 2.6.32-5-686 #1 SMP Thu Nov 3 04:23:54 UTC 2011 i686

#cp /boot/config-2.6.32-5-686 .config


Si se han utilizado las fuentes para compilar un kernel anterior, es necesario primero ejecutar el comando:

#make clean

Y mientras se compila, ya sabes, un cafecito. Por cierto el chango (el Isaac) recomienda que prueben el café de origen de la Finca de Jocutla.



Prueba en un programa de usuario

Finalmente, lanzamos nuestro nuevo kernel y lo probamos invocando nuestra llamada desde un programa de usuario, som_llamada.c:

#include<linux/unistd.h>
#include<stdio.h>

int main()
{

   printf(“1+2 = %d\n”, syscall(349, 1, 2));

   return 0;

}