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

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:

void ND::IRQ::Remap(int pic1, int pic2)
{
#define PIC1 0x20
#define PIC2 0xA0
#define ICW1 0x11
#define ICW4 0x01
/* send ICW1 */
ND::Ports::OutputB(PIC1, ICW1);
ND::Ports::OutputB(PIC2, ICW1);
/* send ICW2 */
ND::Ports::OutputB(PIC1 + 1, pic1); /* remap */
ND::Ports::OutputB(PIC2 + 1, pic2); /* pics */
/* send ICW3 */
ND::Ports::OutputB(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
ND::Ports::OutputB(PIC2 + 1, 2);
/* send ICW4 */
ND::Ports::OutputB(PIC1 + 1, ICW4);
ND::Ports::OutputB(PIC2 + 1, ICW4);
/* disable all IRQs */
ND::Ports::OutputB(PIC1 + 1, 0xFF);
}

Ahora creamos una función para instalar los IRQ:

void ND::IRQ::Install()
{
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);
ND::Screen::PutString("\nInstalling IRQ...");
ND::IRQ::Remap(0x20,0x28);
ND::IDT::SetGate(32,(unsigned)ND::IRQ::IRQ1,0x08,0x8E);
ND::IDT::SetGate(33,(unsigned)ND::IRQ::IRQ2,0x08,0x8E);
ND::IDT::SetGate(34,(unsigned)ND::IRQ::IRQ3,0x08,0x8E);
ND::IDT::SetGate(35,(unsigned)ND::IRQ::IRQ4,0x08,0x8E);
ND::IDT::SetGate(36,(unsigned)ND::IRQ::IRQ5,0x08,0x8E);
ND::IDT::SetGate(37,(unsigned)ND::IRQ::IRQ6,0x08,0x8E);
ND::IDT::SetGate(38,(unsigned)ND::IRQ::IRQ7,0x08,0x8E);
ND::IDT::SetGate(39,(unsigned)ND::IRQ::IRQ8,0x08,0x8E);
ND::IDT::SetGate(40,(unsigned)ND::IRQ::IRQ9,0x08,0x8E);
ND::IDT::SetGate(41,(unsigned)ND::IRQ::IRQ10,0x08,0x8E);
ND::IDT::SetGate(42,(unsigned)ND::IRQ::IRQ11,0x08,0x8E);
ND::IDT::SetGate(43,(unsigned)ND::IRQ::IRQ12,0x08,0x8E);
ND::IDT::SetGate(44,(unsigned)ND::IRQ::IRQ13,0x08,0x8E);
ND::IDT::SetGate(45,(unsigned)ND::IRQ::IRQ14,0x08,0x8E);
ND::IDT::SetGate(46,(unsigned)ND::IRQ::IRQ15,0x08,0x8E);
ND::IDT::SetGate(47,(unsigned)ND::IRQ::IRQ16,0x08,0x8E);
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);
ND::Screen::PutString("done");
asm volatile("sti");
}

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:

void ND::IRQ::IRQ1()
{
asm volatile(
"cli \n"
"pushl 0\n"
"pushl 32\n"
"jmp ND_IRQ_Common"
);
}

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

