Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (VI)

Este artículo lo escribí para el blog en español DesdeLinux el 11 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

Bien, después de un pequeño paréntesis seguimos con nuestra serie de tutoriales. Si retomamos el código anterior debemos de tener el ISR de la división por cero. Ahora debemos de rellenar el resto de las ISR para las que habíamos puesto mensaje (las 32 primeras). Bien ahora vamos a seguir programando interrupciones, vamos a hacer las IRQ también conocidas como Interrupts Requests. Estas IRQ se generan por los dispositivos de hardware tales como teclados, ratones, impresoras, etc. Inicialmente las primeras 8 IRQ se mapean automáticamente en las posiciones de la IDT del 8 al 15. Como hemos usado las 32 primeras para las excepciones ahora tenemos que remapearlas. Nosotros pondremos la IRQ desde la 32 hasta la 45. Para ello primero debemos remapear los los IRQ:

Ahora creamos una función para instalar los IRQ:

La sentencia de asm sti nos activa los IRQ. Bien ahora vamos con algo similar a los ISR. Las funciones de un IRQ básico:

Una parte común (igual que la de los ISR):

Y un handler básico:

Con esto ya deberíamos tener activados los IRQ aunque todavía no hagan nada. En el siguiente capítulo veremos como obtener datos a partir de estos IRQ como el reloj o el teclado.

NextDivel-IRQ

Y con esto termina el post de hoy. Como habeis podido comprobar ahora escribo menos regularmente debido a otros asuntos. Aun así seguiré hasta tener un sistema operativo más completo

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (V)

Este artículo lo escribí para el blog en español DesdeLinux el 11 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

En esta quinta entrega veremos una tabla bastante parecida a la GDT tanto en teoría como en uso, nos referimos a la IDT. Las siglas de IDT hacen referencia a Interrupts Description Table y es una tabla que se usa para manejar las interrupciones que se produzcan. Por ejemplo, alguien hace una división entre 0, se llama a la función encargada de procesar. Estas funciones son los ISR (Interrupt Service Routines). Así pues vamos a crear la IDT y añadir algunos ISR.

Lo primero vamos a declarar las estructuras correspondientes a la IDT:

Como se observa si comparáis con la GDT la estructura Ptr es idéntica y la Entry es bastante parecida. Por consiguiente las funciones de poner una entrada (SetGate) e instalar (Install) son muy parecidas.

Instalar:

Si nos fijamos veremos que la función de instalar usa la función ND::Memory::Set que habíamos declarado en el otro post. También podemos apreciar como no hacemos ninguna llamada a SetGate todavía y llamamos a ND::IDT::Flush, para esta función usamos otra vez la sentencia asm volatile:

Si todo va bien y hacemos un arreglo estético debería quedar así:

NextDivel-IDT

Bien, ahora vamos a empezar a rellenar la IDT con interrupciones. Aquí voy a crear solo una pero para el resto se haría igual. Voy a hacer la interrupción de división por cero. Como bien sabrán en matemáticas no se puede dividir un número entre 0. Si esto ocurre en el procesador se genera una excepción ya que no puede continuar. En la IDT la primera interrupción en la lista (0) corresponde a este suceso.

Añadimos esto entre el seteo de memoria y el flush dentro de la función Install de la IDT:

La función de callback va a ser ND::ISR::ISR1 que es bastante simple aunque debemos usar ASM:

NDISRCommon lo definiremos como una función en lenguaje C. Para ahorrar ficheros y mejorar legibilidad podemos usar extern “C”{}:

Este código en ASM puede ser un poco difícil de entender pero esto es así porque vamos a declarar una estructura en C para acceder a los datos que genere la interrupción. Obviamente si no quisieras eso podrías llamar simplemente en ND::ISR::ISR1 al Kernel Panic o algo por el estilo. La estructura tiene una forma tal que así:

