Dentro del núcleo Linux 2.4 - Arrancando: sector de arranque y configuración
Tutorial creado por Tigran Aivazian. Extraido de: http://es.tldp.org/Manuales-LuCAS/DENTRO-NUCLEO-LINUX/dentro-nucleo-linux-html/
14 de Febrero de 2006
Linux
4 - Arrancando: sector de arranque y configuración
El sector de arranque usado para arrancar el núcleo Linux puede ser uno de los siguientes:
Consideraremos aquí el sector de arranque de Linux en detalle. Las primeras lineas inicializan las macros convenientes para ser usadas por los valores de segmento:
29 SETUPSECS = 4 /* tamaño por defecto de los sectores de configuración */
30 BOOTSEG = 0x07C0 /* dirección original del sector de arranque */
31 INITSEG = DEF_INITSEG /* movemos el arranque aquí - lejos del camino */
32 SETUPSEG = DEF_SETUPSEG /* la configuración empieza aquí */
33 SYSSEG = DEF_SYSSEG /* el sistema es cargado en 0x10000 (65536) */
34 SYSSIZE = DEF_SYSSIZE /* tamaño del sistema: # de palabras de 16 bits */
(los números a la izquierda son los números de linea del archivo bootsect.S) Los valores de DEF_INITSEG, DEF_SETUPSEG, DEF_SYSSEG y DEF_SYSSIZE son tomados desde include/asm/boot.h:
/* No toques esto, a menos que realmente sepas lo que estás haciendo. */
#define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000
#define DEF_SETUPSEG 0x9020
#define DEF_SYSSIZE 0x7F00
Ahora, consideremos el código actual de bootsect.S:
54 movw $BOOTSEG, %ax
55 movw %ax, %ds
56 movw $INITSEG, %ax
57 movw %ax, %es
58 movw $256, %cx
59 subw %si, %si
60 subw %di, %di
61 cld
62 rep
63 movsw
64 ljmp $INITSEG, $go
65 # bde - cambiado 0xff00 a 0x4000 para usar el depurador después de 0x6400 (bde).
66 # No tendríamos que preocuparnos por esto si chequeamos el límite superior
67 # de la memoria. También mi BIOS puede ser configurada para poner las tablas
68 # wini de controladoras en la memoria alta en vez de en la tabla de vectores.
69 # La vieja pila quizás tenga que ser insertada en la tabla de controladores.
70 go: movw $0x4000-12, %di # 0x4000 es un valor arbitrario >=
71 # longitud de sector de arranque + longitud de la
72 # configuración + espacio para la pila;
73 # 12 es el tamaño parm del disco.
74 movw %ax, %ds # ax y es ya contienen INITSEG
75 movw %ax, %ss
76 movw %di, %sp # pone la pila en INITSEG:0x4000-12.
Las lineas 54-63, mueven el código del sector de arranque desde la dirección 0x7C00 a 0x90000. Esto es realizado de la siguiente manera:
El motivo por el que este código no usa rep movsd es intencionado (hint - .code16).
La línea 64 salta a la etiqueta go: en la nueva copia hecha del sector de arranque, esto es, en el segmento 0x9000. Esto y las tres instruciones siguientes (lineas 64-76) preparan la pila en $INITSEG:0x4000-0xC, esto es, %ss = $INITSEG (0x9000) y %sp = 0x3FF4 (0x4000-0xC). Aquí es de dónde viene el límite del tamaño de la configuración que mencionamos antes (ver Construyendo la Imagen del Núcleo Linux).
Las lineas 77-103 parchean la tabla de parámetros del disco para el primer disco para permitir lecturas multi-sector:
77 # Las tablas por defecto de parámetros del disco de muchas BIOS
78 # no reconocerán lecturas multi-sector más allá del número máximo especificado
79 # en las tablas de parámetros del diskette por defecto - esto
80 # quizás signifique 7 sectores en algunos casos.
82 # Como que las lecturas simples de sectores son lentas y fuera de la cuestión
83 # tenemos que tener cuidado con esto creando nuevas tablas de parámetros
84 # (para el primer disco) en la RAM. Estableceremos la cuenta máxima de sectores
85 # a 36 - el máximo que encontraremos en un ED 2.88.
86 #
87 # Lo grande no hace daño. Lo pequeño si.
88 #
89 # Los segmentos son como sigue: ds = es = ss = cs - INITSEG, fs = 0,
90 # y gs queda sin usar.
91 movw %cx, %fs # establece fs a 0
92 movw $0x78, %bx # fs:bx es la dirección de la tabla de parámetros
93 pushw %ds
94 ldsw %fs:(%bx), %si # ds:si es el código
95 movb $6, %cl # copia 12 bytes
96 pushw %di # di = 0x4000-12.
97 rep # no necesita cld -> hecho en la linea 66
98 movsw
99 popw %di
100 popw %ds
101 movb $36, 0x4(%di) # parchea el contador de sectores
102 movw %di, %fs:(%bx)
103 movw %es, %fs:2(%bx)
El controlador de diskettes es reinicializado usando el servicio de la BIOS int 0x13 función 0 (reinicializa FDC) y los sectores de configuración son cargados inmediatamente después del sector de arranque, esto es, en la dirección física 0x90200 ($INITSEG:0x200), otra vez usando el servicio de la BIOS int 0x13, función 2 (leer sector(es)). Esto sucede durante las lineas 107-124:
107 load_setup:
108 xorb %ah, %ah # reinicializa FDC
109 xorb %dl, %dl
110 int $0x13
111 xorw %dx, %dx # controladora 0, cabeza 0
112 movb $0x02, %cl # sector 2, pista 0
113 movw $0x0200, %bx # dirección = 512, en INITSEG
114 movb $0x02, %ah # servicio 2, "leer sector(es)"
115 movb setup_sects, %al # (asume todos en la cabeza 0, pista 0)
116 int $0x13 # los lee
117 jnc ok_load_setup # ok - continua
118 pushw %ax # vuelca el código de error
119 call print_nl
120 movw %sp, %bp
121 call print_hex
122 popw %ax
123 jmp load_setup
124 ok_load_setup:
Si la carga falla por alguna razón (floppy defectuoso o que alguien quitó el diskette durante la operación), volcamos el código de error y se intenta en un bucle infinito. La única forma de salir de él es reiniciando la máquina, a menos que los reintentos tengan éxito, pero usualmente no lo tienen (si algo está mal sólo se pondrá peor).
Si la carga de los sectores setup_sects del código de configuración es realizada con éxito, saltamos a la etiqueta ok_load_setup:.
Entonces procedemos a cargar la imagen comprimida del núcleo en la dirección física 0x10000. Esto es realizado para preservar las áreas de datos del firmware en la memoria baja (0-64K). Después de que es cargado el núcleo, saltamos a $SETUPSEG:0(##arch/i386/boot/setup.S). Una vez que los datos no se necesitan mas (ej. no se realizan más llamadas a la BIOS) es sobreescrito moviendo la imagen entera (comprimida) del núcleo desde 0x10000 a 0x1000 (direcciones físicas, por supuesto). Esto es hecho por setup.S, el cual prepara las cosas para el modo protegido y salta a 0x1000, que es el comienzo del núcleo comprimido, esto es, arch/386/boot/compressed/{head.S,misc.c}. Esto inicializa la pila y llama a decompress_kernel(), que descomprime el núcleo en la dirección 0x100000 y salta a ella.
Destacar que los viejos cargadores de arranque (viejas versiones de LILO) sólo podían cargar los 4 primeros sectores de la configuración, el cual es el motivo por el que existe código en la configuración para cargar el resto de si mismo si se necesita. También, el código en la configuración tiene que tener cuidado de varias combinaciones de tipo/versión del cargador vs zImage/bzImage y esto es altamente complejo.
Examinemos este truco en el código del sector de arranque que nos permite cargar un núcleo grande, también conocido como "bzImage".
Los sectores de configuración son cargados usualmente en la dirección 0x90200, pero el núcleo es cargado en fragmentos de 64k cada vez usando una rutina de ayuda especial que llama a la BIOS para mover datos desde la memoria baja a la memoria alta. Esta rutina de ayuda es referida por bootsect_kludge en bootsect.S y es definida como bootsect_helper en setup.S. La etiqueta bootsect_kludge en setup.S contiene el valor del segmento de configuración y el desplazamiento del código bootsect_helper en él, por lo que el sector de arranque puede usar la instrucción lcall para saltar a él (salto entre segmentos). El motivo por lo cual esto es realizado en setup.S es simplemente porque no existe más espacio libre en bootsect.S (lo cual no es estrictamente verdad - hay aproximadamente 4 bytes dispersos y al menos 1 byte disperso en bootsect.S, pero que obviamente no es suficiente). Esta rutina usa el servicio de la BIOS int 0x15 (ax=0x8700) para moverlo a la memoria alta y restablecer %es al punto de siempre 0x10000. Esto asegura que el código en bootsect.S no se va fuera de memoria cuando está copiando datos desde disco.
- Sector de arranque de Linux (arch/i386/boot/bootsect.S),
- Sector de arranque de LILO (u otros cargadores de arranque) o
- sin sector de arranque (loadlin, etc)
Consideraremos aquí el sector de arranque de Linux en detalle. Las primeras lineas inicializan las macros convenientes para ser usadas por los valores de segmento:
29 SETUPSECS = 4 /* tamaño por defecto de los sectores de configuración */
30 BOOTSEG = 0x07C0 /* dirección original del sector de arranque */
31 INITSEG = DEF_INITSEG /* movemos el arranque aquí - lejos del camino */
32 SETUPSEG = DEF_SETUPSEG /* la configuración empieza aquí */
33 SYSSEG = DEF_SYSSEG /* el sistema es cargado en 0x10000 (65536) */
34 SYSSIZE = DEF_SYSSIZE /* tamaño del sistema: # de palabras de 16 bits */
(los números a la izquierda son los números de linea del archivo bootsect.S) Los valores de DEF_INITSEG, DEF_SETUPSEG, DEF_SYSSEG y DEF_SYSSIZE son tomados desde include/asm/boot.h:
/* No toques esto, a menos que realmente sepas lo que estás haciendo. */
#define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000
#define DEF_SETUPSEG 0x9020
#define DEF_SYSSIZE 0x7F00
Ahora, consideremos el código actual de bootsect.S:
54 movw $BOOTSEG, %ax
55 movw %ax, %ds
56 movw $INITSEG, %ax
57 movw %ax, %es
58 movw $256, %cx
59 subw %si, %si
60 subw %di, %di
61 cld
62 rep
63 movsw
64 ljmp $INITSEG, $go
65 # bde - cambiado 0xff00 a 0x4000 para usar el depurador después de 0x6400 (bde).
66 # No tendríamos que preocuparnos por esto si chequeamos el límite superior
67 # de la memoria. También mi BIOS puede ser configurada para poner las tablas
68 # wini de controladoras en la memoria alta en vez de en la tabla de vectores.
69 # La vieja pila quizás tenga que ser insertada en la tabla de controladores.
70 go: movw $0x4000-12, %di # 0x4000 es un valor arbitrario >=
71 # longitud de sector de arranque + longitud de la
72 # configuración + espacio para la pila;
73 # 12 es el tamaño parm del disco.
74 movw %ax, %ds # ax y es ya contienen INITSEG
75 movw %ax, %ss
76 movw %di, %sp # pone la pila en INITSEG:0x4000-12.
Las lineas 54-63, mueven el código del sector de arranque desde la dirección 0x7C00 a 0x90000. Esto es realizado de la siguiente manera:
- establece %ds:%si a $BOOTSEG:0 (0x7C0:0 = 0x7C00)
- establece %es:%di a $INITSEG:0 (0x9000:0 = 0x90000)
- establece el número de palabras de 16 bits en %cx (256 palabras = 512 bytes = 1 sector)
- limpia la bandera DF (dirección) en EFLAGS a direcciones auto-incrementales (cld)
- va allí y copia 512 bytes (rep movsw)
El motivo por el que este código no usa rep movsd es intencionado (hint - .code16).
La línea 64 salta a la etiqueta go: en la nueva copia hecha del sector de arranque, esto es, en el segmento 0x9000. Esto y las tres instruciones siguientes (lineas 64-76) preparan la pila en $INITSEG:0x4000-0xC, esto es, %ss = $INITSEG (0x9000) y %sp = 0x3FF4 (0x4000-0xC). Aquí es de dónde viene el límite del tamaño de la configuración que mencionamos antes (ver Construyendo la Imagen del Núcleo Linux).
Las lineas 77-103 parchean la tabla de parámetros del disco para el primer disco para permitir lecturas multi-sector:
77 # Las tablas por defecto de parámetros del disco de muchas BIOS
78 # no reconocerán lecturas multi-sector más allá del número máximo especificado
79 # en las tablas de parámetros del diskette por defecto - esto
80 # quizás signifique 7 sectores en algunos casos.
82 # Como que las lecturas simples de sectores son lentas y fuera de la cuestión
83 # tenemos que tener cuidado con esto creando nuevas tablas de parámetros
84 # (para el primer disco) en la RAM. Estableceremos la cuenta máxima de sectores
85 # a 36 - el máximo que encontraremos en un ED 2.88.
86 #
87 # Lo grande no hace daño. Lo pequeño si.
88 #
89 # Los segmentos son como sigue: ds = es = ss = cs - INITSEG, fs = 0,
90 # y gs queda sin usar.
91 movw %cx, %fs # establece fs a 0
92 movw $0x78, %bx # fs:bx es la dirección de la tabla de parámetros
93 pushw %ds
94 ldsw %fs:(%bx), %si # ds:si es el código
95 movb $6, %cl # copia 12 bytes
96 pushw %di # di = 0x4000-12.
97 rep # no necesita cld -> hecho en la linea 66
98 movsw
99 popw %di
100 popw %ds
101 movb $36, 0x4(%di) # parchea el contador de sectores
102 movw %di, %fs:(%bx)
103 movw %es, %fs:2(%bx)
El controlador de diskettes es reinicializado usando el servicio de la BIOS int 0x13 función 0 (reinicializa FDC) y los sectores de configuración son cargados inmediatamente después del sector de arranque, esto es, en la dirección física 0x90200 ($INITSEG:0x200), otra vez usando el servicio de la BIOS int 0x13, función 2 (leer sector(es)). Esto sucede durante las lineas 107-124:
107 load_setup:
108 xorb %ah, %ah # reinicializa FDC
109 xorb %dl, %dl
110 int $0x13
111 xorw %dx, %dx # controladora 0, cabeza 0
112 movb $0x02, %cl # sector 2, pista 0
113 movw $0x0200, %bx # dirección = 512, en INITSEG
114 movb $0x02, %ah # servicio 2, "leer sector(es)"
115 movb setup_sects, %al # (asume todos en la cabeza 0, pista 0)
116 int $0x13 # los lee
117 jnc ok_load_setup # ok - continua
118 pushw %ax # vuelca el código de error
119 call print_nl
120 movw %sp, %bp
121 call print_hex
122 popw %ax
123 jmp load_setup
124 ok_load_setup:
Si la carga falla por alguna razón (floppy defectuoso o que alguien quitó el diskette durante la operación), volcamos el código de error y se intenta en un bucle infinito. La única forma de salir de él es reiniciando la máquina, a menos que los reintentos tengan éxito, pero usualmente no lo tienen (si algo está mal sólo se pondrá peor).
Si la carga de los sectores setup_sects del código de configuración es realizada con éxito, saltamos a la etiqueta ok_load_setup:.
Entonces procedemos a cargar la imagen comprimida del núcleo en la dirección física 0x10000. Esto es realizado para preservar las áreas de datos del firmware en la memoria baja (0-64K). Después de que es cargado el núcleo, saltamos a $SETUPSEG:0(##arch/i386/boot/setup.S). Una vez que los datos no se necesitan mas (ej. no se realizan más llamadas a la BIOS) es sobreescrito moviendo la imagen entera (comprimida) del núcleo desde 0x10000 a 0x1000 (direcciones físicas, por supuesto). Esto es hecho por setup.S, el cual prepara las cosas para el modo protegido y salta a 0x1000, que es el comienzo del núcleo comprimido, esto es, arch/386/boot/compressed/{head.S,misc.c}. Esto inicializa la pila y llama a decompress_kernel(), que descomprime el núcleo en la dirección 0x100000 y salta a ella.
Destacar que los viejos cargadores de arranque (viejas versiones de LILO) sólo podían cargar los 4 primeros sectores de la configuración, el cual es el motivo por el que existe código en la configuración para cargar el resto de si mismo si se necesita. También, el código en la configuración tiene que tener cuidado de varias combinaciones de tipo/versión del cargador vs zImage/bzImage y esto es altamente complejo.
Examinemos este truco en el código del sector de arranque que nos permite cargar un núcleo grande, también conocido como "bzImage".
Los sectores de configuración son cargados usualmente en la dirección 0x90200, pero el núcleo es cargado en fragmentos de 64k cada vez usando una rutina de ayuda especial que llama a la BIOS para mover datos desde la memoria baja a la memoria alta. Esta rutina de ayuda es referida por bootsect_kludge en bootsect.S y es definida como bootsect_helper en setup.S. La etiqueta bootsect_kludge en setup.S contiene el valor del segmento de configuración y el desplazamiento del código bootsect_helper en él, por lo que el sector de arranque puede usar la instrucción lcall para saltar a él (salto entre segmentos). El motivo por lo cual esto es realizado en setup.S es simplemente porque no existe más espacio libre en bootsect.S (lo cual no es estrictamente verdad - hay aproximadamente 4 bytes dispersos y al menos 1 byte disperso en bootsect.S, pero que obviamente no es suficiente). Esta rutina usa el servicio de la BIOS int 0x15 (ax=0x8700) para moverlo a la memoria alta y restablecer %es al punto de siempre 0x10000. Esto asegura que el código en bootsect.S no se va fuera de memoria cuando está copiando datos desde disco.
Valora este capítulo:
Autor y licencia de 'Dentro del núcleo Linux 2.4 - Arrancando: sector de arranque y configuración'
|
Opiniona sobre 'Dentro del núcleo Linux 2.4 - Arrancando: sector de arranque y configuración' (4)
Tu nombre debe tener tres caracteres como mínimo.
Es necesario que te des de alta con una cuenta de correo válida.
Es necesario que te des de alta con una cuenta de correo válida.
El contenido del título de tu opinión debe tener tres caracteres como mínimo.
Es obligatorio que selecciones una valoración del recurso.
El contenido del comentario de tu opinión debe tener tres caracteres como mínimo.
Opina sobre este tutorial |
Wikis relacionados con 'Dentro del núcleo Linux 2.4 - Arrancando: sector de arranque y configuración'
Este documento describe cómo hacer el enmascarado (masqueradinq), proxy transparente, reenvío de puertos (port forwarding),...
Más »
Aprenderás a acelerar el arranque de tu ordenador y a eliminar algunos virus que se...
Más »
Truco para conseguir crear una imagen de arranque en Linux utilizando Frame Buffer.
La fijación de precios está convirtiéndose en un modo de vida para muchos minoristas y...
Más »
Los discos de arranque de Linux, aparte de poder ser usados en el proceso de...
Más »
