Laboratorio de Sistemas Operativos Primer Parcial Fecha de entrega: Jueves 14 de octubre de 2010 Planteamiento del problema Se deberá desarrollar un sector de arranque y un minikernel que lea una tabla de particiones de una imagen de disco duro, y que imprima la información de las particiones descritas en dicha tabla. El sector de arranque y el mini-kernel se ubicarán en una imagen de floppy, dese la cual debe arrancar el emulador (bochs o qemu). El sector de arranque, leído por la BIOS a la posición de memoria 0x7C001 deberá copiarse a sí mismo a la posición de memoria 0x500 (la primera posición de memoria disponible en modo real), y luego deberá leer el mini-kernel que se encuentra contiguo al sector de arranque en la imagen de diskette a la posición de memoria 0x1000. El mini-kernel leído deberá implementar la funcionalidad básica para leer a RAM el primer sector de una imagen de disco duro (suministrada por el profesor). De este primer sector deberá extraer la tabla de particiones (1), (2) que se encuentra en una posición fija dentro del primer sector. Ver documentación adicional suministrada. De la tabla de particiones extraída, el mini-kernel deberá interpretar cada entrada (que corresponde a una partición) e imprimir por pantalla la estructura de cada una de ellas. Esta se compone de: Tipo de partición Inicio de la partición Fin de la partición Tamaño de la partición Para imprimir la información por pantalla, el mini-kernel no podrá usar los servicios de la BIOS. En vez de ello, el mini-kernel deberá imprimir los caracteres directamente en la memoria de video que se encuentra mapeada a la RAM, como se explica en la documentación adicional suministrada. Se puede tomar como base para resolver el parcial el código del proyecto „05_load_kernel‟ de los ejemplos de modo real para el aprendizaje de sistemas operativos. 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 electrónico de los integrantes del grupo). Esta carpeta deberá contener los siguientes archivos: 1 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. La representación numérica 0X#### se usa para expresar un número en formato hexadecimal. También se puede utilizar la representación ####h ó ####H. bochsrc.txt: archivo de configuración de bochs para ejecutar la imagen de disco creada bootsect.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 bochs: arrancar el emulador bochs para ejecutar el bootsector creado o qemu: arrrancar el emulador qemu para ejecutar el bootsector creado 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 (5 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 (15 puntos) El sector de arranque se copia en la posición 0x500, y una vez copiado carga el mini-kernel de la imagen de floppy a la posición 0x1000 y le pasa el control al kernel 2.2 (70 puntos) El kernel implementa en forma correcta la funcionalidad requerida en el planteamiento del problema: (15 puntos)Leer el primer sector de una imagen de disco duro a memoria RAM (15 puntos) Extraer la tabla de particiones del primer sector (40 puntos)Para cada entrada de la tabla de particiones, imprimir o Tipo de partición o Inicio o Fin o Tamaño 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 aspectos 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 asesoría o a la revisión de las referencias y el código suministrado por el profesor en la página web de la asignatura (3). 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 0x7C00 (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 cargó 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, la carga de un sistema operativo 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 0xAA55, 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) se establece un entorno de ejecución de 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 DS (E) BX ES (E) CX FS (E) DX GS (E) SI (E) DI (E) SP (E) BP SS Organización 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 2 16 = 65535). Los diseñadores de Intel desarrollaron una estrategia que permitía acceder hasta 1 MB de memoria, basada en el concepto de 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, además de SP y BP). Los valores de estos registros se combinan para crear una dirección lógica (de la forma segmento : offset), que permite referenciar cualquier dirección lineal desde 0 hasta 1 Megabye. Con el fin de permitir que el procesador pudiera acceder a un mayor espacio de memoria, el valor que se almacena en los registos de segmento (la dirección lineal de inicio del segmento en memoria) se encuentra dividido por 16. La siguiente figura ilustra cómo se puede obtener una dirección lineal (de memoria) a partir de una dirección lógica. Ejemplo: para la dirección 0x7C00 Dir. Lógica (seg:offset) Reg. de segmento * 16 Reg. de propósito general + DS 0x7C0 * 16 SI 0x00 + = Dirección lineal (20 bits) 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 * 0X10) + 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 * 0X10 + %di = (0xB800 * 0X10) + 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 Como se mencionó en clase, la misma dirección lineal (físca) en modo real se puede obtener usando 4096 combinaciones diferentes de valores para segmento y desplazamiento. Es importante resaltar que no se puede asignar un valor directamente en un registro de de adtps. Para lograr este propósito, 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 pasos en ensamblador (sintaxis Intel): mov ax, 0xB800 mov es, ax 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). 00 Inicio Fin 3FF Tamaño 400 500 4FF 7BFF 255 bytes 30643 bytes (aprox 59 sectores, 30 KB) 7C00 7DFF 512 bytes 7E00 80000 9FC00 A0000 7FFF 9FBFF 9FFFF FFFFF 480 KB (aprox) 120 KB (aprox) 1 KB 384 KB (aprox) 1 KB Descripción Tabla de interrupciones del modo real Área de datos de la BIOS Memoria convencional (usable). En esta región se deberá copiar el 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 64 KB 32 KB Tamaño 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 en modo texto La memoria de video en modo texto a color (que en la pantalla se dispone en 25 líneas de 80 caracteres cada una) inicia en la dirección 0xB8000, y es un espacio lineal que 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. La siguiente figura ilustra cómo se encuentra mapeada la memoria de video a la dirección de memoria RAM 0xB8000. Pantalla RAM 0 A B … … Atributos de texto 0xB8002 0x42 = „B‟ 0xB8001 Atributos de texto 0xB8000 0x41 = „A‟ 80x25 Bootsector 0x7C00 IVT 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 (4 bits cada uno). Esto quiere decir que en modo texto a color se tienen 16 combinaciones diferentes (24 = 16) para los colores de texto y de fondo. Lo anterior se ilustra en el siguiente esquema: Atributos de texto Carácter ASCII 7 15 0 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*i) + j) *2 En donde 0xB8000 es la dirección base de la memoria de video (esquina superior izquierda), i representa la fila, j 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 dispositivo 2 Descripción Los registros deberán contener los siguientes valores: Información extractada de http://files.osdev.org/mirrors/geezer/osd/cons/index.htm. Aquí se utiliza la notación ##h para representar un número hexadecimal. En GNU Assembler (sintaxis Intel), la instrucción para invocar la interrupción 13h sería: int 0x13 Para una lista completa de los servicios de la BIOS, consultar el enlace a la documentación presentado en la página de la asignatura. 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, 0x80=disco duro) ES:BX deberá contener la dirección lógica de 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ó. 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 (2 8 = 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 (2 10 = 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 (2 6 = 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 Bit de Acarreo dentro del registro FLAGS 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. Tabla de particiones La tabla de particiones se encuentra dentro del primer sector de la imagen de disco, a un desplazamiento exacto de 446 bytes (0x1BE) desde el inicio del sector. Una tabla de particiones tiene el siguiente formato (1), (2): Desplazamiento dentro del sector Hex Decimal 0x1BE 446 0x1CE 462 0x1DE 478 0x1EE 494 Tamaño (bytes) 16 16 16 16 Descripción Descriptor de la primera partición Descriptor de la segunda partición Descriptor de la tercera partición Descriptor de la cuarta partición Cada entrada de la tabla describe una partición, por lo cual solo es posible tener cuatro particiones primarias en un disco duro4. Descriptores de partición Cada descriptor ocupa exactamente 16 bytes, y describe las siguientes características de una partición: Indicador de arranque (1 byte). Indica si la partición está habilitada para que desde ella la BIOS lea el sector de arranque. En cualquier momento sólo puede existir una partición de arranque. Los valores válidos son: 0x00 (inactiva), y 0x80 (arranque) Inicio de la partición (3 bytes): Contiene la dirección de inicio de la partición, en formato C/H/S. Tipo de la partición (1 byte): Describe el tipo del sistema de archivos presente en la partición. Fin de la partición (3 bytes): Contiene la dirección de fin de la partición, en formato C/H/S Sector de inicio de la partición (4 bytes): Contiene el sector absoluto de inicio de la partición (en formato LBA). Para un disco con N sectores, este atributo tiene un valor entre 0 (primer sector) y N-1 (último sector) Tamaño de la partición (4 bytes). Especifica el tamaño en sectores de la partición. Para mayor información acerca de la tabla de particiones, consultar las referencias (1) y (2). Bibliografía 1. Partition Table - OsDevWiki. [En línea] [Citado el: 30 de Septiembre de 2010.] http://wiki.osdev.org/Partition_Table. 2. Sedory, Daniel B. MBR/EBR Partition Tables. [En línea] 2004-2007. [Citado el: 30 de Septiembre de 2010.] http://mirror.href.com/thestarman/asm/mbr/PartTables.htm. 3. Meza, Erwin. Laboratorio de Sistemas Operativos. [En línea] 2010. [Citado el: 30 de Septiembre de 2010.] http://artemisa.unicauca.edu.co/~emezav/operativos_lab. 4 No obstante, una de ellas puede ser de un tipo especial, llamado “partición extendida”, y dentro de esta partición extendida es posible definir hasta 24 “sub-particiones” llamadas “particiones lógicas”. Por esta razón es posible crear más de cuatro particiones en un disco duro.