Y por último hacemos la función NDISRHandler (también con link del C) en que mostramos un kernel panic y una pequeña descripción del error según el que tenemos en una lista de errores.

Bien y con esto ya somos capaces de manejar esta interrupción. Con el resto de interrupciones pasaría parecido salvo que hay algunas que devuelven parámetros y usaríamos la estructura reg para obtenerlo. Sin embargo te preguntarás que como sabemos si funciona de verdad. Para probar si funciona vamos a introducir una sencilla línea después del ND::IDT::Install():

Si compilamos nos dará un warning y si tratamos de ejecutarlo nos saldrá una bonita pantalla:

NextDivel-ISR

Y con esto termina este post, creo que es uno de los más extensos pero bastante funcional.

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (IV)

Este artículo lo escribí para el blog en español DesdeLinux el 5 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

Bienvenidos de nuevo a esta serie de posts titulada “Emulando a Linus Torvalds”. Hoy veremos la GDT. Primero tenemos que ver que es la GDT. Según Wikipedia:

The Global Descriptor Table or GDT is a data structure used by Intel x86-family processors starting with the 80286 in order to define the characteristics of the various memory areas used during program execution, including the base address, the size and access privileges like executability and writability

Que traducido sería una Tabla de Descriptores Global, una estructura de datos usada en los procesadores Intel x86 desde el 80286 para definir las características de varias áreas de memoria usadas durante la ejecución del programa.

Resumiendo, si estamos en un procesador Intel x86 deberemos definir una GDT para un correcto uso de la memoria. Nosotros no vamos a hacer mucha complicación y vamos a definir 3 entradas en la tabla:

  • Una entrada NULL, obligatoria para todas las tablas.
  • Una entrada para la sección data, usaremos el máximo, que en 32 bits son 4 GB.
  • Una entrada para la sección code, usaremos el máximo, que en 32 bits son 4 GB.

Como veis data y code usarán el mismo espacio. Bien, ahora vamos a implementarlo. Para ello usaremos dos estructuras, la primera se encargará de contener un puntero hacia los datos reales de nuestra GDT. Y la segunda será un array con las entradas de la GDT. Primero vamos a definirlas.

Habrán observado un curioso __attribute__((packed)) al final de las estructuras. Esto le dice al GCC que no optimice las estructuras porque lo que queremos es pasar los datos tal cual al procesador. Ahora vamos a hacer una función para instalar la GDT. Antes deberemos haber declarado las estructuras, ahora vamos a inicializarlas.

Así conseguimos el construir el puntero que va hacia nuestra tabla de 3 entradas.

Ahora definimos una función común para poner los datos en las entradas

Y la llamamos 3 veces desde la función de instalar

Por último debemos decirle al procesador que tenemos una GDT, para que la cargue, y en nuestro caso al cargar el kernel con GRUB, sobreescribir la GDT de GRUB. Para cargar la GDT existe una instrucción en asm llamada lgdt (o lgdtl dependiendo de la sintaxis), vamos a usarla.

Bien una vez hayamos terminado esto nuestro sistema ya contará con GDT. En el siguiente capítulo veremos la IDT, una tabla muy parecida a la GDT pero con interrupciones. Yo he puesto unos mensajes de estado y confirmación con la GDT así que NextDivel ahora luce así:

NextDivel-GDT

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (III)

Este artículo lo escribí para el blog en español DesdeLinux el 1 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

NextDivel-3

Continuamos esta serie de posts sobre cómo crear nuestro sistema operativo. Hoy no nos vamos a centrar en un tema sino que vamos a definir algunas funciones útiles de ahora en adelante. En primer lugar vamos a definir 3 funciones que cumplan la función de memcpy, memset y memcmp:

Todas ellas se auto-implementan. Estas funciones yo las he sacado de una pequeña librería del C, la implementación suele ser parecida en todos los sistemas operativos. Ahora vamos a hacer 3 funciones simulares pero para manipular strings. Cumplirían la función de strcpy, strcat y strcmp.

