Laboratorio de Sistemas Operativos Primer Parcial Fecha de entrega: Martes, 13 de octubre de 2009 Planteamiento del problema Se deberá desarrollar un sector de arranque y un minikernel que permita realizar las siguientes funciones: Leer dos cadenas de caracteres X e Y, Calcular e imprimir la longitud de las dos cadenas en Hexadecimal, Determinar si las dos cadenas son iguales. En caso que las cadenas no sean iguales, imprimir la parte restante de la cadena de mayor longitud. Entrar en un ciclo infinito cuando cualquiera de las dos cadenas (X o Y) sea ‘exit’ Para leer las cadenas se podrá utilizar repetidamente la interrupción de la BIOS 16h (para leer un carácter), hasta encontrar el carácter de fin de linea (0x0D). Se deberá adicionar el carácter nulo (0x00) al final de cada cadena leida. Las operaciones que impliquen la pantalla deberán ser realizadas utilizando directamente la memoria de video (que se encuentra en la dirección de memoria 0xB8000). Igualmente, el paso de parámetros (para las rutinas de imprimir carácter / cadena de caracteres) se deberá realizar utilizando la pila. El sector de arranque (de 512 bytes) y el minikernel (que debe ocupar exactamente diecisiete (17) sectores de 512 bytes: el primer sector de 512 bytes corresponde al bootsector, los siguientes 8 sectores corresponden al código del kernel, y los siguientes 8 sectores corresponden a los datos del kernel. El sector de arranque deberá contener la lógica necesaria para copiarse a sí mismo a la dirección de memoria 0x800 (ver anexo : mapa de memoria en modo real) y continuar su ejecución en esa nueva ubicación. Luego deberá cargar el minikernel de la imagen de diskette (que se encuentra a partir del sector 2), por medio de interrupciones de la BIOS (int 13h). Este kernel contendrá las rutinas requeridas. Una vez que el sector de arranque haya terminado de cargar el kernel (en la dirección 0x1000), le deberá transferir el control (por medio de una instrucción ljmp). Condiciones de entrega Se deberá entregar un archivo comprimido (en formato zip) que contenga una carpeta con nombre login1_login2 (donde login1_login2 son los login de usuario del correo electronico de los integrantes del grupo). Esta carpeta deberá contener los siguientes archivos: README.txt: archivo que contiene el nombre y el código de los integrantes. También puede incluir aclaraciones, comentarios inconvenientes y soluciones relacionadas con el problema. bochsrc.txt: archivo de configuración de bochs para ejecutar la imagen de disco creada boot.s: código fuente del sector de arranque kernel.s: código fuente del kernel Makefile: archivo de configuración de la utilidad make, que deberá contener como mínimo las siguientes reglas: o all: crear la imagen de disco (floppy) o boot.o: compilar el archivo boot.S o kernel.o: compilar el archivo kernel.S o run: ejecutar la imagen de disco o clean: borrar los archivos de compilación y la imagen de disco Calificación La lista de verificación que se usará para calificar el parcial es la siguiente: LISTA DE VERIFICACIÓN 1. Aspectos generales CUMPLE (S/N) 1.1 (10 puntos) El programa se entregó de acuerdo con las condiciones estipuladas y el la fecha indicada 1.2 (2 puntos) El programa incluye documentación que indica los autores, la fecha de creación y el problema que soluciona 1.3 (2 puntos) El programa se encuentra estructurado (en rutinas) de acuerdo con las características del problema 1.4 (2 puntos) Las rutinas se encuentran correctamente documentadas en términos de parámetros de entrada y descripción general de su funcionamiento 1.5 (2 puntos) Las secciones críticas del programa se encuentran documentadas 1.6 (2 punto) El código fuente se encuentra estructurado correctamente (por ejemplo, permite ver con claridad el inicio y fin de las rutinas y los ciclos) 2. Funcionamiento del programa 2.1 (30 puntos) El sector de arranque se copia en la posición 0x800 (20 puntos), carga el kernel de disco a la posición 0x1000 y le pasa el control al kernel (10 puntos) 2.2 (50 puntos) El kernel implementa en forma correcta la funcionalidad requerida en el planteamiento del problema: Leer dos líneas (5 puntos), calcular e imprimir la longitud de las lineas en hexadecimal (5 puntos), determinar si las cadenas son iguales e imprimir la parte restante de la cadena de mayor longitud (25 puntos), el kernel entra en un ciclo infinito si X o Y son ‘exit’ (15 puntos) En caso que alguna de las características no se cumpla completamente, se podrá asignar una calificación proporcional con su cumplimiento. Anexo. Documentación básica para la solución del problema A continuación se presentan algunos tips básicos que se deben considerar para la solución del problema. Si se desea mayor profundidad en los temas presentados, se deberá recurrir a consultas externas. Proceso general de arranque del computador Cuando se enciende el computador o se envía la señal RESET a la unidad de control, el procesador ejecuta la instrucción que se encuentra en el límite de la memoria RAM. Esta dirección generalmente es mapeada a la BIOS, que a su vez busca el primer sector del dispositivo configurado como de arranque. Si no lo encuentra en este dispositivo, continúa con el siguiente dispositivo hasta encontrar un sector de arranque valido. Si no puede encontrar ningún sector de arranque valido, imprime un mensaje de error y detiene su ejecución. El sector de arranque (de 512 bytes) es cargado por la BIOS a la dirección de memoria 0x7C00h (31744 bytes), que es el límite de memoria de 32 K (31744 + 512 = 32256 = 32K). Luego le pasa el control de la ejecución a este sector de arranque. Además, la BIOS almacena en los 8 bits menos significativos del registro EDX (es decir, en DL) un valor que indica la unidad desde la cual se cargo el sector de arranque, el cual puede ser: 1h = floppy 80h = disco duro primario. Debido al tamaño limitado que tiene un sector de arranque (512 bytes incluyendo la tabla de particiones), este sólo contiene la funcionalidad necesaria para continuar con la carga del kernel. De esta forma, cargar un kernel completo generalmente se realiza por medio de pequeños programas que se cargan y se transfieren el control en forma secuencial (el primero de ellos es lógicamente el sector de arranque). A este proceso e le conoce como “arranque por fases”. Todo sector de arranque tiene un tamaño predeterminado de 512 bytes, y sus últimos 2 bytes (510 y 511) deberán contener la palabra AA55H, que se conoce como la "firma del sector de arranque", o bootsector signature. Modo real (Procesadores x86) Cuando un procesador x86 inicia (luego de PWR o RESET), siempre lo hace en modo real, en el cual se comporta básicamente como un 8086 con un conjunto de instrucciones muy limitado, y en el cual sólo son válidas operaciones de 16 bits. Adicionalmente, en modo real sólo es posible acceder al primer MegaByte de memoria RAM, debido a que la línea de dirección 20 (conocida como la puerta A20) se encuentra desactivada. Un esquema de los registros del procesador en modo real se muestra a continuación: MODO REAL (16 bits) 15 Registros de propósito general (No se usa en modo real) Registros de segmento 0 15 31 0 %cs (e) %ax %ss (e) %bx %ds (e) %cx %es (e) %dx %fs (e) %si %gs (e) %di Direccionamiento de memoria en modo real Debido a que en modo real todo procesador se comporta como un 8086 (registros de 16 bits), con estos registros sólo es posible referenciar regiones de memoria de 64 KB (cuando todos los bits de un registro de 16 bits se encuentran en 1, la máxima dirección de memoria es 216 = 65536 = 64 KB). Los diseñadores de Intel desarrollaron una estrategia que permitía acceder hasta 1 MB de memoria, denominada segmentación. La segmentación se implementa por medio de dos clases de registros: los registros de segmento (%cs para el segmento de código, %ss para la pila, %ds para el segmento de datos, y %es, %fs y %gs como segmentos adicionales de datos) y los registros de propósito general (%ax, %bx, %cx, %dx, %si y %di). Los valores de estos registros se combinan de la siguiente forma: Ejemplo: para la dirección 0x7C00 Reg. de segmento Reg. de propósito general * 16 + %ds 0x7C0 * 16 %si 0x00 + = Dirección física (20 bits) %ds:%si 0x7C00 Por ejemplo, para referenciar la dirección de memoria en la cual se carga el sector de arranque (0x7C00), haciendo uso del registro de segmento %ds (datos), y del registro de propósito general %si, %ds deberá contener 0x7C0 y %si deberá contener 0x00. Al multiplicar %ds por 16 (que en un número hexadecimal implica desplazar un dígito a la izquierda), se obtiene: %ds*16 + %si = (0x7C0 * 16) + 0x00 = 0x7C00 + 0x00 = 0x7C00. De la misma forma, para referenciar la dirección inicial de la memoria de video (0xB8000), por ejemplo se puede hacer uso de los registros %es y %di de la siguiente forma: %es = 0xB800, %di = 0x00, entonces: %es:%di = %es * 16 + %di = (0xB800 * 16) + 0x00 = 0xB8000. Se puede ver que la dirección física obtenida contiene 20 bits, con la siguiente gráfica: Segmento * 16 Desplazamiento Dirección física Es importante resaltar que no es conveniente modificar (asignar un valor) directamente en un registro de segmento. Generalmente, primero se asigna el valor al registro %ax, y luego el valor se pasa de %ax al registro de segmento indicado. Por ejemplo, para mover 0xB800 al registro %es, se deben realizar los siguientes pasos1: movw $0xB800, %ax movw %ax, %es Mapa de memoria en modo real Cuando el computador arranca y pasa el control a la BIOS, ésta realiza un diagnóstico inicial del hardware y configura el primer MegaByte de memoria RAM como se presenta a continuación (las direcciones se presentan en formato hexadecimal). Inicio 00 Fin 3FF 400 500 4FF 7BFF 1 Tamaño 1 KB Descripción Tabla de interrupciones del modo real 255 bytes Área de datos de la BIOS 30643 bytes (aprox Memoria convencional (usable). En 59 sectores, 30 KB) esta región se deberá copiar el El sufijo “w” en las instrucciones mov indica que se está almacenando un word. Los sufijos válidos en la sintaxis de GNU Assembler son b para bytes (8 bits), w para word (2 bytes = 16 bits)y l para long (4 bytes = 32 bits). En modo protegido se puede usar máximo 16 bits. 7C00 7DFF 512 bytes 7E00 80000 9FC00 A0000 7FFF 9FBFF 9FFFF FFFFF 480 KB (aprox) 120 KB (aprox) 1 KB 384 KB (aprox) sector de arranque y el minikernel. Posición de memoria en la cual la BIOS carga el sector de arranque Memoria convencional (usable) Memoria convencional (usable) Área de datos de la BIOS extendida Área de ROM A su vez, el área de ROM (ubicada de de A0000 a FFFF) se divide de la siguiente forma: Inicio A0000 B0000 Fin AFFFF B7FFF Tamaño 64 KB 32 KB B8000 BFFFF 32 KB C0000 C8000 C7000 EFFFF 32 KB 160 KB F0000 FFFFF 64 KB Descripción Framebuffer VGA Memoria de video en modo texto, monocromático Memoria de video en modo texto, a color BIOS de Video (ROM) Hardware mapeado en memoria y dispositivos varios BIOS Memoria de video La memoria de video en modo texto a color (25 líneas de 80 caracteres cada una) inicia en la dirección 0xB8000, y ocupa 32 KB de memoria. Cada carácter en que se muestra en la pantalla ocupa 2 bytes en la memoria de video (un word): un byte contiene el código ASCII del carácter, y el otro byte contiene los atributos de color de texto y color de fondo del carácter. A su vez, el byte para los atributos de texto (color de texto y de fondo) se divide en fracciones, que representan el color de texto y el color de fondo. Esto quiere decir que en modo texto a color se tienen 16 combinaciones diferentes (2 4 = 16) para los colores de texto y de fondo. Lo anterior se ilustra en el siguiente esquema: Atributos de texto Carácter ASCII 0 15 b = blink b fondo texto De esta forma, para mostrar un carácter en la esquina superior de la pantalla (línea 0, carácter 0) se deberá escribir un word (2 bytes) en la dirección de memoria 0xB8000. El primer byte de este word será el código ascii a mostrar, y los siguientes bytes representarán el color de texto y de fondo del carácter. El siguiente word (ubicado en la dirección de memoria 0xB8002) corresponde al segundo carácter en la pantalla, y así sucesivamente. Los colores válidos se muestran en la siguiente tabla2: Valor Color Valor Color 0 black 8 dark gray 1 blue 9 bright blue 2 green 10 bright green 3 cyan 11 bright cyan 4 red 12 pink 5 magenta 13 bright magenta 6 brown 14 yellow 7 white 15 bright white Los colores 0-15 son válidos para el color de texto. Sin embargo para el fondo solo es posible utilizar los colores del 0 al 7, debido al que el 8 bit se utiliza para el “blink” (texto que aparece y desaparece). Por ejemplo, para imprimir el carácter ‘A’, (al cual le corresponde el código ASCII 65, 0x41 en hexadecimal) con color blanco sobre fondo negro en la esquina superior de la pantalla, se deberá copiar el word 0x0741 en la dirección de memoria 0xB8000. Se puede observar la posición XY de un carácter en la pantalla se puede obtener de la siguiente forma: Pos_XY = 0xB8000 + ((80*Y) + X) *2 En donde 0xB8000 es la dirección base de la memoria de video (esquina superior izquierda), Y representa la fila, X representa la columna. Se debe multiplicar por 2 debido a que cada carácter en la pantalla en realidad ocupa 2 bytes, uno para el código ascii y otro para los atributos de color de texto y fondo. Servicios de la BIOS La BIOS (Basic Input/Output System) ofrece una serie de servicios que pueden ser utilizados en modo real. Los servicios necesarios para este parcial se explican en forma general a continuación3: Servicio INT Leer sectores de un 13h drive 2 Descripción Los registros deberán contener los siguientes valores: tomada de http://my.execpc.com/~geezer/osd/cons/index.htm Aquí se utiliza la notación ##h para representar un número hexadecimal. En GNU Assembler, la instrucción para invocar la interrupción 13h sería: int $0x13 Para una lista completa de los servicios de la BIOS, consultar http://en.wikipedia.org/wiki/BIOS_call 3 AH = 02h (Read sectors from drive) AL = Número de sectores a leer CH = 8 bits menos significativos del cilindro CL = (bits 0-5) número del sector, (bits 6-7) 2 bits más significativos del cilindro DH = Cabeza DL = Dispositivo del cual se desean leer los sectores (0x00 = floppy) ES:BX deberá apuntar a la posición de memoria en la cual se desean almacenar los lectores leídos Este servicio guarda en AL el número de sectores leídos, y establece el Flag de Acarreo en 0 si la lectura fue correcta, o 1 si se presentó algún error. Lee una tecla 16h AH = 00h (Read Character) Este servicio guarda el character leído en AX: AH contiene el scan code AL contiene el código ASCII del carácter que se leyó. Aunque no es un problema para este parcial, es importante resaltar que el servicio de la BIOS para leer sectores de disco funciona con una geometría de Cilindros, Cabezas (Heads) y Sectores (C/H/S). Por esta razón tiene algunas limitaciones: Debido a que el número de la cabeza se almacena en DL (registro de 8 bits), sólo es posible referenciar de la cabeza 0 a la cabeza 255 (28 = 256 cabezas). Igualmente, como sólo se cuenta con 10 bits para el cilindro (8 bits en CH y 2 bits en CL), sólo se puede referenciar del cilindro 0 al 1023 (210 = 1024 cilindros). De la misma forma, solo se cuenta con 6 bits para el número del sector, por lo cual sólo es posible referenciar del sector 0 al sector 63 (26 = 64). De esta forma, los servicios de la BIOS sólo permiten referenciar los primeros 8.5 GB de un disco duro: (Cilindros * Heads * Sectores) * 512 = 8.5 GB Como se mencionó anteriormente, cuando se utiliza el servicio de leer sectores de un drive, la BIOS guarda en AL el número de sectores leídos, y establece el Flag de Acarreo en 0 si la lectura fue correcta, o 1 si se presentó algún error. Generalmente no se deberían presentar errores al leer el kernel, pero es aconsejable validar si la lectura fue correcta.