Format markdown

This commit is contained in:
Philipp Oppermann
2025-03-27 17:52:48 +01:00
parent 88fd5aabdd
commit 52b31ded4d
10 changed files with 91 additions and 91 deletions

View File

@@ -30,12 +30,12 @@ Este blog se desarrolla abiertamente en [GitHub]. Si tienes algún problema o pr
## El Buffer de Texto VGA
Para imprimir un carácter en la pantalla en modo de texto VGA, uno tiene que escribirlo en el buffer de texto del hardware VGA. El buffer de texto VGA es un arreglo bidimensional con típicamente 25 filas y 80 columnas, que se renderiza directamente en la pantalla. Cada entrada del arreglo describe un solo carácter de pantalla a través del siguiente formato:
Bit(s) | Valor
------ | ----------------
0-7 | Código de punto ASCII
8-11 | Color de primer plano
12-14 | Color de fondo
15 | Parpadeo
| Bit(s) | Valor |
| ------ | --------------------- |
| 0-7 | Código de punto ASCII |
| 8-11 | Color de primer plano |
| 12-14 | Color de fondo |
| 15 | Parpadeo |
El primer byte representa el carácter que debe imprimirse en la [codificación ASCII]. Para ser más específicos, no es exactamente ASCII, sino un conjunto de caracteres llamado [_página de códigos 437_] con algunos caracteres adicionales y ligeras modificaciones. Para simplificar, procederemos a llamarlo un carácter ASCII en esta publicación.
@@ -44,16 +44,16 @@ El primer byte representa el carácter que debe imprimirse en la [codificación
El segundo byte define cómo se muestra el carácter. Los primeros cuatro bits definen el color de primer plano, los siguientes tres bits el color de fondo, y el último bit si el carácter debe parpadear. Los siguientes colores están disponibles:
Número | Color | Número + Bit de Brillo | Color Brillante
------ | ---------- | ---------------------- | -------------
0x0 | Negro | 0x8 | Gris Oscuro
0x1 | Azul | 0x9 | Azul Claro
0x2 | Verde | 0xa | Verde Claro
0x3 | Cian | 0xb | Cian Claro
0x4 | Rojo | 0xc | Rojo Claro
0x5 | Magenta | 0xd | Magenta Claro
0x6 | Marrón | 0xe | Amarillo
0x7 | Gris Claro | 0xf | Blanco
| Número | Color | Número + Bit de Brillo | Color Brillante |
| ------ | ---------- | ---------------------- | --------------- |
| 0x0 | Negro | 0x8 | Gris Oscuro |
| 0x1 | Azul | 0x9 | Azul Claro |
| 0x2 | Verde | 0xa | Verde Claro |
| 0x3 | Cian | 0xb | Cian Claro |
| 0x4 | Rojo | 0xc | Rojo Claro |
| 0x5 | Magenta | 0xd | Magenta Claro |
| 0x6 | Marrón | 0xe | Amarillo |
| 0x7 | Gris Claro | 0xf | Blanco |
Bit 4 es el _bit de brillo_, que convierte, por ejemplo, azul en azul claro. Para el color de fondo, este bit se reutiliza como el bit de parpadeo.

View File

@@ -46,28 +46,28 @@ Para ver la lista completa de excepciones, consulte la [wiki de OSDev][exception
### La tabla de descriptores de interrupción
Para poder capturar y manejar excepciones, tenemos que configurar una llamada _tabla de descriptores de interrupción_ (IDT). En esta tabla, podemos especificar una función manejadora para cada excepción de CPU. El hardware utiliza esta tabla directamente, por lo que necesitamos seguir un formato predefinido. Cada entrada debe tener la siguiente estructura de 16 bytes:
Tipo| Nombre | Descripción
----|--------------------------|-----------------------------------
u16 | Puntero a función [0:15] | Los bits más bajos del puntero a la función manejadora.
u16 | Selector GDT | Selector de un segmento de código en la [tabla de descriptores global].
u16 | Opciones | (ver abajo)
u16 | Puntero a función [16:31] | Los bits del medio del puntero a la función manejadora.
u32 | Puntero a función [32:63] | Los bits restantes del puntero a la función manejadora.
u32 | Reservado |
| Tipo | Nombre | Descripción |
| ---- | ------------------------- | ----------------------------------------------------------------------- |
| u16 | Puntero a función [0:15] | Los bits más bajos del puntero a la función manejadora. |
| u16 | Selector GDT | Selector de un segmento de código en la [tabla de descriptores global]. |
| u16 | Opciones | (ver abajo) |
| u16 | Puntero a función [16:31] | Los bits del medio del puntero a la función manejadora. |
| u32 | Puntero a función [32:63] | Los bits restantes del puntero a la función manejadora. |
| u32 | Reservado |
[tabla de descriptores global]: https://en.wikipedia.org/wiki/Global_Descriptor_Table
El campo de opciones tiene el siguiente formato:
Bits | Nombre | Descripción
------|-----------------------------------|-----------------------------------
0-2 | Índice de tabla de pila de interrupción | 0: No cambiar pilas, 1-7: Cambiar a la n-ésima pila en la Tabla de Pila de Interrupción cuando se llama a este manejador.
3-7 | Reservado |
8 | 0: Puerta de interrupción, 1: Puerta de trampa | Si este bit es 0, las interrupciones están deshabilitadas cuando se llama a este manejador.
9-11 | debe ser uno |
12 | debe ser cero |
1314 | Nivel de privilegio del descriptor (DPL) | El nivel mínimo de privilegio requerido para llamar a este manejador.
15 | Presente |
| Bits | Nombre | Descripción |
| ----- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| 0-2 | Índice de tabla de pila de interrupción | 0: No cambiar pilas, 1-7: Cambiar a la n-ésima pila en la Tabla de Pila de Interrupción cuando se llama a este manejador. |
| 3-7 | Reservado |
| 8 | 0: Puerta de interrupción, 1: Puerta de trampa | Si este bit es 0, las interrupciones están deshabilitadas cuando se llama a este manejador. |
| 9-11 | debe ser uno |
| 12 | debe ser cero |
| 1314 | Nivel de privilegio del descriptor (DPL) | El nivel mínimo de privilegio requerido para llamar a este manejador. |
| 15 | Presente |
Cada excepción tiene un índice de IDT predefinido. Por ejemplo, la excepción de código de operación inválido tiene índice de tabla 6 y la excepción de fallo de página tiene índice de tabla 14. Así, el hardware puede cargar automáticamente la entrada de IDT correspondiente para cada excepción. La [Tabla de Excepciones][exceptions] en la wiki de OSDev muestra los índices de IDT de todas las excepciones en la columna “Vector nr.”.
@@ -162,10 +162,10 @@ En contraste, una función llamada puede sobrescribir registros _de uso_ sin res
En x86_64, la convención de llamada C especifica los siguientes registros preservados y de uso:
registros preservados | registros de uso
---|---
`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
_guardados por el llamado_ | _guardados por el llamador_
| registros preservados | registros de uso |
| ----------------------------------------------- | ----------------------------------------------------------- |
| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` |
| _guardados por el llamado_ | _guardados por el llamador_ |
El compilador conoce estas reglas, por lo que genera el código en consecuencia. Por ejemplo, la mayoría de las funciones comienzan con un `push rbp`, que respalda `rbp` en la pila (porque es un registro guardado por el llamado).

View File

@@ -127,10 +127,10 @@ Por ejemplo, ¿qué ocurre si:
Afortunadamente, el manual de AMD64 ([PDF][AMD64 manual]) tiene una definición exacta (en la Sección 8.2.9). Según él, una “excepción de doble fallo _puede_ ocurrir cuando una segunda excepción ocurre durante el manejo de un controlador de excepción previo (primera)”. El _“puede”_ es importante: Solo combinaciones muy específicas de excepciones conducen a un doble fallo. Estas combinaciones son:
Primera Excepción | Segunda Excepción
------------------|------------------
[División por cero],<br>[TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General] | [TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General]
[Fallo de Página] | [Fallo de Página],<br>[TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General]
| Primera Excepción | Segunda Excepción |
| ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| [División por cero],<br>[TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General] | [TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General] |
| [Fallo de Página] | [Fallo de Página],<br>[TSS No Válido],<br>[Segmento No Presente],<br>[Fallo de Segmento de Pila],<br>[Fallo de Protección General] |
[División por cero]: https://wiki.osdev.org/Exceptions#Division_Error
[TSS No Válido]: https://wiki.osdev.org/Exceptions#Invalid_TSS
@@ -218,15 +218,15 @@ En `x86_64`, la TSS ya no contiene ninguna información específica de tarea. En
La TSS de 64 bits tiene el siguiente formato:
Campo | Tipo
------ | ----------------
<span style="opacity: 0.5">(reservado)</span> | `u32`
Tabla de Pilas de Privilegio | `[u64; 3]`
<span style="opacity: 0.5">(reservado)</span> | `u64`
Tabla de Pila de Interrupciones | `[u64; 7]`
<span style="opacity: 0.5">(reservado)</span> | `u64`
<span style="opacity: 0.5">(reservado)</span> | `u16`
Dirección Base del Mapa de E/S | `u16`
| Campo | Tipo |
| --------------------------------------------- | ---------- |
| <span style="opacity: 0.5">(reservado)</span> | `u32` |
| Tabla de Pilas de Privilegio | `[u64; 3]` |
| <span style="opacity: 0.5">(reservado)</span> | `u64` |
| Tabla de Pila de Interrupciones | `[u64; 7]` |
| <span style="opacity: 0.5">(reservado)</span> | `u64` |
| <span style="opacity: 0.5">(reservado)</span> | `u16` |
| Dirección Base del Mapa de E/S | `u16` |
La _Tabla de Pilas de Privilegio_ es usada por la CPU cuando cambia el nivel de privilegio. Por ejemplo, si ocurre una excepción mientras la CPU está en modo usuario (nivel de privilegio 3), la CPU normalmente cambia a modo núcleo (nivel de privilegio 0) antes de invocar el controlador de excepciones. En ese caso, la CPU cambiaría a la 0ª pila en la Tabla de Pilas de Privilegio (ya que 0 es el nivel de privilegio de destino). Aún no tenemos programas en modo usuario, así que ignoraremos esta tabla por ahora.

View File

@@ -277,16 +277,16 @@ pub fn _print(args: fmt::Arguments) {
Bloquea el `WRITER`, llama a `write_fmt` en él y lo desbloquea implícitamente al final de la función. Ahora imagina que una interrupción ocurre mientras `WRITER` está bloqueado y el manejador de interrupciones intenta imprimir algo también:
Timestep | _start | manejador_interrupcion
---------|------|------------------
0 | llama a `println!` | &nbsp;
1 | `print` bloquea `WRITER` | &nbsp;
2 | | **ocurre la interrupción**, el manejador comienza a ejecutarse
3 | | llama a `println!` |
4 | | `print` intenta bloquear `WRITER` (ya bloqueado)
5 | | `print` intenta bloquear `WRITER` (ya bloqueado)
… | | …
_nunca_ | _desbloquear `WRITER`_ |
| Timestep | _start | manejador_interrupcion |
| -------- | ------------------------ | -------------------------------------------------------------- |
| 0 | llama a `println!` | &nbsp; |
| 1 | `print` bloquea `WRITER` | &nbsp; |
| 2 | | **ocurre la interrupción**, el manejador comienza a ejecutarse |
| 3 | | llama a `println!` |
| 4 | | `print` intenta bloquear `WRITER` (ya bloqueado) |
| 5 | | `print` intenta bloquear `WRITER` (ya bloqueado) |
| … | | … |
| _nunca_ | _desbloquear `WRITER`_ |
El `WRITER` está bloqueado, así que el manejador de interrupciones espera hasta que se libere. Pero esto nunca sucede, porque la función `_start` solo continúa ejecutándose después de que el manejador de interrupciones regrese. Así, todo el sistema se cuelga.

View File

@@ -204,21 +204,21 @@ Como se indica por el atributo `repr`, las tablas de páginas necesitan estar al
Cada entrada tiene un tamaño de 8 bytes (64 bits) y tiene el siguiente formato:
Bit(s) | Nombre | Significado
------ | ---- | -------
0 | presente | la página está actualmente en memoria
1 | escribible | se permite escribir en esta página
2 | accesible por el usuario | si no se establece, solo el código en modo núcleo puede acceder a esta página
3 | caché de escritura a través | las escrituras van directamente a la memoria
4 | desactivar caché | no se utiliza caché para esta página
5 | accedido | la CPU establece este bit cuando se utiliza esta página
6 | sucio | la CPU establece este bit cuando se realiza una escritura en esta página
7 | página enorme/null | debe ser 0 en P1 y P4, crea una página de 1&nbsp;GiB en P3, crea una página de 2&nbsp;MiB en P2
8 | global | la página no se borra de las cachés al cambiar el espacio de direcciones (el bit PGE del registro CR4 debe estar establecido)
9-11 | disponible | puede ser utilizado libremente por el sistema operativo
12-51 | dirección física | la dirección física alineada de 52 bits del marco o de la siguiente tabla de páginas
52-62 | disponible | puede ser utilizado libremente por el sistema operativo
63 | no ejecutar | prohibir la ejecución de código en esta página (el bit NXE en el registro EFER debe estar establecido)
| Bit(s) | Nombre | Significado |
| ------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| 0 | presente | la página está actualmente en memoria |
| 1 | escribible | se permite escribir en esta página |
| 2 | accesible por el usuario | si no se establece, solo el código en modo núcleo puede acceder a esta página |
| 3 | caché de escritura a través | las escrituras van directamente a la memoria |
| 4 | desactivar caché | no se utiliza caché para esta página |
| 5 | accedido | la CPU establece este bit cuando se utiliza esta página |
| 6 | sucio | la CPU establece este bit cuando se realiza una escritura en esta página |
| 7 | página enorme/null | debe ser 0 en P1 y P4, crea una página de 1&nbsp;GiB en P3, crea una página de 2&nbsp;MiB en P2 |
| 8 | global | la página no se borra de las cachés al cambiar el espacio de direcciones (el bit PGE del registro CR4 debe estar establecido) |
| 9-11 | disponible | puede ser utilizado libremente por el sistema operativo |
| 12-51 | dirección física | la dirección física alineada de 52 bits del marco o de la siguiente tabla de páginas |
| 52-62 | disponible | puede ser utilizado libremente por el sistema operativo |
| 63 | no ejecutar | prohibir la ejecución de código en esta página (el bit NXE en el registro EFER debe estar establecido) |
Vemos que solo los bits 1251 se utilizan para almacenar la dirección física del marco. Los bits restantes se utilizan como banderas o pueden ser utilizados libremente por el sistema operativo. Esto es posible porque siempre apuntamos a una dirección alineada a 4096 bytes, ya sea a una tabla de páginas alineada a la página o al inicio de un marco mapeado. Esto significa que los bits 011 son siempre cero, por lo que no hay razón para almacenar estos bits porque el hardware puede simplemente configurarlos en cero antes de usar la dirección. Lo mismo es cierto para los bits 5263, ya que la arquitectura x86_64 solo admite direcciones físicas de 52 bits (similar a como solo admite direcciones virtuales de 48 bits).

View File

@@ -172,13 +172,13 @@ Ahora podemos calcular direcciones virtuales para las tablas de los cuatro nivel
La tabla a continuación resume la estructura de la dirección para acceder a los diferentes tipos de marcos:
Dirección Virtual para | Estructura de Dirección ([octal])
------------------- | -------------------------------
Página | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE`
Entrada de Tabla de Nivel 1 | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD`
Entrada de Tabla de Nivel 2 | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC`
Entrada de Tabla de Nivel 3 | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB`
Entrada de Tabla de Nivel 4 | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA`
| Dirección Virtual para | Estructura de Dirección ([octal]) |
| --------------------------- | --------------------------------- |
| Página | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` |
| Entrada de Tabla de Nivel 1 | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` |
| Entrada de Tabla de Nivel 2 | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` |
| Entrada de Tabla de Nivel 3 | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` |
| Entrada de Tabla de Nivel 4 | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` |
[octal]: https://en.wikipedia.org/wiki/Octal