Vamos ahora con unas funciones bastante interesantes. Con estas funciones podremos leer y escribir en los puertos del hardware. Esto normalmente se hace con ASM y corresponde (en x86) a las instrucciones in y out. Para llamar de una manera fácil a ASM desde C se usa la instrucción asm, con el peligro que conlleva de que no es portable. A esta sentencia le añadimos el volatile para que GCC no intente optimizar ese texto. Por otra parte la instrucción asm tiene una forma curiosa de aceptar parámetros, pero eso creo que se entiende mejor viendo los ejemplos.

Y hasta aquí el post 3, hoy no hemos hecho nada vistoso pero sí hemos definido una funciones que nos vendrán bien de cara a un futuro. Aviso a los usuarios de 64 bits que estoy trabajando en solucionar un bug que impide compilar correctamente en 64 bits. En el siguiente post veremos un componente importante de la arquitectura x86, la GDT.

Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (II)

Este artículo lo escribí para el blog en español DesdeLinux el 29 de diciembre de 2013 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.

Bienvenidos a otro post sobre como crear nuestro propio sistema operativo, en este caso NextDivel.

Si retomamos el código del primer post al final de todo nos debería haber salido algo como esto:

NextDivel-1

Si esto es correcto podemos continuar. Voy a usar el sistema y la estructura que tengo en GitHub (http://github.com/AdrianArroyoCalle/next-divel) ya que es más cómodo para mí y para vosotros. Como se puede apreciar el texto es un texto básico, no resulta atractiv0. Puede parecer algo más del montón. Pero como dice el dicho, para gustos colores, y en nuestro sistema operativo habrá colores. Los primeros colores que vamos a poder poner van a ser los que definen las tarjetas VGA y son 16:

  1. Negro
  2. Azul
  3. Verde
  4. Cyan
  5. Rojo
  6. Magenta
  7. Marrón
  8. Gris claro
  9. Gris oscuro
  10. Azul claro
  11. Verde claro
  12. Cyan claro
  13. Rojo claro
  14. Magenta claro
  15. Marrón claro
  16. Blanco

Estos colores los vamos a definir en un header para tenerlo más a mano y quizá en un futuro formar parte de la API del sistema. Así creamos el archivo ND_Colors.hpp en el include de NextDivel.

A su vez vamos a definir nuevas funciones para escribir en pantalla de una manera más cómoda (no, todavía no vamos a implementar printf, sé que lo estais deseando). Crearemos un archivo y su header para un set de funciones relacionadas con la pantalla (NDScreen.cpp y NDScreen.hpp). En ellas vamos a crear funciones para: cambiar el color de las letras y el fondo, escribir frases y letras, limpiar la pantalla y desplazarnos por la pantalla. Seguimos usando las pantallas VGA pero ahora usaremos unos bytes que darán el color. ND_Screen.cpp quedaría como:

El header será muy básico así que no lo incluyo aquí, pero destacar la definición del tipo ND_SIDE

También mencionar que hacemos uso del header NDTypes.hpp, este header nos define unos tipos básicos para uint8t, uint16t, etc basado en los char y los int. Realmente este header es el en el estándar C99 y de hecho mi NDTypes.hpp es un copia/pega del archivo desde Linux, así que podeis intercambiarlos y no pasaría nada (solo hay definiciones, ninguna función).

Para probar si este código funciona vamos a modificar el punto de entrada en C del kernel:

Y si seguimos estos pasos obtendríamos este resultado

NextDivel-3

Gracias a estas funciones que hemos creado podemos empezar a hacer pequeñas GUI, como por ejemplo un kernel panic que mostraremos cada vez que haya un error irrecuperable. Algo tal que así:

NextDivel-4

Y esta pequeña GUI la hicimos solamente con estas funciones:

Y aprovecho para daros las gracias por la excelente acogida que tuvo el primer post.