extern "C"
void ND_IRQ_Common()
{
asm volatile(
"pusha \n"
"push %ds\n"
"push %es\n"
"push %fs\n"
"push %gs\n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_IRQ_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}

Y un handler básico:

extern "C"
void ND_IRQ_Handler(struct regs* r)
{
void (*handler)(struct regs *r);
if(r->int_no >= 40)
{
ND::Ports::OutputB(0xA0,0x20);
}
ND::Ports::OutputB(0x20,0x20);
}

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.


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

Comparte para difundir

Si te ha gustado nuestro contenido ahora puedes ayudar a difundirlo en las redes sociales de manera sencilla usando los siguientes botones:

Envía
Pinea
Print

22 comentarios

  1.   aitor_cz dijo

    Muchas gracias, Adrian, en cuanto tenga algo de tiempo (ahora estoy bastante ocupado, entre otras cosas también con un sistema operativo) me pondré a probar el tutorial paso por paso.

  2.   Ruby dijo

    Muchas gracias por el tute.

  3.   sasuke dijo

    Una pregunta estoy haciendo un proyecto basado en estos lenguajes de programación
    *Html5
    *Css3
    *Java
    Mi pregunta es quisiera que este proyecto sea ejecutable osea que los usuarios lo usen en los sistemas operativos como linux y windows me podrían decir como hago eso

    1.    AdrianArroyoCalle dijo

      No tiene nada que ver con esto pero te respondo igualmente. Supongo que será HTML5, CSS3 y JavaScript no Java, ya que HTML5 y Java se llevan fatal. Con HTML5 puedes crear webs como hasta ahora que se accedan desde internet. Si la quieres hacer local la puedes empaquetar como app de Firefox OS y de Chrome OS. Si lo que quieres es que en cada sistema operativo haya un ejecutable hecha un vistazo entonces a XUL Runner que es una herramienta para ejecutar XUL (y por ende HTML5 dentro del elemento ) con el motor Gecko.

    2.    Soid Perez dijo

      Java frame o panel es una muy buena opcion yo e creado algunas app ejecuatbles en window usando las clases de java frame como navegador web pero en vez de usarlo para cualquier pagina le doy una ruta directa en el codigo y con php ejecuto sentencias de java atravez de un .jar que se encarga de acerlo. aunque te recomendaria usar HTML5, CSS3 y JavaScript ya que como dice Adrian Java se la lleva Fatal con Html5 y a mi me trajo muchos dolores de cabeza

  4.   lurban dijo

    Estaria bien un tutorial de como hacer tu propio lenguaje de programación

  5.   Iván dijo

    Muy buena esta serie de artículos sobre como se construye un sistema operativo, se aprenden muchas cosas. Estoy expectante a la siguiente entrada, ya tengo ganas de tener teclado en el SO. Estuve trasteando con el código del git, y no he conseguido hacerlo funcionar con el puerto 0x60 y el 0x64. Aún que pensé que para el teclado había una interrupción que te daba la tecla pulsada.

    1.    AdrianArroyoCalle dijo

      Realmente puedes obtener la entrada del teclado sin interrupciones, pero debes leer con ND::Ports::InputB en el puerto 0x60. Sin embargo la manera ideal de hacerlo es con interrupciones IRQ. Actualmente estoy tratando de hacerlo y estoy tardando un poco más en continuar debido a eso.

      1.    carlosorta dijo

        Hola Adrian, estuve checando el codigo y estoy impresionado con lo que hace y lo bien que me ha servido para entender algunas cosillas.

        Tengo unas pequeñas preguntillas acerca de su funcionamiento y la fuente es la que obtuve de tu GIT:

        1.- En la parte de los IRQ, mencionaste que se usaron las posiciones de la 0 a la 32 del IDT para excepciones y de la 32 (0x20) a la 45 (0x2D) para los IRQ, pero los IRQ son 16 en total, el remaping no seria de 0x20 a 0x30?

        2.- En la parte de los IRQ note que los setgates los mandaste a el apartado de IDT; cuando los separe note que ya no producia la excepcion de la division por 0 por lo que es necesario agregar el Flush() de IDT para cada modificacion que se le haga. ¿Por que al setear el Timer el IDT se altera y deja de funcionar la division entre 0?

        3.- Estuve tratando de trazar el codigo con alguna impresion para tomar como indicio de lo que va ejecutando (para no ir depurando paso a paso) y me di cuenta que ningun IRQ esta en funcionamiento, ¿Hay que dar de alta algo mas para que se generen las interrupciones de los IRQ?

        1.    carlosorta dijo

          olvide mencionar que encontre estas ligas con informacion:
          http://arstechnica.com/civis/viewtopic.php?f=20&t=899001
          http://www.superfrink.net/athenaeum/OS-FAQ/os-faq-pics.html
          http://orga2.exp.dc.uba.ar/data/downloads/clasespracticas/interrupciones2_clase_17.pdf
          http://www.intel-assembler.it/PORTALE/4/231468_8259A_PIC.pdf

          Al parecer para el manejo de las IRQ, es necesario tener en cuenta que tipo de manejador se usa si, PIC, APIC, IOAPIC. . .etc. Habra manera de hacer un dinamico del manejo de los IRQ o se necesita probar a suertes?

  6.   carlosorta dijo

    Buenas Tardes Adrian.

    Veia que tenia problemas con los IRQ y por ello no se podia avanzar en el codigo, saque una copia del proyecto y me puse a analizarlo; le agregue una funcion a la impresion de pantalla para imprimir los registros de struct reg, al momento de hacer las interrupciones; encontre varias cosillas, entre ellas que se esta corriendo un registro y aun no encuentro el porque; cambie la interrupcion del Timer por la interrupcion de teclado para hacer pruebas y hace lo que debe mas no encuentro el problema, usted podria ayudarme y continuar con este buen post? 😀

    Le dejo el link (tiene unas modificaciones porque uso Mageia y uso Grub2, estoy usando VirtualBox para testearlo)
    https://www.mediafire.com/?93psrgaoozatse8

    En espera de su atenta respuesta y si tiene alguna pregunta o necesita algo con gusto me gustaria ayudar 🙂

    1.    carlosorta dijo

      olvide mencionar que tambien cheque lo de los KernelPanic porque los ISR no funcionaban y tengo el mismo problema al tope de la pila se esta colando un valor y no se si es mi compilador o de plano existe un problema, uso GCC 4.8.2 con Mageia 4

    2.    AdrianArroyoCalle dijo

      Me agrada mucho que me ayudas en el proyecto. Realmente me quedé bloqueado en el Timer y no entiendo porque no llega a funcionar. Hice pruebas modificando bastantes cosas y no me llegó a salir. Actualmente no puedo editar código (estoy de vacaciones fuera) pero en cuanto pueda lo miraré bien. Te paso un link con información sobre este problema que al parecer es algo común: http://wiki.osdev.org/I_Cant_Get_Interrupts_Working

      Respecto a las excepciones, creo recordar que hay que hacer una llamada a “sti” en ASM para activarlas aunque está claro que hay algo que falla en alguna parte.

      1.    carlosorta dijo

        Gracias por tu respuesta y si, efectivamente. Las interrupciones estaban fallando pero era un problema al insertar los codigos en la pila y un problema de casting, checare el link y estare haciendo pruebas. Si lo soluciono te lo hago saber y sino te informo avances. Felices vacaciones 🙂

      2.    carlosorta dijo

        no habra manera de ver el codigo ensamblado?, esque algo extraño pasa y no encuentro el que?, mire este screen (pongo el link al final), es algo extraño ya que en la funcion de IRQ 2 (el teclado) mete en la pila el valor 0 y el 0x20 (32, asi lo acomode para probar) luego pushal (los registros GPR de 32 bits) seguidos de los registros de segmentacion y luego el tope de pila para despues mandar a llamar a IRQ Handler. Me puse a ver cada apilacion y aparentemente esta en el orden pero si logra observar el output de la VM se ve que apila un elemento mas, no logro encontrar donde, solo se que es un 0x10 y la estructura se desacomoda. Esta es la estructura de registros.

        struct regs{
        uint32_t gs, fs, es, ds; /* pushed the segs last */
        uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* pushed by ‘pushal’ */
        uint32_t int_no, err_code; /* Codigo de error */
        uint32_t eip, cs, eflags, useresp, ss; /* Apilado al interrumpir */
        };

        me da lata que pone un elemento mas al tope y no encuentro de donde se apila si aparentemente todo se apila como debe ser. Tiene alguna idea de que podria ser?

        http://www.subeimagenes.com/img/sin-titulo-1036729.jpg

  7.   carlosorta dijo

    Logre que funcionara la interrupcion del teclado mas no la del timer; el error estaba en la forma en la que se compila, use objdump para ver el objeto final y resultaba que cada funcion a la que se entraba aun usando “asm volatile” tambien iba acompañada de un pushl ebp, mov ebp, esp. Entonces solo agregarle un popl ebp para restaurar la pila inicial y se pasara los argumentos sin falla. Aqui adjunto el codigo para el que lo quiera checar y si logra descubrir porque no se genera interrupcion por el Timer me encantaria saberlo y le adjunto un link donde hablan sobre multitasking http://www.jamesmolloy.co.uk/tutorial_html/9.-Multitasking.html
    Next Divel
    https://www.mediafire.com/?kmo83dxyzc7c3cz

    1.    AdrianArroyoCalle dijo

      Lo conseguí. Era un fallo en otro sitio que afectaba a la activación del PIC del Timer, concretamente en el remap de IRQ había dos líneas que he tenido que modificar. Gracias a que pude ver un código en una web que en ese punto tenía algo diferente y ¡chas! El cambio era en la línea de:
      ND::Ports::OutputB(PIC1 + 1, 0xFF);
      ND::Ports::OutputB(PIC2 + 1, 0xFF);

      Había que cambiar el valor de 0xFF (diría yo que indica deshabilitado) por 0x00 (diría yo que indica habilitado) aunque no estoy muy seguro realmente, pero funciona. He actualizado el código en GitHub. Muchas, muchísimas gracias por haberme ayudado con el proyecto que tenía un poco abandonado debido a este problema. H

      1.    carlosorta dijo

        De nada, esperare con ansias la siguiente actualizacion del tema y cualquier cosa cuentas conmigo 🙂 (Y)

      2.    carlosorta dijo

        Cambia la rutina de captura de string del teclado; porque esta leyendo al momento de soltar la tecla y monta un 0 en el buffer, eso da problemas en la lectura y al final el ‘\n’ cambialo por ” para que funcione la impresion correcta

  8.   Soid Perez dijo

    Hola leí todo tu post aunque en la practica no pase del 2 post de verdad esta muy bueno lo guarde todo pero de verdad voy a necesitar estudiar c++ y posix para poder entenderlo porque se de “c” (Me fascina ese lenguaje de programación) pero aunque c++ es c OO realmente nunca e trabajado en el me lee unos tutoriales en google y luego regreso esta muy interesante y una pregunta ¿el arranque de window es parecido al de linux?

    1.    AdrianArroyoCalle dijo

      El arranque en Windows es parecido en el sentido de que esta es la manera de arrancar un sistema con procesador x86 y en eso influye poco el sistema operativo que se construye encima. De todos modos no estamos arrancando nosotros de verdad, está arrancando GRUB por nosotros. GRUB, diseñado para arrancar Linux, puede arrancar Windows y en este caso NextDivel.

      1.    soid dijo

        ok gracias eso quiere decir que lo que deseo hacer es posible ya estoy estudiando c++ y e creado algunas app y e instalado tu sistema en un pendrive y lo estoy estudiando mas detalladamente es muy buen post

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

*

  1. Responsable de los datos: Miguel Ángel Gatón
  2. Finalidad de los datos: Controlar el SPAM, gestión de comentarios.
  3. Legitimación: Tu consentimiento
  4. Comunicación de los datos: No se comunicarán los datos a terceros salvo por obligación legal.
  5. Almacenamiento de los datos: Base de datos alojada en Occentus Networks (UE)
  6. Derechos: En cualquier momento puedes limitar, recuperar y borrar tu información.