+
+Esta série de posts do blog cria um pequeno sistema operacional na [linguagem de programação Rust](https://www.rust-lang.org/). Cada post é um pequeno tutorial e inclui todo o código necessário, então você pode acompanhar se quiser. O código-fonte também está disponível no [repositório Github](https://github.com/phil-opp/blog_os) correspondente.
+
+Último post:
+
+
\ No newline at end of file
From 6bd79380d99ad64f84ac6aeb124b86d3e847e12a Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:03:59 -0300
Subject: [PATCH 02/26] [Translation][Portuguese pt-BR] post-1 (edition-2)
---
.../index.pt-BR.md | 597 ++++++++++++++++++
1 file changed, 597 insertions(+)
create mode 100644 blog/content/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md
new file mode 100644
index 00000000..9f340e11
--- /dev/null
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md
@@ -0,0 +1,597 @@
++++
+title = "Um Binário Rust Independente"
+weight = 1
+path = "pt-BR/freestanding-rust-binary"
+date = 2018-02-10
+
+[extra]
+# Please update this when updating the translation
+translation_based_on_commit = "624f0b7663daca1ce67f297f1c450420fbb4d040"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+O primeiro passo para criar nosso próprio kernel de sistema operacional é criar um executável Rust que não vincule a biblioteca padrão. Isso torna possível executar o código Rust no [bare metal] sem um sistema operacional subjacente.
+
+[bare metal]: https://en.wikipedia.org/wiki/Bare_machine
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na banch [`post-01`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-01
+
+
+
+## Introdução
+Para escrever um kernel de sistema operacional, precisamos de código que não dependa de nenhum recurso do sistema operacional. Isso significa que não podemos usar threads, arquivos, memória heap, rede, números aleatórios, saída padrão ou qualquer outro recurso que exija abstrações do sistema operacional ou hardware específico. O que faz sentido, já que estamos tentando escrever nosso próprio sistema operacional e nossos próprios drivers.
+
+Isso significa que não podemos usar a maior parte da [biblioteca padrão do Rust], mas há muitos recursos do Rust que _podemos_ usar. Por exemplo, podemos usar [iteradores], [closures], [pattern matching], [option] e [result], [formatação de string] e, claro, o [sistema de ownership]. Esses recursos tornam possível escrever um kernel de uma maneira muito expressiva e de alto nível, sem nos preocuparmos com [undefined behavior] ou [memory safety].
+
+[option]: https://doc.rust-lang.org/core/option/
+[result]:https://doc.rust-lang.org/core/result/
+[Rust standard library]: https://doc.rust-lang.org/std/
+[iteradores]: https://doc.rust-lang.org/book/ch13-02-iterators.html
+[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html
+[pattern matching]: https://doc.rust-lang.org/book/ch06-00-enums.html
+[formatação de string]: https://doc.rust-lang.org/core/macro.write.html
+[sistema de ownership]: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
+[undefined behavior]: https://www.nayuki.io/page/undefined-behavior-in-c-and-cplusplus-programs
+[memory safety]: https://tonyarcieri.com/it-s-time-for-a-memory-safety-intervention
+
+Para criar um kernel de sistema operacional em Rust, precisamos criar um executável que possa ser executado sem um sistema operacional subjacente. Esse executável é frequentemente chamado de executável “autônomo” ou “bare-metal”.
+
+Este post descreve as etapas necessárias para criar um binário Rust independente e explica por que essas etapas são necessárias. Se você estiver interessado apenas em um exemplo mínimo, pode **[ir para o sumário](#sumario)**.
+
+## Desativando a biblioteca padrão
+Por padrão, todos as crates Rust vinculam a [biblioteca padrão], que depende do sistema operacional para recursos como threads, arquivos ou rede. Ela também depende da biblioteca padrão C `libc`, que interage intimamente com os serviços do sistema operacional. Como nosso plano é escrever um sistema operacional, não podemos usar nenhuma biblioteca dependente de um sistema operacional.
+Portanto, temos que desativar a inclusão automática da biblioteca padrão por meio do [atributo `no_std`].
+
+[biblioteca padrão]: https://doc.rust-lang.org/std/
+[atributo `no_std`]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
+
+Começamos criando um novo projeto de binário cargo. A maneira mais fácil de fazer isso é através da linha de comando:
+
+```
+cargo new blog_os --bin --edition 2024
+```
+
+Eu nommei o projeto `blog_os`, mas claro que você pode escolher o seu próprio nome. A flag `--bin` especifica que queremos criar um executável binário (em contraste com uma biblioteca) e a flag `--edition 2024` especifica que queremos usar a [edição 2024] de Rust para nossa crate. Quando executamos o comando, o cargo cria a seguinte estrutura de diretório para nós:
+
+[edição 2024]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html
+
+```
+blog_os
+├── Cargo.toml
+└── src
+ └── main.rs
+```
+
+O `Cargo.toml` contém a configuração da crate, por exemplo o nome da crate, o autor, o número da [versão semântica] e dependências. O arquivo `src/main.rs` contém o módulo raiz da nossa crate e nossa função `main`. Você pode compilar sua crate através de `cargo build` e então executar o binário compilado `blog_os` na subpasta `target/debug`.
+
+[versão semântica]: https://semver.org/
+
+### O Atributo `no_std`
+
+Agora nossa crate implicitamente vincula a biblioteca padrão. Vamos tentar desativar isso adicionando o [atributo `no_std`]:
+
+```rust
+// main.rs
+
+#![no_std]
+
+fn main() {
+ println!("Olá, mundo!");
+}
+```
+
+Quando tentamos compilá-lo agora (executando `cargo build`), o seguinte erro ocorre:
+
+```
+error: cannot find macro `println!` in this scope
+ --> src/main.rs:4:5
+ |
+4 | println!("Olá, mundo!");
+ | ^^^^^^^
+```
+
+A razão deste erro é que a [macro `println`] é parte da biblioteca padrão, que não incluímos mais. Então não conseguimos mais imprimir coisas. Isso faz sentido, já que `println` escreve no [standard output], que é um descritor de arquivo especial fornecido pelo sistema operacional.
+
+[macro `println`]: https://doc.rust-lang.org/std/macro.println.html
+[standard output]: https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29
+
+Então vamos remover o println!() e tentar novamente com uma função main vazia:
+
+```rust
+// main.rs
+
+#![no_std]
+
+fn main() {}
+```
+
+```
+> cargo build
+error: `#[panic_handler]` function required, but not found
+error: language item required, but not found: `eh_personality`
+```
+
+Agora o compilador está pedindo uma função `#[panic_handler]` e um _item de linguagem_.
+
+## Implementação de Panic
+
+O atributo `panic_handler` define a função que o compilador deve invocar quando ocorre um [panic]. A biblioteca padrão fornece sua própria função de tratamento de panic, mas em um ambiente `no_std` precisamos defini-la nós mesmos:
+
+[panic]: https://doc.rust-lang.org/stable/book/ch09-01-unrecoverable-errors-with-panic.html
+
+```rust
+// in main.rs
+
+use core::panic::PanicInfo;
+
+/// Esta função é chamada em caso de pânico.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+O parâmetro [`PanicInfo`][PanicInfo] contém o arquivo e a linha onde o panic aconteceu e a mensagem de panic opcional. A função nunca deve retornar, então é marcada como uma [função divergente] ao retornar o [tipo “never”] `!`. Não há muito que possamos fazer nesta função por enquanto, então apenas fazemos um loop infinito.
+
+[PanicInfo]: https://doc.rust-lang.org/nightly/core/panic/struct.PanicInfo.html
+[função divergente]: https://doc.rust-lang.org/1.30.0/book/first-edition/functions.html#diverging-functions
+[tipo “never”]: https://doc.rust-lang.org/nightly/std/primitive.never.html
+
+## O Item de Linguagem `eh_personality`
+
+Items de linguagem são funções e tipos especiais necessários internamente pelo compilador. Por exemplo, a trait [`Copy`] é um item de linguagem que diz ao compilador quais tipos têm [_semântica de cópia_][`Copy`]. Quando olhamos para a [implementação][copy code], vemos que tem o atributo especial `#[lang = "copy"]` que o define como um item de linguagem (_Language Item_ em inglês).
+
+[`Copy`]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
+[copy code]: https://github.com/rust-lang/rust/blob/485397e49a02a3b7ff77c17e4a3f16c653925cb3/src/libcore/marker.rs#L296-L299
+
+Enquanto é possível fornecer implementações customizadas de items de linguagem, isso deve ser feito apenas como último recurso. A razão é que items de linguagem são detalhes de implementação altamente instáveis e nem mesmo verificados de tipo (então o compilador não verifica se uma função tem os tipos de argumento corretos). Felizmente, há uma forma mais estável de corrigir o erro de item de linguagem acima.
+
+O [item de linguagem `eh_personality`] marca uma função que é usada para implementar [stack unwinding]. Por padrão, Rust usa unwinding para executar os destructores de todas as variáveis da stack vivas em caso de [panic]. Isso garante que toda memória usada seja liberada e permite que a thread pai capture o panic e continue a execução. Unwinding, no entanto, é um processo complicado e requer algumas bibliotecas específicas do SO (por exemplo, [libunwind] no Linux ou [tratamento estruturado de exceção] no Windows), então não queremos usá-lo para nosso sistema operacional.
+
+[item de linguagem `eh_personality`]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
+[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
+[libunwind]: https://www.nongnu.org/libunwind/
+[tratamento estruturado de exceção]: https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
+
+### Desativando o Unwinding
+
+Existem outros casos de uso também para os quais unwinding é indesejável, então Rust fornece uma opção para [abortar no panic] em vez disso. Isso desativa a geração de informações de símbolo de desenrolar e reduz consideravelmente o tamanho do binário. Há múltiplos locais onde podemos desativar o unwinding. A forma mais fácil é adicionar as seguintes linhas ao nosso `Cargo.toml`:
+
+```toml
+[profile.dev]
+panic = "abort"
+
+[profile.release]
+panic = "abort"
+```
+
+Isso define a estratégia de panic para `abort` tanto para o perfil `dev` (usado para `cargo build`) quanto para o perfil `release` (usado para `cargo build --release`). Agora o item de linguagem `eh_personality` não deve mais ser necessário.
+
+[abortar no panic]: https://github.com/rust-lang/rust/pull/32900
+
+Agora corrigimos ambos os erros acima. No entanto, se tentarmos compilar agora, outro erro ocorre:
+
+```
+> cargo build
+error: requires `start` lang_item
+```
+
+Está faltando o item de linguagem `start` no nosso programa, que define o ponto de entrada.
+
+## O Atributo `start`
+
+Alguém pode pensar que a função `main` é a primeira função chamada quando você executa um programa. No entanto, a maioria das linguagens tem um [sistema de runtime], que é responsável por coisas como coleta de lixo (por exemplo, em Java) ou threads de software (por exemplo, goroutines em Go). Este runtime precisa ser chamado antes de `main`, já que ele precisa se inicializar a si mesmo.
+
+[sistema de runtime]: https://en.wikipedia.org/wiki/Runtime_system
+
+Em um binário Rust típico que vincula a biblioteca padrão, a execução começa em uma biblioteca de runtime C chamada `crt0` ("C runtime zero"), que configura o ambiente para uma aplicação C. Isso inclui criar um stack e colocar os argumentos nos registradores certos. O runtime C então invoca o [ponto de entrada do runtime Rust][rt::lang_start], que é marcado pelo item de linguagem `start`. Rust tem apenas um runtime muito mínimo, que cuida de algumas poucas coisas como configurar guardas de estouro do stack ou imprimir um backtrace ao fazer panic. O runtime então finalmente chama a função `main`.
+
+[rt::lang_start]: https://github.com/rust-lang/rust/blob/bb4d1491466d8239a7a5fd68bd605e3276e97afb/src/libstd/rt.rs#L32-L73
+
+Nosso executável independente não tem acesso ao runtime Rust e ao `crt0`, então precisamos definir nosso próprio ponto de entrada. Implementar o item de linguagem `start` não ajudaria, já que ainda exigiria `crt0`. Em vez disso, precisamos sobrescrever diretamente o ponto de entrada `crt0`.
+
+### Sobrescrevendo o Ponto de Entrada (Entry Point)
+Para dizer ao compilador Rust que não queremos usar a cadeia normal de ponto de entrada, adicionamos o atributo `#![no_main]`.
+
+```rust
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+/// Esta função é chamada em caso de pânico.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+Você pode notar que removemos a função `main`. A razão é que um `main` não faz sentido sem um runtime subjacente que o chame. Em vez disso, estamos agora sobrescrevendo o ponto de entrada do sistema operacional com nossa própria função `_start`:
+
+```rust
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ loop {}
+}
+```
+
+Ao usar o atributo `#[unsafe(no_mangle)]`, desativamos [mangling de nomes] para garantir que o compilador Rust realmente produza uma função com o nome `_start`. Sem o atributo, o compilador geraria algum símbolo criptografado como `_ZN3blog_os4_start7hb173fedf945531caE` para dar a cada função um nome único. O atributo é necessário porque precisamos dizer o nome da função do ponto de entrada ao linker no próximo passo.
+
+Também temos que marcar a função como `extern "C"` para dizer ao compilador que ele deve usar a [convenção de chamada C] para esta função (em vez da convenção de chamada Rust não especificada). A razão de nomear a função `_start` é que este é o nome do ponto de entrada padrão para a maioria dos sistemas.
+
+[mangling de nomes]: https://en.wikipedia.org/wiki/Name_mangling
+[convenção de chamada C]: https://en.wikipedia.org/wiki/Calling_convention
+
+O tipo de retorno `!` significa que a função é divergente, ou seja, não é permitida retornar nunca. Isso é necessário porque o ponto de entrada não é chamado por nenhuma função, mas invocado diretamente pelo sistema operacional ou bootloader. Então em vez de retornar, o ponto de entrada deve por exemplo invocar a [chamada de sistema `exit`] do sistema operacional. No nosso caso, desligar a máquina poderia ser uma ação razoável, já que não há nada mais a fazer se um binário independente retorna. Por enquanto, cumprimos o requisito fazendo um loop infinito.
+
+[chamada de sistema `exit`]: https://en.wikipedia.org/wiki/Exit_(system_call)
+
+Quando executamos `cargo build` agora, recebemos um feio erro de _linker_.
+
+## Erros do Linker
+
+O linker é um programa que combina o código gerado em um executável. Como o formato executável difere entre Linux, Windows e macOS, cada sistema tem seu próprio linker que lança um erro diferente. A causa fundamental dos erros é a mesma: a configuração padrão do linker assume que nosso programa depende do runtime C, o que não é o caso.
+
+Para resolver os erros, precisamos dizer ao linker que ele não deve incluir o runtime C. Podemos fazer isso passando um certo conjunto de argumentos ao linker ou compilando para um alvo bare metal.
+
+### Compilando para um Alvo Bare Metal
+
+Por padrão, Rust tenta construir um executável que seja capaz de executar no seu ambiente de sistema atual. Por exemplo, se você estiver usando Windows em `x86_64`, Rust tenta construir um executável `.exe` Windows que usa instruções `x86_64`. Este ambiente é chamado seu sistema "host".
+
+Para descrever ambientes diferentes, Rust usa uma string chamada [_target triple_]. Você pode ver o target triple do seu sistema host executando `rustc --version --verbose`:
+
+[_target triple_]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+
+```
+rustc 1.91.0 (f8297e351 2025-10-28)
+binary: rustc
+commit-hash: f8297e351a40c1439a467bbbb6879088047f50b3
+commit-date: 2025-10-28
+host: x86_64-unknown-linux-gnu
+release: 1.91.0
+LLVM version: 21.1.2
+```
+
+A saída acima é de um sistema Linux `x86_64`. Vemos que o triple `host` é `x86_64-unknown-linux-gnu`, que inclui a arquitetura de CPU (`x86_64`), o vendor (`unknown`), o sistema operacional (`linux`), e a [ABI] (`gnu`).
+
+[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
+
+Ao compilar para nosso triple host, o compilador Rust e o linker assumem que há um sistema operacional subjacente como Linux ou Windows que usa o runtime C por padrão, o que causa os erros do linker. Então, para evitar os erros do linker, podemos compilar para um ambiente diferente sem nenhum sistema operacional subjacente.
+
+Um exemplo de tal ambiente bare metal é o target triple `thumbv7em-none-eabihf`, que descreve um sistema [embarcado] [ARM]. Os detalhes não são importantes, tudo o que importa é que o target triple não tem nenhum sistema operacional subjacente, o que é indicado pelo `none` no target triple. Para ser capaz de compilar para este alvo, precisamos adicioná-lo em rustup:
+
+[embarcado]: https://en.wikipedia.org/wiki/Embedded_system
+[ARM]: https://en.wikipedia.org/wiki/ARM_architecture
+
+```
+rustup target add thumbv7em-none-eabihf
+```
+
+Isso baixa uma cópia da biblioteca padrão std (e core) para o sistema. Agora podemos compilar nosso executável independente para este alvo:
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+Ao passar um argumento `--target`, nós fazemos uma compilação [cross compile] nosso executável para um sistema alvo bare metal. Como o sistema alvo não tem sistema operacional, o linker não tenta vincular o runtime C e nossa compilação é bem-sucedida sem nenhum erro de linker.
+
+[cross compile]: https://en.wikipedia.org/wiki/Cross_compiler
+
+Esta é a abordagem que usaremos para construir nosso kernel de SO. Em vez de `thumbv7em-none-eabihf`, usaremos um [alvo customizado] que descreve um ambiente bare metal `x86_64`. Os detalhes serão explicados no próximo post.
+
+[alvo customizado]: https://doc.rust-lang.org/rustc/targets/custom.html
+
+### Argumentos do Linker
+
+Em vez de compilar para um sistema bare metal, também é possível resolver os erros do linker passando um certo conjunto de argumentos ao linker. Esta não é a abordagem que usaremos para nosso kernel, portanto esta seção é opcional e fornecida apenas para completude. Clique em _"Argumentos do Linker"_ abaixo para mostrar o conteúdo opcional.
+
+
+
+Argumentos do Linker
+
+Nesta seção discutimos os erros do linker que ocorrem no Linux, Windows e macOS, e explicamos como resolvê-los passando argumentos adicionais ao linker. Note que o formato executável e o linker diferem entre sistemas operacionais, então que um conjunto diferente de argumentos é necessário para cada sistema.
+
+#### Linux
+
+No Linux, o seguinte erro de linker ocorre (encurtado):
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x12): undefined reference to `__libc_csu_fini'
+ /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x19): undefined reference to `__libc_csu_init'
+ /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x25): undefined reference to `__libc_start_main'
+ collect2: error: ld returned 1 exit status
+```
+
+O problema é que o linker inclui a rotina de inicialização do runtime C por padrão, que também é chamada `_start`. Ela requer alguns símbolos da biblioteca padrão C `libc` que não incluímos devido ao atributo `no_std`, portanto o linker não consegue resolver estas referências. Para resolver isso, podemos dizer ao linker que ele não deve vincular a rotina de inicialização C passando a flag `-nostartfiles`.
+
+Uma forma de passar atributos de linker via cargo é o comando `cargo rustc`. O comando se comporta exatamente como `cargo build`, mas permite passar opções para `rustc`, o compilador Rust subjacente. `rustc` tem a flag `-C link-arg`, que passa um argumento ao linker. Combinados, nosso novo comando de compilação se parece com isso:
+
+```
+cargo rustc -- -C link-arg=-nostartfiles
+```
+
+Agora nossa crate compilada como um executável independente no Linux!
+
+Não precisávamos especificar o nome da nossa função de ponto de entrada explicitamente, já que o linker procura por uma função com o nome `_start` por padrão.
+
+#### Windows
+
+No Windows, um erro de linker diferente ocorre (encurtado):
+
+```
+error: linking with `link.exe` failed: exit code: 1561
+ |
+ = note: "C:\\Program Files (x86)\\…\\link.exe" […]
+ = note: LINK : fatal error LNK1561: entry point must be defined
+```
+
+O erro "entry point must be defined" (ponto de entrada deve ser definido) significa que o linker não consegue encontrar o ponto de entrada. No Windows, o nome do ponto de entrada padrão [depende do subsistema usado][windows-subsystems]. Para o subsistema `CONSOLE`, o linker procura por uma função nomeada `mainCRTStartup` e para o subsistema `WINDOWS`, ele procura por uma função nomeada `WinMainCRTStartup`. Para sobrescrever o padrão e dizer ao linker para procurar por nossa função `_start` em vez disso, podemos passar um argumento `/ENTRY` ao linker:
+
+[windows-subsystems]: https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol
+
+```
+cargo rustc -- -C link-arg=/ENTRY:_start
+```
+
+Do formato de argumento diferente, vemos claramente que o linker Windows é um programa completamente diferente do linker Linux.
+
+Agora um erro de linker diferente ocorre:
+
+```
+error: linking with `link.exe` failed: exit code: 1221
+ |
+ = note: "C:\\Program Files (x86)\\…\\link.exe" […]
+ = note: LINK : fatal error LNK1221: a subsystem can't be inferred and must be
+ defined
+```
+
+Este erro ocorre porque os executáveis Windows podem usar [subsistemas] diferentes[windows-subsystems]. Para programas normais, eles são inferidos dependendo do nome do ponto de entrada: Se o ponto de entrada é nomeado `main`, o subsistema `CONSOLE` é usado, e se o ponto de entrada é nomeado `WinMain`, o subsistema `WINDOWS` é usado. Como nossa função `_start` tem um nome diferente, precisamos especificar o subsistema explicitamente:
+
+```
+cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
+```
+
+Usamos o subsistema `CONSOLE` aqui, mas o subsistema `WINDOWS` funcionaria também. Em vez de passar `-C link-arg` múltiplas vezes, usamos `-C link-args` que leva uma lista de argumentos separados por espaço.
+
+Com este comando, nosso executável deve compilar com sucesso no Windows.
+
+#### macOS
+
+No macOS, o seguinte erro de linker ocorre (encurtado):
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: entry point (_main) undefined. for architecture x86_64
+ clang: error: linker command failed with exit code 1 […]
+```
+
+Esta mensagem de erro nos diz que o linker não consegue encontrar uma função de ponto de entrada com o nome padrão `main` (por alguma razão, todas as funções são prefixadas com um `_` no macOS). Para definir o ponto de entrada para nossa função `_start`, passamos o argumento de linker `-e`:
+
+```
+cargo rustc -- -C link-args="-e __start"
+```
+
+A flag `-e` especifica o nome da função de ponto de entrada. Como todas as funções têm um `_` adicional prefixado no macOS, precisamos definir o ponto de entrada para `__start` em vez de `_start`.
+
+Agora o seguinte erro de linker ocorre:
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: dynamic main executables must link with libSystem.dylib
+ for architecture x86_64
+ clang: error: linker command failed with exit code 1 […]
+```
+
+macOS [não oferece suporte oficial a binários vinculados estaticamente] e requer que programas vinculem a biblioteca `libSystem` por padrão. Para sobrescrever isto e vincular um binário estático, passamos a flag `-static` ao linker:
+
+[não oferece suporte oficial a binários vinculados estaticamente]: https://developer.apple.com/library/archive/qa/qa1118/_index.html
+
+```
+cargo rustc -- -C link-args="-e __start -static"
+```
+
+Isso ainda não é suficiente, pois um terceiro erro de linker ocorre:
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: library not found for -lcrt0.o
+ clang: error: linker command failed with exit code 1 […]
+```
+
+Este erro ocorre porque programas no macOS vinculam a `crt0` ("C runtime zero") por padrão. Isto é similar ao erro que tivemos no Linux e também pode ser resolvido adicionando o argumento de linker `-nostartfiles`:
+
+```
+cargo rustc -- -C link-args="-e __start -static -nostartfiles"
+```
+
+Agora nosso programa deve compilar com sucesso no macOS.
+
+#### Unificando os Comandos de Compilação
+
+Agora temos diferentes comandos de compilação dependendo da plataforma host, o que não é o ideal. Para evitar isto, podemos criar um arquivo nomeado `.cargo/config.toml` que contém os argumentos específicos de plataforma:
+
+```toml
+# in .cargo/config.toml
+
+[target.'cfg(target_os = "linux")']
+rustflags = ["-C", "link-arg=-nostartfiles"]
+
+[target.'cfg(target_os = "windows")']
+rustflags = ["-C", "link-args=/ENTRY:_start /SUBSYSTEM:console"]
+
+[target.'cfg(target_os = "macos")']
+rustflags = ["-C", "link-args=-e __start -static -nostartfiles"]
+```
+
+A key `rustflags` contém argumentos que são automaticamente adicionados a cada invocação de `rustc`. Para mais informações sobre o arquivo `.cargo/config.toml`, veja a [documentação oficial](https://doc.rust-lang.org/cargo/reference/config.html).
+
+Agora nosso programa deve ser compilável em todas as três plataformas com um simples `cargo build`.
+
+#### Você Deveria Fazer Isto?
+
+Enquanto é possível construir um executável independente para Linux, Windows e macOS, provavelmente não é uma boa ideia. A razão é que nosso executável ainda espera por várias coisas, por exemplo que uma pilha seja inicializada quando a função `_start` é chamada. Sem o runtime C, alguns desses requisitos podem não ser atendidos, o que pode causar nosso programa falhar, por exemplo através de um segmentation fault.
+
+Se você quiser criar um binário mínimo que execute em cima de um sistema operacional existente, incluindo `libc` e definindo o atributo `#[start]` conforme descrito [aqui](https://doc.rust-lang.org/1.16.0/book/no-stdlib.html) é provavelmente uma melhor ideia.
+
+
+
+## Sumário
+
+Um binário Rust independente mínimo se parece com isto:
+
+`src/main.rs`:
+
+```rust
+#![no_std] // Não vincule a biblioteca padrão do Rust
+#![no_main] // desativar todos os pontos de entrada no nível Rust
+
+use core::panic::PanicInfo;
+
+#[unsafe(no_mangle)] // não altere (mangle) o nome desta função
+pub extern "C" fn _start() -> ! {
+ // essa função é o ponto de entrada, já que o vinculador procura uma função
+ // denominado `_start` por padrão
+ loop {}
+}
+
+/// Esta função é chamada em caso de pânico.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+`Cargo.toml`:
+
+```toml
+[package]
+name = "crate_name"
+version = "0.1.0"
+authors = ["Author Name "]
+
+# o perfil usado para `cargo build`
+[profile.dev]
+panic = "abort" # desativar o unwinding do stack em caso de pânico
+
+# o perfil usado para `cargo build --release`
+[profile.release]
+panic = "abort" # desativar o unwinding do stack em caso de pânico
+```
+
+Para construir este binário, precisamos compilar para um alvo bare metal como `thumbv7em-none-eabihf`:
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+Alternativamente, podemos compilá-lo para o sistema host passando argumentos adicionais de linker:
+
+```bash
+# Linux
+cargo rustc -- -C link-arg=-nostartfiles
+# Windows
+cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
+# macOS
+cargo rustc -- -C link-args="-e __start -static -nostartfiles"
+```
+
+Note que isto é apenas um exemplo mínimo de um binário Rust independente. Este binário espera por várias coisas, por exemplo, que um stack seja inicializado quando a função `_start` é chamada. **Portanto para qualquer uso real de tal binário, mais passos são necessários**.
+
+## Deixando `rust-analyzer` Feliz
+
+O projeto [`rust-analyzer`](https://rust-analyzer.github.io/) é uma ótima forma de obter autocompletar e suporte "ir para definição" (e muitos outros recursos) para código Rust no seu editor.
+Funciona muito bem para projetos `#![no_std]` também, então recomendo usá-lo para desenvolvimento de kernel!
+
+Se você estiver usando a funcionalidade [`checkOnSave`](https://rust-analyzer.github.io/book/configuration.html#checkOnSave) de `rust-analyzer` (habilitada por padrão), ela pode relatar um erro para a função panic do nosso kernel:
+
+```
+found duplicate lang item `panic_impl`
+```
+
+A razão para este erro é que `rust-analyzer` invoca `cargo check --all-targets` por padrão, que também tenta construir o binário em modo [teste](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) e [benchmark](https://doc.rust-lang.org/rustc/tests/index.html#benchmarks).
+
+
+
+### Os dois significados de "target"
+
+A flag `--all-targets` é completamente não relacionada ao argumento `--target`.
+Há dois significados diferentes do termo "target" no `cargo`:
+
+- A flag `--target` especifica o [_alvo de compilação_] que deve ser passado ao compilador `rustc`. Isso deve ser definido como o [target triple] da máquina que deve executar nosso código.
+- A flag `--all-targets` referencia o [_alvo do package] do Cargo. Pacotes Cargo podem ser uma biblioteca e binário ao mesmo tempo, então você pode especificar de qual forma você gostaria de construir sua crate. Além disso, Cargo também tem alvos de package para [exemplos](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#examples), [testes](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#tests), e [benchmarks](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#benchmarks). Esses alvos de pacote podem coexistir, então você pode construir/verificar a mesma crate por exemplo em modo biblioteca ou modo teste.
+
+[_alvo de compilação_]: https://doc.rust-lang.org/rustc/targets/index.html
+[target triple]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+[_alvo do package]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html
+
+
+
+Por padrão, `cargo check` apenas constrói o _biblioteca_ e os alvos de pacote _binário_.
+No entanto, `rust-analyzer` escolhe verificar todos os alvos de pacote por padrão quando [`checkOnSave`](https://rust-analyzer.github.io/book/configuration.html#checkOnSave) é habilitado.
+Esta é a razão pela qual `rust-analyzer` relata o erro de `lang item` acima que não vemos em `cargo check`.
+Se executarmos `cargo check --all-targets`, vemos o erro também:
+
+```
+error[E0152]: found duplicate lang item `panic_impl`
+ --> src/main.rs:13:1
+ |
+13 | / fn panic(_info: &PanicInfo) -> ! {
+14 | | loop {}
+15 | | }
+ | |_^
+ |
+ = note: the lang item is first defined in crate `std` (which `test` depends on)
+ = note: first definition in `std` loaded from /home/[...]/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-8df6be531efb3fd0.rlib
+ = note: second definition in the local crate (`blog_os`)
+```
+
+A primeira `note` nos diz que o item de linguagem panic já está definido na crate `std`, que é uma dependência da crate `test`.
+A crate `test` é automaticamente incluída ao construir uma crate em [modo teste](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#tests).
+Isso não faz sentido para nosso kernel `#![no_std]` já que não há forma de suportar a biblioteca padrão em bare metal.
+Então este erro não é relevante para nosso projeto e podemos seguramente ignorá-lo.
+
+A forma apropriada de evitar este erro é especificar em nosso `Cargo.toml` que nosso binário não suporta construção em modos `test` e `bench`.
+Podemos fazer isso adicionando uma seção `[[bin]]` em nosso `Cargo.toml` para [configurar a construção](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target) do nosso binário:
+
+```toml
+# no Cargo.toml
+
+[[bin]]
+name = "blog_os"
+test = false
+bench = false
+```
+
+Os colchetes duplos ao redor de `bin` não é um erro, isto é como o formato TOML define chaves que podem aparecer múltiplas vezes.
+Como uma crate pode ter múltiplos binários, a seção `[[bin]]` pode aparecer múltiplas vezes em `Cargo.toml` também.
+Esta é também a razão para o campo `name` obrigatório, que precisa corresponder ao nome do binário (para que `cargo` saiba quais configurações devem ser aplicadas a qual binário).
+
+Ao definir os campos [`test`](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-test-field) e [`bench` ](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-bench-field) para `false`, instruímos `cargo` a não construir nosso binário em modo teste ou benchmark.
+Agora `cargo check --all-targets` não deve lançar mais erros, e a implementação de `checkOnSave` de `rust-analyzer` também deve estar feliz.
+
+## O que vem a seguir?
+
+O [próximo post] explica os passos necessários para transformar nosso binário independente em um kernel mínimo do sistema operacional. Isso inclui criar um alvo customizado, combinar nosso executável com um bootloader, e aprender como imprimir algo na tela.
+
+[próximo post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
From ac61083311a8aeb3dcd2a38a3d5aa4567f79b91f Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:24:48 -0300
Subject: [PATCH 03/26] [Translation][Portuguese pt-BR] post-2 (edition-2)
---
.../disable-red-zone/index.pt-BR.md | 35 ++
.../disable-simd/index.pt-BR.md | 50 ++
.../02-minimal-rust-kernel/index.pt-BR.md | 504 ++++++++++++++++++
3 files changed, 589 insertions(+)
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.pt-BR.md
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.pt-BR.md
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.pt-BR.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.pt-BR.md
new file mode 100644
index 00000000..1d134922
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.pt-BR.md
@@ -0,0 +1,35 @@
++++
+title = "Desabilitando a Red Zone"
+weight = 1
+path = "pt-BR/red-zone"
+template = "edition-2/extra.html"
+
+[extra]
+# Please update this when updating the translation
+translation_based_on_commit = "9d079e6d3e03359469d6cf1759bb1a196d8a11ac"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+A [red zone] é uma otimização da [System V ABI] que permite que funções usem temporariamente os 128 bytes abaixo do seu stack frame sem ajustar o ponteiro de pilha:
+
+[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
+[System V ABI]: https://wiki.osdev.org/System_V_ABI
+
+
+
+
+
+A imagem mostra o stack frame de uma função com `n` variáveis locais. Na entrada da função, o ponteiro de pilha é ajustado para abrir espaço na pilha para o endereço de retorno e as variáveis locais.
+
+A red zone é definida como os 128 bytes abaixo do ponteiro de pilha ajustado. A função pode usar esta área para dados temporários que não são necessários entre chamadas de função. Assim, as duas instruções para ajustar o ponteiro de pilha podem ser evitadas em alguns casos (por exemplo, em pequenas funções folha).
+
+No entanto, esta otimização leva a problemas enormes com exceções ou interrupções de hardware. Vamos assumir que uma exceção ocorre enquanto uma função usa a red zone:
+
+
+
+A CPU e o handler de exceção sobrescrevem os dados na red zone. Mas estes dados ainda são necessários pela função interrompida. Então a função não funcionará mais corretamente quando retornarmos do handler de exceção. Isso pode levar a bugs estranhos que [levam semanas para depurar].
+
+[levam semanas para depurar]: https://forum.osdev.org/viewtopic.php?t=21720
+
+Para evitar tais bugs quando implementarmos tratamento de exceções no futuro, desabilitamos a red zone logo de início. Isso é alcançado adicionando a linha `"disable-redzone": true` ao nosso arquivo de configuração de alvo.
\ No newline at end of file
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.pt-BR.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.pt-BR.md
new file mode 100644
index 00000000..a397fe80
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.pt-BR.md
@@ -0,0 +1,50 @@
++++
+title = "Desabilitando SIMD"
+weight = 2
+path = "pt-BR/disable-simd"
+template = "edition-2/extra.html"
+
+[extra]
+# Please update this when updating the translation
+translation_based_on_commit = "9d079e6d3e03359469d6cf1759bb1a196d8a11ac"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Instruções [Single Instruction Multiple Data (SIMD)] são capazes de realizar uma operação (por exemplo, adição) simultaneamente em múltiplas palavras de dados, o que pode acelerar programas significativamente. A arquitetura `x86_64` suporta vários padrões SIMD:
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+
+
+- [MMX]: O conjunto de instruções _Multi Media Extension_ foi introduzido em 1997 e define oito registradores de 64 bits chamados `mm0` até `mm7`. Esses registradores são apenas aliases para os registradores da [unidade de ponto flutuante x87].
+- [SSE]: O conjunto de instruções _Streaming SIMD Extensions_ foi introduzido em 1999. Em vez de reutilizar os registradores de ponto flutuante, ele adiciona um conjunto de registradores completamente novo. Os dezesseis novos registradores são chamados `xmm0` até `xmm15` e têm 128 bits cada.
+- [AVX]: As _Advanced Vector Extensions_ são extensões que aumentam ainda mais o tamanho dos registradores multimídia. Os novos registradores são chamados `ymm0` até `ymm15` e têm 256 bits cada. Eles estendem os registradores `xmm`, então por exemplo `xmm0` é a metade inferior de `ymm0`.
+
+[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
+[unidade de ponto flutuante x87]: https://en.wikipedia.org/wiki/X87
+[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
+[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
+
+Ao usar tais padrões SIMD, programas frequentemente podem acelerar significativamente. Bons compiladores são capazes de transformar loops normais em tal código SIMD automaticamente através de um processo chamado [auto-vetorização].
+
+[auto-vetorização]: https://en.wikipedia.org/wiki/Automatic_vectorization
+
+No entanto, os grandes registradores SIMD levam a problemas em kernels de SO. A razão é que o kernel tem que fazer backup de todos os registradores que usa para a memória em cada interrupção de hardware, porque eles precisam ter seus valores originais quando o programa interrompido continua. Então, se o kernel usa registradores SIMD, ele tem que fazer backup de muito mais dados (512-1600 bytes), o que diminui notavelmente o desempenho. Para evitar esta perda de desempenho, queremos desabilitar os recursos `sse` e `mmx` (o recurso `avx` é desabilitado por padrão).
+
+Podemos fazer isso através do campo `features` na nossa especificação de alvo. Para desabilitar os recursos `mmx` e `sse`, nós os adicionamos prefixados com um menos:
+
+```json
+"features": "-mmx,-sse"
+```
+
+## Ponto Flutuante
+Infelizmente para nós, a arquitetura `x86_64` usa registradores SSE para operações de ponto flutuante. Assim, todo uso de ponto flutuante com SSE desabilitado causa um erro no LLVM. O problema é que a biblioteca core do Rust já usa floats (por exemplo, ela implementa traits para `f32` e `f64`), então evitar floats no nosso kernel não é suficiente.
+
+Felizmente, o LLVM tem suporte para um recurso `soft-float` que emula todas as operações de ponto flutuante através de funções de software baseadas em inteiros normais. Isso torna possível usar floats no nosso kernel sem SSE; será apenas um pouco mais lento.
+
+Para ativar o recurso `soft-float` para o nosso kernel, nós o adicionamos à linha `features` na nossa especificação de alvo, prefixado com um mais:
+
+```json
+"features": "-mmx,-sse,+soft-float"
+```
\ No newline at end of file
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md
new file mode 100644
index 00000000..2c0c3ae6
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md
@@ -0,0 +1,504 @@
++++
+title = "Um Kernel Rust Mínimo"
+weight = 2
+path = "pt-BR/minimal-rust-kernel"
+date = 2018-02-10
+
+[extra]
+chapter = "Bare Bones"
+# Please update this when updating the translation
+translation_based_on_commit = "95d4fbd54c6b0e5a874981558c0cc1fe85d31606"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Neste post, criamos um kernel Rust mínimo de 64 bits para a arquitetura x86. Construímos sobre o [binário Rust independente] do post anterior para criar uma imagem de disco inicializável que imprime algo na tela.
+
+[binário Rust independente]: @/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-02`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-02
+
+
+
+## O Processo de Boot
+Quando você liga um computador, ele começa a executar código de firmware que está armazenado na [ROM] da placa-mãe. Este código executa um [teste automático de inicialização], detecta a RAM disponível e pré-inicializa a CPU e o hardware. Depois, ele procura por um disco inicializável e começa a inicializar o kernel do sistema operacional.
+
+[ROM]: https://en.wikipedia.org/wiki/Read-only_memory
+[teste automático de inicialização]: https://en.wikipedia.org/wiki/Power-on_self-test
+
+No x86, existem dois padrões de firmware: o "Basic Input/Output System" (**[BIOS]**) e o mais novo "Unified Extensible Firmware Interface" (**[UEFI]**). O padrão BIOS é antigo e ultrapassado, mas simples e bem suportado em qualquer máquina x86 desde os anos 1980. UEFI, em contraste, é mais moderno e tem muito mais recursos, mas é mais complexo de configurar (na minha opinião, pelo menos).
+
+[BIOS]: https://en.wikipedia.org/wiki/BIOS
+[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
+
+Atualmente, fornecemos apenas suporte para BIOS, mas suporte para UEFI também está planejado. Se você gostaria de nos ajudar com isso, confira o [issue no Github](https://github.com/phil-opp/blog_os/issues/349).
+
+### Boot BIOS
+Quase todos os sistemas x86 têm suporte para boot BIOS, incluindo máquinas mais novas baseadas em UEFI que usam um BIOS emulado. Isso é ótimo, porque você pode usar a mesma lógica de boot em todas as máquinas do último século. Mas essa ampla compatibilidade é ao mesmo tempo a maior desvantagem do boot BIOS, porque significa que a CPU é colocada em um modo de compatibilidade de 16 bits chamado [modo real] antes do boot, para que bootloaders arcaicos dos anos 1980 ainda funcionem.
+
+Mas vamos começar do início:
+
+Quando você liga um computador, ele carrega o BIOS de uma memória flash especial localizada na placa-mãe. O BIOS executa rotinas de teste automático e inicialização do hardware, então procura por discos inicializáveis. Se ele encontra um, o controle é transferido para seu _bootloader_, que é uma porção de 512 bytes de código executável armazenado no início do disco. A maioria dos bootloaders é maior que 512 bytes, então os bootloaders são comumente divididos em um primeiro estágio pequeno, que cabe em 512 bytes, e um segundo estágio, que é subsequentemente carregado pelo primeiro estágio.
+
+O bootloader tem que determinar a localização da imagem do kernel no disco e carregá-la na memória. Ele também precisa mudar a CPU do [modo real] de 16 bits primeiro para o [modo protegido] de 32 bits, e então para o [modo longo] de 64 bits, onde registradores de 64 bits e a memória principal completa estão disponíveis. Seu terceiro trabalho é consultar certas informações (como um mapa de memória) do BIOS e passá-las ao kernel do SO.
+
+[modo real]: https://en.wikipedia.org/wiki/Real_mode
+[modo protegido]: https://en.wikipedia.org/wiki/Protected_mode
+[modo longo]: https://en.wikipedia.org/wiki/Long_mode
+[segmentação de memória]: https://en.wikipedia.org/wiki/X86_memory_segmentation
+
+Escrever um bootloader é um pouco trabalhoso, pois requer linguagem assembly e muitos passos pouco intuitivos como "escrever este valor mágico neste registrador do processador". Portanto, não cobrimos a criação de bootloader neste post e em vez disso fornecemos uma ferramenta chamada [bootimage] que anexa automaticamente um bootloader ao seu kernel.
+
+[bootimage]: https://github.com/rust-osdev/bootimage
+
+Se você estiver interessado em construir seu próprio bootloader: Fique ligado, um conjunto de posts sobre este tópico já está planejado!
+
+#### O Padrão Multiboot
+Para evitar que todo sistema operacional implemente seu próprio bootloader, que é compatível apenas com um único SO, a [Free Software Foundation] criou um padrão de bootloader aberto chamado [Multiboot] em 1995. O padrão define uma interface entre o bootloader e o sistema operacional, para que qualquer bootloader compatível com Multiboot possa carregar qualquer sistema operacional compatível com Multiboot. A implementação de referência é o [GNU GRUB], que é o bootloader mais popular para sistemas Linux.
+
+[Free Software Foundation]: https://en.wikipedia.org/wiki/Free_Software_Foundation
+[Multiboot]: https://wiki.osdev.org/Multiboot
+[GNU GRUB]: https://en.wikipedia.org/wiki/GNU_GRUB
+
+Para tornar um kernel compatível com Multiboot, basta inserir um chamado [cabeçalho Multiboot] no início do arquivo do kernel. Isso torna muito fácil inicializar um SO a partir do GRUB. No entanto, o GRUB e o padrão Multiboot também têm alguns problemas:
+
+[cabeçalho Multiboot]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#OS-image-format
+
+- Eles suportam apenas o modo protegido de 32 bits. Isso significa que você ainda tem que fazer a configuração da CPU para mudar para o modo longo de 64 bits.
+- Eles são projetados para tornar o bootloader simples em vez do kernel. Por exemplo, o kernel precisa ser vinculado com um [tamanho de página padrão ajustado], porque o GRUB não consegue encontrar o cabeçalho Multiboot caso contrário. Outro exemplo é que as [informações de boot], que são passadas ao kernel, contêm muitas estruturas dependentes de arquitetura em vez de fornecer abstrações limpas.
+- Tanto o GRUB quanto o padrão Multiboot são documentados apenas esparsamente.
+- O GRUB precisa estar instalado no sistema host para criar uma imagem de disco inicializável a partir do arquivo do kernel. Isso torna o desenvolvimento no Windows ou Mac mais difícil.
+
+[tamanho de página padrão ajustado]: https://wiki.osdev.org/Multiboot#Multiboot_2
+[informações de boot]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+
+Por causa dessas desvantagens, decidimos não usar o GRUB ou o padrão Multiboot. No entanto, planejamos adicionar suporte Multiboot à nossa ferramenta [bootimage], para que seja possível carregar seu kernel em um sistema GRUB também. Se você estiver interessado em escrever um kernel compatível com Multiboot, confira a [primeira edição] desta série de blog.
+
+[primeira edição]: @/edition-1/_index.md
+
+### UEFI
+
+(Não fornecemos suporte UEFI no momento, mas adoraríamos! Se você gostaria de ajudar, por favor nos diga no [issue do Github](https://github.com/phil-opp/blog_os/issues/349).)
+
+## Um Kernel Mínimo
+Agora que sabemos aproximadamente como um computador inicializa, é hora de criar nosso próprio kernel mínimo. Nosso objetivo é criar uma imagem de disco que imprima um "Hello World!" na tela quando inicializada. Fazemos isso estendendo o [binário Rust independente] do post anterior.
+
+Como você deve se lembrar, construímos o binário independente através do `cargo`, mas dependendo do sistema operacional, precisávamos de nomes de ponto de entrada e flags de compilação diferentes. Isso ocorre porque o `cargo` compila para o _sistema host_ por padrão, ou seja, o sistema em que você está executando. Isso não é algo que queremos para nosso kernel, porque um kernel que executa em cima de, por exemplo, Windows, não faz muito sentido. Em vez disso, queremos compilar para um _sistema alvo_ claramente definido.
+
+### Instalando o Rust Nightly
+O Rust tem três canais de lançamento: _stable_, _beta_ e _nightly_. O Livro do Rust explica a diferença entre esses canais muito bem, então dê uma olhada [aqui](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains). Para construir um sistema operacional, precisaremos de alguns recursos experimentais que estão disponíveis apenas no canal nightly, então precisamos instalar uma versão nightly do Rust.
+
+Para gerenciar instalações do Rust, eu recomendo fortemente o [rustup]. Ele permite instalar compiladores nightly, beta e stable lado a lado e facilita a atualização deles. Com rustup, você pode usar um compilador nightly para o diretório atual executando `rustup override set nightly`. Alternativamente, você pode adicionar um arquivo chamado `rust-toolchain` com o conteúdo `nightly` ao diretório raiz do projeto. Você pode verificar que tem uma versão nightly instalada executando `rustc --version`: O número da versão deve conter `-nightly` no final.
+
+[rustup]: https://www.rustup.rs/
+
+O compilador nightly nos permite optar por vários recursos experimentais usando as chamadas _feature flags_ no topo do nosso arquivo. Por exemplo, poderíamos habilitar a [macro `asm!`] experimental para assembly inline adicionando `#![feature(asm)]` no topo do nosso `main.rs`. Note que tais recursos experimentais são completamente instáveis, o que significa que versões futuras do Rust podem alterá-los ou removê-los sem aviso prévio. Por esta razão, só os usaremos se absolutamente necessário.
+
+[macro `asm!`]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
+
+### Especificação de Alvo
+O Cargo suporta diferentes sistemas alvo através do parâmetro `--target`. O alvo é descrito por uma chamada _[target triple]_, que descreve a arquitetura da CPU, o vendor, o sistema operacional e a [ABI]. Por exemplo, o target triple `x86_64-unknown-linux-gnu` descreve um sistema com uma CPU `x86_64`, sem vendor claro, e um sistema operacional Linux com a ABI GNU. O Rust suporta [muitos target triples diferentes][platform-support], incluindo `arm-linux-androideabi` para Android ou [`wasm32-unknown-unknown` para WebAssembly](https://www.hellorust.com/setup/wasm-target/).
+
+[target triple]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+[ABI]: https://stackoverflow.com/a/2456882
+[platform-support]: https://forge.rust-lang.org/release/platform-support.html
+[custom-targets]: https://doc.rust-lang.org/nightly/rustc/targets/custom.html
+
+Para nosso sistema alvo, no entanto, precisamos de alguns parâmetros de configuração especiais (por exemplo, nenhum SO subjacente), então nenhum dos [target triples existentes][platform-support] se encaixa. Felizmente, o Rust nos permite definir [nosso próprio alvo][custom-targets] através de um arquivo JSON. Por exemplo, um arquivo JSON que descreve o target `x86_64-unknown-linux-gnu` se parece com isto:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-linux-gnu",
+ "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": 64,
+ "target-c-int-width": 32,
+ "os": "linux",
+ "executables": true,
+ "linker-flavor": "gcc",
+ "pre-link-args": ["-m64"],
+ "morestack": false
+}
+```
+
+A maioria dos campos é exigida pelo LLVM para gerar código para aquela plataforma. Por exemplo, o campo [`data-layout`] define o tamanho de vários tipos integer, floating point e pointer. Então há campos que o Rust usa para compilação condicional, como `target-pointer-width`. O terceiro tipo de campo define como a crate deve ser construída. Por exemplo, o campo `pre-link-args` especifica argumentos passados ao [linker].
+
+[`data-layout`]: https://llvm.org/docs/LangRef.html#data-layout
+[linker]: https://en.wikipedia.org/wiki/Linker_(computing)
+
+Também visamos sistemas `x86_64` com nosso kernel, então nossa especificação de alvo será muito similar à acima. Vamos começar criando um arquivo `x86_64-blog_os.json` (escolha qualquer nome que você goste) com o conteúdo comum:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": 64,
+ "target-c-int-width": 32,
+ "os": "none",
+ "executables": true
+}
+```
+
+Note que mudamos o SO no `llvm-target` e no campo `os` para `none`, porque executaremos em bare metal.
+
+Adicionamos as seguintes entradas relacionadas à compilação:
+
+```json
+"linker-flavor": "ld.lld",
+"linker": "rust-lld",
+```
+
+Em vez de usar o linker padrão da plataforma (que pode não suportar alvos Linux), usamos o linker multiplataforma [LLD] que vem com o Rust para vincular nosso kernel.
+
+[LLD]: https://lld.llvm.org/
+
+```json
+"panic-strategy": "abort",
+```
+
+Esta configuração especifica que o alvo não suporta [stack unwinding] no panic, então em vez disso o programa deve abortar diretamente. Isso tem o mesmo efeito que a opção `panic = "abort"` no nosso Cargo.toml, então podemos removê-la de lá. (Note que, em contraste com a opção Cargo.toml, esta opção de alvo também se aplica quando recompilamos a biblioteca `core` mais adiante neste post. Então, mesmo se você preferir manter a opção Cargo.toml, certifique-se de incluir esta opção.)
+
+[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
+
+```json
+"disable-redzone": true,
+```
+
+Estamos escrevendo um kernel, então precisaremos lidar com interrupções em algum momento. Para fazer isso com segurança, temos que desabilitar uma certa otimização do ponteiro de stack chamada _"red zone"_, porque ela causaria corrupção do stack caso contrário. Para mais informações, veja nosso post separado sobre [desabilitando a red zone].
+
+[desabilitando a red zone]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.md
+
+```json
+"features": "-mmx,-sse,+soft-float",
+```
+
+O campo `features` habilita/desabilita recursos do alvo. Desabilitamos os recursos `mmx` e `sse` prefixando-os com um menos e habilitamos o recurso `soft-float` prefixando-o com um mais. Note que não deve haver espaços entre flags diferentes, caso contrário o LLVM falha ao interpretar a string de features.
+
+Os recursos `mmx` e `sse` determinam suporte para instruções [Single Instruction Multiple Data (SIMD)], que frequentemente podem acelerar programas significativamente. No entanto, usar os grandes registradores SIMD em kernels de SO leva a problemas de desempenho. A razão é que o kernel precisa restaurar todos os registradores ao seu estado original antes de continuar um programa interrompido. Isso significa que o kernel tem que salvar o estado SIMD completo na memória principal em cada chamada de sistema ou interrupção de hardware. Como o estado SIMD é muito grande (512-1600 bytes) e interrupções podem ocorrer com muita frequência, essas operações adicionais de salvar/restaurar prejudicam consideravelmente o desempenho. Para evitar isso, desabilitamos SIMD para nosso kernel (não para aplicações executando em cima!).
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+Um problema com desabilitar SIMD é que operações de ponto flutuante em `x86_64` exigem registradores SIMD por padrão. Para resolver este problema, adicionamos o recurso `soft-float`, que emula todas as operações de ponto flutuante através de funções de software baseadas em inteiros normais.
+
+Para mais informações, veja nosso post sobre [desabilitando SIMD](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.md).
+
+```json
+"rustc-abi": "x86-softfloat"
+```
+
+Como queremos usar o recurso `soft-float`, também precisamos dizer ao compilador Rust `rustc` que queremos usar a ABI correspondente. Podemos fazer isso definindo o campo `rustc-abi` para `x86-softfloat`.
+
+#### Juntando Tudo
+Nosso arquivo de especificação de alvo agora se parece com isto:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": 64,
+ "target-c-int-width": 32,
+ "os": "none",
+ "executables": true,
+ "linker-flavor": "ld.lld",
+ "linker": "rust-lld",
+ "panic-strategy": "abort",
+ "disable-redzone": true,
+ "features": "-mmx,-sse,+soft-float",
+ "rustc-abi": "x86-softfloat"
+}
+```
+
+### Construindo nosso Kernel
+Compilar para nosso novo alvo usará convenções Linux, já que o linker-flavor ld.lld instrui o llvm a compilar com a flag `-flavor gnu` (para mais opções de linker, veja [a documentação do rustc](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker-flavor)). Isso significa que precisamos de um ponto de entrada chamado `_start` como descrito no [post anterior]:
+
+[post anterior]: @/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md
+
+```rust
+// src/main.rs
+
+#![no_std] // não vincule a biblioteca padrão do Rust
+#![no_main] // desativar todos os pontos de entrada no nível Rust
+
+use core::panic::PanicInfo;
+
+/// Esta função é chamada em caso de pânico.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+
+#[unsafe(no_mangle)] // não altere (mangle) o nome desta função
+pub extern "C" fn _start() -> ! {
+ // essa função é o ponto de entrada, já que o vinculador procura uma função
+ // denominado `_start` por padrão
+ loop {}
+}
+```
+
+Note que o ponto de entrada precisa ser chamado `_start` independentemente do seu SO host.
+
+Agora podemos construir o kernel para nosso novo alvo passando o nome do arquivo JSON como `--target`:
+
+```
+> cargo build --target x86_64-blog_os.json
+
+error[E0463]: can't find crate for `core`
+```
+
+Falha! O erro nos diz que o compilador Rust não consegue mais encontrar a [biblioteca `core`]. Esta biblioteca contém tipos básicos do Rust como `Result`, `Option` e iteradores, e é implicitamente vinculada a todas as crates `no_std`.
+
+[biblioteca `core`]: https://doc.rust-lang.org/nightly/core/index.html
+
+O problema é que a biblioteca core é distribuída junto com o compilador Rust como uma biblioteca _pré-compilada_. Então ela é válida apenas para target triples host suportados (por exemplo, `x86_64-unknown-linux-gnu`) mas não para nosso alvo customizado. Se quisermos compilar código para outros alvos, precisamos recompilar `core` para esses alvos primeiro.
+
+#### A Opção `build-std`
+
+É aí que entra o [recurso `build-std`] do cargo. Ele permite recompilar `core` e outras crates da biblioteca padrão sob demanda, em vez de usar as versões pré-compiladas enviadas com a instalação do Rust. Este recurso é muito novo e ainda não está finalizado, então é marcado como "unstable" e disponível apenas em [compiladores Rust nightly].
+
+[recurso `build-std`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
+[compiladores Rust nightly]: #instalando-o-rust-nightly
+
+Para usar o recurso, precisamos criar um arquivo de [configuração cargo] local em `.cargo/config.toml` (a pasta `.cargo` deve estar ao lado da sua pasta `src`) com o seguinte conteúdo:
+
+```toml
+# em .cargo/config.toml
+
+[unstable]
+build-std = ["core", "compiler_builtins"]
+```
+
+Isso diz ao cargo que ele deve recompilar as bibliotecas `core` e `compiler_builtins`. Esta última é necessária porque é uma dependência de `core`. Para recompilar essas bibliotecas, o cargo precisa de acesso ao código-fonte do rust, que podemos instalar com `rustup component add rust-src`.
+
+
+
+**Nota:** A chave de configuração `unstable.build-std` requer pelo menos o Rust nightly de 15-07-2020.
+
+
+
+Depois de definir a chave de configuração `unstable.build-std` e instalar o componente `rust-src`, podemos executar novamente nosso comando de compilação:
+
+```
+> cargo build --target x86_64-blog_os.json
+ Compiling core v0.0.0 (/…/rust/src/libcore)
+ Compiling rustc-std-workspace-core v1.99.0 (/…/rust/src/tools/rustc-std-workspace-core)
+ Compiling compiler_builtins v0.1.32
+ Compiling blog_os v0.1.0 (/…/blog_os)
+ Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
+```
+
+Vemos que `cargo build` agora recompila as bibliotecas `core`, `rustc-std-workspace-core` (uma dependência de `compiler_builtins`) e `compiler_builtins` para nosso alvo customizado.
+
+#### Intrínsecos Relacionados a Memória
+
+O compilador Rust assume que um certo conjunto de funções embutidas está disponível para todos os sistemas. A maioria dessas funções é fornecida pela crate `compiler_builtins` que acabamos de recompilar. No entanto, existem algumas funções relacionadas a memória nessa crate que não são habilitadas por padrão porque normalmente são fornecidas pela biblioteca C no sistema. Essas funções incluem `memset`, que define todos os bytes em um bloco de memória para um valor dado, `memcpy`, que copia um bloco de memória para outro, e `memcmp`, que compara dois blocos de memória. Embora não precisássemos de nenhuma dessas funções para compilar nosso kernel agora, elas serão necessárias assim que adicionarmos mais código a ele (por exemplo, ao copiar structs).
+
+Como não podemos vincular à biblioteca C do sistema operacional, precisamos de uma maneira alternativa de fornecer essas funções ao compilador. Uma possível abordagem para isso poderia ser implementar nossas próprias funções `memset` etc. e aplicar o atributo `#[unsafe(no_mangle)]` a elas (para evitar a renomeação automática durante a compilação). No entanto, isso é perigoso, pois o menor erro na implementação dessas funções pode levar a undefined behavior. Por exemplo, implementar `memcpy` com um loop `for` pode resultar em recursão infinita porque loops `for` implicitamente chamam o método da trait [`IntoIterator::into_iter`], que pode chamar `memcpy` novamente. Então é uma boa ideia reutilizar implementações existentes e bem testadas em vez disso.
+
+[`IntoIterator::into_iter`]: https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html#tymethod.into_iter
+
+Felizmente, a crate `compiler_builtins` já contém implementações para todas as funções necessárias, elas estão apenas desabilitadas por padrão para não colidir com as implementações da biblioteca C. Podemos habilitá-las definindo a flag [`build-std-features`] do cargo para `["compiler-builtins-mem"]`. Como a flag `build-std`, esta flag pode ser passada na linha de comando como uma flag `-Z` ou configurada na tabela `unstable` no arquivo `.cargo/config.toml`. Como queremos sempre compilar com esta flag, a opção do arquivo de configuração faz mais sentido para nós:
+
+[`build-std-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std-features
+
+```toml
+# em .cargo/config.toml
+
+[unstable]
+build-std-features = ["compiler-builtins-mem"]
+build-std = ["core", "compiler_builtins"]
+```
+
+(O suporte para o recurso `compiler-builtins-mem` foi [adicionado muito recentemente](https://github.com/rust-lang/rust/pull/77284), então você precisa pelo menos do Rust nightly `2020-09-30` para ele.)
+
+Nos bastidores, esta flag habilita o [recurso `mem`] da crate `compiler_builtins`. O efeito disso é que o atributo `#[unsafe(no_mangle)]` é aplicado às [implementações `memcpy` etc.] da crate, o que as torna disponíveis ao linker.
+
+[recurso `mem`]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L54-L55
+[implementações `memcpy` etc.]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69
+
+Com esta mudança, nosso kernel tem implementações válidas para todas as funções exigidas pelo compilador, então ele continuará a compilar mesmo se nosso código ficar mais complexo.
+
+#### Definir um Alvo Padrão
+
+Para evitar passar o parâmetro `--target` em cada invocação de `cargo build`, podemos sobrescrever o alvo padrão. Para fazer isso, adicionamos o seguinte ao nosso arquivo de [configuração cargo] em `.cargo/config.toml`:
+
+[configuração cargo]: https://doc.rust-lang.org/cargo/reference/config.html
+
+```toml
+# em .cargo/config.toml
+
+[build]
+target = "x86_64-blog_os.json"
+```
+
+Isso diz ao `cargo` para usar nosso alvo `x86_64-blog_os.json` quando nenhum argumento `--target` explícito é passado. Isso significa que agora podemos construir nosso kernel com um simples `cargo build`. Para mais informações sobre opções de configuração do cargo, confira a [documentação oficial][configuração cargo].
+
+Agora podemos construir nosso kernel para um alvo bare metal com um simples `cargo build`. No entanto, nosso ponto de entrada `_start`, que será chamado pelo bootloader, ainda está vazio. É hora de mostrar algo na tela a partir dele.
+
+### Imprimindo na Tela
+A maneira mais fácil de imprimir texto na tela neste estágio é o [buffer de texto VGA]. É uma área de memória especial mapeada para o hardware VGA que contém o conteúdo exibido na tela. Normalmente consiste em 25 linhas que cada uma contém 80 células de caractere. Cada célula de caractere exibe um caractere ASCII com algumas cores de primeiro plano e fundo. A saída da tela se parece com isto:
+
+[buffer de texto VGA]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
+
+
+
+Discutiremos o layout exato do buffer VGA no próximo post, onde escreveremos um primeiro pequeno driver para ele. Para imprimir "Hello World!", só precisamos saber que o buffer está localizado no endereço `0xb8000` e que cada célula de caractere consiste em um byte ASCII e um byte de cor.
+
+A implementação se parece com isto:
+
+```rust
+static HELLO: &[u8] = b"Hello World!";
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ let vga_buffer = 0xb8000 as *mut u8;
+
+ for (i, &byte) in HELLO.iter().enumerate() {
+ unsafe {
+ *vga_buffer.offset(i as isize * 2) = byte;
+ *vga_buffer.offset(i as isize * 2 + 1) = 0xb;
+ }
+ }
+
+ loop {}
+}
+```
+
+Primeiro, convertemos o inteiro `0xb8000` em um [ponteiro bruto]. Então [iteramos] sobre os bytes da [byte string] [static] `HELLO`. Usamos o método [`enumerate`] para obter adicionalmente uma variável em execução `i`. No corpo do loop for, usamos o método [`offset`] para escrever o byte da string e o byte de cor correspondente (`0xb` é um ciano claro).
+
+[iterar]: https://doc.rust-lang.org/stable/book/ch13-02-iterators.html
+[static]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime
+[`enumerate`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate
+[byte string]: https://doc.rust-lang.org/reference/tokens.html#byte-string-literals
+[ponteiro bruto]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
+[`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
+
+Note que há um bloco [`unsafe`] em torno de todas as escritas de memória. A razão é que o compilador Rust não pode provar que os ponteiros brutos que criamos são válidos. Eles poderiam apontar para qualquer lugar e levar à corrupção de dados. Ao colocá-los em um bloco `unsafe`, estamos basicamente dizendo ao compilador que temos absoluta certeza de que as operações são válidas. Note que um bloco `unsafe` não desativa as verificações de segurança do Rust. Ele apenas permite que você faça [cinco coisas adicionais].
+
+[`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html
+[cinco coisas adicionais]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers
+
+Quero enfatizar que **esta não é a maneira como queremos fazer as coisas em Rust!** É muito fácil bagunçar ao trabalhar com ponteiros brutos dentro de blocos unsafe. Por exemplo, poderíamos facilmente escrever além do fim do buffer se não tivermos cuidado.
+
+Então queremos minimizar o uso de `unsafe` o máximo possível. O Rust nos dá a capacidade de fazer isso criando abstrações seguras. Por exemplo, poderíamos criar um tipo de buffer VGA que encapsula toda a unsafety e garante que seja _impossível_ fazer algo errado de fora. Desta forma, precisaríamos apenas de quantidades mínimas de código `unsafe` e poderíamos ter certeza de que não violamos [memory safety]. Criaremos tal abstração de buffer VGA segura no próximo post.
+
+[memory safety]: https://en.wikipedia.org/wiki/Memory_safety
+
+## Executando nosso Kernel
+
+Agora que temos um executável que faz algo perceptível, é hora de executá-lo. Primeiro, precisamos transformar nosso kernel compilado em uma imagem de disco inicializável vinculando-o com um bootloader. Então podemos executar a imagem de disco na máquina virtual [QEMU] ou inicializá-la em hardware real usando um pendrive USB.
+
+### Criando uma Bootimage
+
+Para transformar nosso kernel compilado em uma imagem de disco inicializável, precisamos vinculá-lo com um bootloader. Como aprendemos na [seção sobre boot], o bootloader é responsável por inicializar a CPU e carregar nosso kernel.
+
+[seção sobre boot]: #o-processo-de-boot
+
+Em vez de escrever nosso próprio bootloader, que é um projeto por si só, usamos a crate [`bootloader`]. Esta crate implementa um bootloader BIOS básico sem nenhuma dependência C, apenas Rust e assembly inline. Para usá-lo para inicializar nosso kernel, precisamos adicionar uma dependência nele:
+
+[`bootloader`]: https://crates.io/crates/bootloader
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+bootloader = "0.9"
+```
+
+**Nota:** Este post é compatível apenas com `bootloader v0.9`. Versões mais novas usam um sistema de compilação diferente e resultarão em erros de compilação ao seguir este post.
+
+Adicionar o bootloader como uma dependência não é suficiente para realmente criar uma imagem de disco inicializável. O problema é que precisamos vincular nosso kernel com o bootloader após a compilação, mas o cargo não tem suporte para [scripts pós-compilação].
+
+[scripts pós-compilação]: https://github.com/rust-lang/cargo/issues/545
+
+Para resolver este problema, criamos uma ferramenta chamada `bootimage` que primeiro compila o kernel e o bootloader, e então os vincula juntos para criar uma imagem de disco inicializável. Para instalar a ferramenta, vá para seu diretório home (ou qualquer diretório fora do seu projeto cargo) e execute o seguinte comando no seu terminal:
+
+```
+cargo install bootimage
+```
+
+Para executar `bootimage` e construir o bootloader, você precisa ter o componente rustup `llvm-tools-preview` instalado. Você pode fazer isso executando `rustup component add llvm-tools-preview`.
+
+Depois de instalar `bootimage` e adicionar o componente `llvm-tools-preview`, você pode criar uma imagem de disco inicializável voltando para o diretório do seu projeto cargo e executando:
+
+```
+> cargo bootimage
+```
+
+Vemos que a ferramenta recompila nosso kernel usando `cargo build`, então automaticamente pegará quaisquer mudanças que você fizer. Depois, ela compila o bootloader, o que pode demorar um pouco. Como todas as dependências de crate, ele é compilado apenas uma vez e então armazenado em cache, então compilações subsequentes serão muito mais rápidas. Finalmente, `bootimage` combina o bootloader e seu kernel em uma imagem de disco inicializável.
+
+Após executar o comando, você deve ver uma imagem de disco inicializável chamada `bootimage-blog_os.bin` no seu diretório `target/x86_64-blog_os/debug`. Você pode inicializá-la em uma máquina virtual ou copiá-la para um pendrive USB para inicializá-la em hardware real. (Note que este não é uma imagem de CD, que tem um formato diferente, então gravá-la em um CD não funciona).
+
+#### Como funciona?
+A ferramenta `bootimage` executa os seguintes passos nos bastidores:
+
+- Ela compila nosso kernel para um arquivo [ELF].
+- Ela compila a dependência do bootloader como um executável autônomo.
+- Ela vincula os bytes do arquivo ELF do kernel ao bootloader.
+
+[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+[rust-osdev/bootloader]: https://github.com/rust-osdev/bootloader
+
+Quando inicializado, o bootloader lê e analisa o arquivo ELF anexado. Ele então mapeia os segmentos do programa para endereços virtuais nas tabelas de página, zera a seção `.bss` e configura um stack. Finalmente, ele lê o endereço do ponto de entrada (nossa função `_start`) e salta para ele.
+
+### Inicializando no QEMU
+
+Agora podemos inicializar a imagem de disco em uma máquina virtual. Para inicializá-la no [QEMU], execute o seguinte comando:
+
+[QEMU]: https://www.qemu.org/
+
+```
+> qemu-system-x86_64 -drive format=raw,file=target/x86_64-blog_os/debug/bootimage-blog_os.bin
+```
+
+Isso abre uma janela separada que deve se parecer com isto:
+
+
+
+Vemos que nosso "Hello World!" está visível na tela.
+
+### Máquina Real
+
+Também é possível escrevê-lo em um pendrive USB e inicializá-lo em uma máquina real, **mas tenha cuidado** para escolher o nome correto do dispositivo, porque **tudo naquele dispositivo será sobrescrito**:
+
+```
+> dd if=target/x86_64-blog_os/debug/bootimage-blog_os.bin of=/dev/sdX && sync
+```
+
+Onde `sdX` é o nome do dispositivo do seu pendrive USB.
+
+Depois de escrever a imagem no pendrive USB, você pode executá-la em hardware real inicializando a partir dele. Você provavelmente precisará usar um menu de boot especial ou alterar a ordem de boot na configuração do BIOS para inicializar a partir do pendrive USB. Note que atualmente não funciona para máquinas UEFI, já que a crate `bootloader` ainda não tem suporte UEFI.
+
+### Usando `cargo run`
+
+Para facilitar a execução do nosso kernel no QEMU, podemos definir a chave de configuração `runner` para o cargo:
+
+```toml
+# em .cargo/config.toml
+
+[target.'cfg(target_os = "none")']
+runner = "bootimage runner"
+```
+
+A tabela `target.'cfg(target_os = "none")'` se aplica a todos os alvos cujo campo `"os"` do arquivo de configuração de alvo está definido como `"none"`. Isso inclui nosso alvo `x86_64-blog_os.json`. A chave `runner` especifica o comando que deve ser invocado para `cargo run`. O comando é executado após uma compilação bem-sucedida com o caminho do executável passado como o primeiro argumento. Veja a [documentação do cargo][configuração cargo] para mais detalhes.
+
+O comando `bootimage runner` é especificamente projetado para ser utilizável como um executável `runner`. Ele vincula o executável dado com a dependência do bootloader do projeto e então lança o QEMU. Veja o [Readme do `bootimage`] para mais detalhes e opções de configuração possíveis.
+
+[Readme do `bootimage`]: https://github.com/rust-osdev/bootimage
+
+Agora podemos usar `cargo run` para compilar nosso kernel e inicializá-lo no QEMU.
+
+## O que vem a seguir?
+
+No próximo post, exploraremos o buffer de texto VGA em mais detalhes e escreveremos uma interface segura para ele. Também adicionaremos suporte para a macro `println`.
\ No newline at end of file
From bb66e905cf7777c1783fe2d21324774742ff0904 Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:35:06 -0300
Subject: [PATCH 04/26] [Translation][Portuguese pt-BR] post-3 (edition-2)
---
.../posts/03-vga-text-buffer/index.pt-BR.md | 703 ++++++++++++++++++
1 file changed, 703 insertions(+)
create mode 100644 blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md
new file mode 100644
index 00000000..0990ee31
--- /dev/null
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md
@@ -0,0 +1,703 @@
++++
+title = "Modo de Texto VGA"
+weight = 3
+path = "pt-BR/vga-text-mode"
+date = 2018-02-26
+
+[extra]
+chapter = "Bare Bones"
+# Please update this when updating the translation
+translation_based_on_commit = "9753695744854686a6b80012c89b0d850a44b4b0"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+O [modo de texto VGA] é uma maneira simples de imprimir texto na tela. Neste post, criamos uma interface que torna seu uso seguro e simples ao encapsular toda a unsafety em um módulo separado. Também implementamos suporte para as [macros de formatação] do Rust.
+
+[modo de texto VGA]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
+[macros de formatação]: https://doc.rust-lang.org/std/fmt/#related-macros
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-03`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-03
+
+
+
+## O Buffer de Texto VGA
+Para imprimir um caractere na tela em modo de texto VGA, é preciso escrevê-lo no buffer de texto do hardware VGA. O buffer de texto VGA é um array bidimensional com tipicamente 25 linhas e 80 colunas, que é renderizado diretamente na tela. Cada entrada do array descreve um único caractere da tela através do seguinte formato:
+
+Bit(s) | Valor
+------ | ----------------
+0-7 | Ponto de código ASCII
+8-11 | Cor do primeiro plano
+12-14 | Cor do fundo
+15 | Piscar
+
+O primeiro byte representa o caractere que deve ser impresso na [codificação ASCII]. Para ser mais específico, não é exatamente ASCII, mas um conjunto de caracteres chamado [_página de código 437_] com alguns caracteres adicionais e pequenas modificações. Para simplificar, continuaremos chamando-o de caractere ASCII neste post.
+
+[codificação ASCII]: https://en.wikipedia.org/wiki/ASCII
+[_página de código 437_]: https://en.wikipedia.org/wiki/Code_page_437
+
+O segundo byte define como o caractere é exibido. Os primeiros quatro bits definem a cor do primeiro plano, os próximos três bits a cor do fundo, e o último bit se o caractere deve piscar. As seguintes cores estão disponíveis:
+
+Número | Cor | Número + Bit Brilhante | Cor Brilhante
+------ | ------------- | ---------------------- | --------------
+0x0 | Preto | 0x8 | Cinza Escuro
+0x1 | Azul | 0x9 | Azul Claro
+0x2 | Verde | 0xa | Verde Claro
+0x3 | Ciano | 0xb | Ciano Claro
+0x4 | Vermelho | 0xc | Vermelho Claro
+0x5 | Magenta | 0xd | Rosa
+0x6 | Marrom | 0xe | Amarelo
+0x7 | Cinza Claro | 0xf | Branco
+
+O bit 4 é o _bit brilhante_, que transforma, por exemplo, azul em azul claro. Para a cor de fundo, este bit é reaproveitado como o bit de piscar.
+
+O buffer de texto VGA é acessível via [I/O mapeado em memória] no endereço `0xb8000`. Isso significa que leituras e escritas naquele endereço não acessam a RAM, mas acessam diretamente o buffer de texto no hardware VGA. Isso significa que podemos lê-lo e escrevê-lo através de operações normais de memória naquele endereço.
+
+[I/O mapeado em memória]: https://en.wikipedia.org/wiki/Memory-mapped_I/O
+
+Note que hardware mapeado em memória pode não suportar todas as operações normais de RAM. Por exemplo, um dispositivo poderia suportar apenas leituras byte a byte e retornar lixo quando um `u64` é lido. Felizmente, o buffer de texto [suporta leituras e escritas normais], então não precisamos tratá-lo de maneira especial.
+
+[suporta leituras e escritas normais]: https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vgamem.htm#manip
+
+## Um Módulo Rust
+Agora que sabemos como o buffer VGA funciona, podemos criar um módulo Rust para lidar com a impressão:
+
+```rust
+// em src/main.rs
+mod vga_buffer;
+```
+
+Para o conteúdo deste módulo, criamos um novo arquivo `src/vga_buffer.rs`. Todo o código abaixo vai para nosso novo módulo (a menos que especificado o contrário).
+
+### Cores
+Primeiro, representamos as diferentes cores usando um enum:
+
+```rust
+// em src/vga_buffer.rs
+
+#[allow(dead_code)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum Color {
+ Black = 0,
+ Blue = 1,
+ Green = 2,
+ Cyan = 3,
+ Red = 4,
+ Magenta = 5,
+ Brown = 6,
+ LightGray = 7,
+ DarkGray = 8,
+ LightBlue = 9,
+ LightGreen = 10,
+ LightCyan = 11,
+ LightRed = 12,
+ Pink = 13,
+ Yellow = 14,
+ White = 15,
+}
+```
+Usamos um [enum estilo C] aqui para especificar explicitamente o número para cada cor. Por causa do atributo `repr(u8)`, cada variante do enum é armazenada como um `u8`. Na verdade, 4 bits seriam suficientes, mas Rust não tem um tipo `u4`.
+
+[enum estilo C]: https://doc.rust-lang.org/rust-by-example/custom_types/enum/c_like.html
+
+Normalmente o compilador emitiria um aviso para cada variante não utilizada. Ao usar o atributo `#[allow(dead_code)]`, desabilitamos esses avisos para o enum `Color`.
+
+Ao [derivar] as traits [`Copy`], [`Clone`], [`Debug`], [`PartialEq`] e [`Eq`], habilitamos [semântica de cópia] para o tipo e o tornamos imprimível e comparável.
+
+[derivar]: https://doc.rust-lang.org/rust-by-example/trait/derive.html
+[`Copy`]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
+[`Clone`]: https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html
+[`Debug`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html
+[`PartialEq`]: https://doc.rust-lang.org/nightly/core/cmp/trait.PartialEq.html
+[`Eq`]: https://doc.rust-lang.org/nightly/core/cmp/trait.Eq.html
+[semântica de cópia]: https://doc.rust-lang.org/1.30.0/book/first-edition/ownership.html#copy-types
+
+Para representar um código de cor completo que especifica as cores de primeiro plano e de fundo, criamos um [newtype] em cima de `u8`:
+
+[newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
+
+```rust
+// em src/vga_buffer.rs
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+struct ColorCode(u8);
+
+impl ColorCode {
+ fn new(foreground: Color, background: Color) -> ColorCode {
+ ColorCode((background as u8) << 4 | (foreground as u8))
+ }
+}
+```
+A struct `ColorCode` contém o byte de cor completo, contendo as cores de primeiro plano e de fundo. Como antes, derivamos as traits `Copy` e `Debug` para ela. Para garantir que o `ColorCode` tenha exatamente o mesmo layout de dados que um `u8`, usamos o atributo [`repr(transparent)`].
+
+[`repr(transparent)`]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent
+
+### Buffer de Texto
+Agora podemos adicionar estruturas para representar um caractere da tela e o buffer de texto:
+
+```rust
+// em src/vga_buffer.rs
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(C)]
+struct ScreenChar {
+ ascii_character: u8,
+ color_code: ColorCode,
+}
+
+const BUFFER_HEIGHT: usize = 25;
+const BUFFER_WIDTH: usize = 80;
+
+#[repr(transparent)]
+struct Buffer {
+ chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
+}
+```
+Como a ordenação dos campos em structs padrão é indefinida em Rust, precisamos do atributo [`repr(C)`]. Ele garante que os campos da struct sejam dispostos exatamente como em uma struct C e, portanto, garante a ordenação correta dos campos. Para a struct `Buffer`, usamos [`repr(transparent)`] novamente para garantir que ela tenha o mesmo layout de memória que seu único campo.
+
+[`repr(C)`]: https://doc.rust-lang.org/nightly/nomicon/other-reprs.html#reprc
+
+Para realmente escrever na tela, agora criamos um tipo writer:
+
+```rust
+// em src/vga_buffer.rs
+
+pub struct Writer {
+ column_position: usize,
+ color_code: ColorCode,
+ buffer: &'static mut Buffer,
+}
+```
+O writer sempre escreverá na última linha e deslocará as linhas para cima quando uma linha estiver cheia (ou no `\n`). O campo `column_position` acompanha a posição atual na última linha. As cores de primeiro plano e de fundo atuais são especificadas por `color_code` e uma referência ao buffer VGA é armazenada em `buffer`. Note que precisamos de um [lifetime explícito] aqui para dizer ao compilador por quanto tempo a referência é válida. O lifetime [`'static`] especifica que a referência é válida durante toda a execução do programa (o que é verdade para o buffer de texto VGA).
+
+[lifetime explícito]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax
+[`'static`]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime
+
+### Impressão
+Agora podemos usar o `Writer` para modificar os caracteres do buffer. Primeiro criamos um método para escrever um único byte ASCII:
+
+```rust
+// em src/vga_buffer.rs
+
+impl Writer {
+ pub fn write_byte(&mut self, byte: u8) {
+ match byte {
+ b'\n' => self.new_line(),
+ byte => {
+ if self.column_position >= BUFFER_WIDTH {
+ self.new_line();
+ }
+
+ let row = BUFFER_HEIGHT - 1;
+ let col = self.column_position;
+
+ let color_code = self.color_code;
+ self.buffer.chars[row][col] = ScreenChar {
+ ascii_character: byte,
+ color_code,
+ };
+ self.column_position += 1;
+ }
+ }
+ }
+
+ fn new_line(&mut self) {/* TODO */}
+}
+```
+Se o byte é o byte de [newline] `\n`, o writer não imprime nada. Em vez disso, ele chama um método `new_line`, que implementaremos mais tarde. Outros bytes são impressos na tela no segundo caso `match`.
+
+[newline]: https://en.wikipedia.org/wiki/Newline
+
+Ao imprimir um byte, o writer verifica se a linha atual está cheia. Nesse caso, uma chamada `new_line` é usada para quebrar a linha. Então ele escreve um novo `ScreenChar` no buffer na posição atual. Finalmente, a posição da coluna atual é avançada.
+
+Para imprimir strings inteiras, podemos convertê-las em bytes e imprimi-los um por um:
+
+```rust
+// em src/vga_buffer.rs
+
+impl Writer {
+ pub fn write_string(&mut self, s: &str) {
+ for byte in s.bytes() {
+ match byte {
+ // byte ASCII imprimível ou newline
+ 0x20..=0x7e | b'\n' => self.write_byte(byte),
+ // não faz parte da faixa ASCII imprimível
+ _ => self.write_byte(0xfe),
+ }
+
+ }
+ }
+}
+```
+
+O buffer de texto VGA suporta apenas ASCII e os bytes adicionais da [página de código 437]. Strings Rust são [UTF-8] por padrão, então podem conter bytes que não são suportados pelo buffer de texto VGA. Usamos um `match` para diferenciar bytes ASCII imprimíveis (um newline ou qualquer coisa entre um caractere de espaço e um caractere `~`) e bytes não imprimíveis. Para bytes não imprimíveis, imprimimos um caractere `■`, que tem o código hexadecimal `0xfe` no hardware VGA.
+
+[página de código 437]: https://en.wikipedia.org/wiki/Code_page_437
+[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
+
+#### Experimente!
+Para escrever alguns caracteres na tela, você pode criar uma função temporária:
+
+```rust
+// em src/vga_buffer.rs
+
+pub fn print_something() {
+ let mut writer = Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ };
+
+ writer.write_byte(b'H');
+ writer.write_string("ello ");
+ writer.write_string("Wörld!");
+}
+```
+Primeiro ele cria um novo Writer que aponta para o buffer VGA em `0xb8000`. A sintaxe para isso pode parecer um pouco estranha: Primeiro, convertemos o inteiro `0xb8000` como um [ponteiro bruto] mutável. Então o convertemos em uma referência mutável ao desreferenciá-lo (através de `*`) e imediatamente emprestar novamente (através de `&mut`). Esta conversão requer um [bloco `unsafe`], pois o compilador não pode garantir que o ponteiro bruto é válido.
+
+[ponteiro bruto]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
+[bloco `unsafe`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
+
+Então ele escreve o byte `b'H'` nele. O prefixo `b` cria um [byte literal], que representa um caractere ASCII. Ao escrever as strings `"ello "` e `"Wörld!"`, testamos nosso método `write_string` e o tratamento de caracteres não imprimíveis. Para ver a saída, precisamos chamar a função `print_something` da nossa função `_start`:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ vga_buffer::print_something();
+
+ loop {}
+}
+```
+
+Quando executamos nosso projeto agora, um `Hello W■■rld!` deve ser impresso no canto inferior _esquerdo_ da tela em amarelo:
+
+[byte literal]: https://doc.rust-lang.org/reference/tokens.html#byte-literals
+
+
+
+Note que o `ö` é impresso como dois caracteres `■`. Isso ocorre porque `ö` é representado por dois bytes em [UTF-8], que ambos não se enquadram na faixa ASCII imprimível. Na verdade, esta é uma propriedade fundamental do UTF-8: os bytes individuais de valores multi-byte nunca são ASCII válido.
+
+### Volatile
+Acabamos de ver que nossa mensagem foi impressa corretamente. No entanto, pode não funcionar com futuros compiladores Rust que otimizam de forma mais agressiva.
+
+O problema é que escrevemos apenas no `Buffer` e nunca lemos dele novamente. O compilador não sabe que realmente acessamos memória do buffer VGA (em vez de RAM normal) e não sabe nada sobre o efeito colateral de que alguns caracteres aparecem na tela. Então ele pode decidir que essas escritas são desnecessárias e podem ser omitidas. Para evitar esta otimização errônea, precisamos especificar essas escritas como _[volatile]_. Isso diz ao compilador que a escrita tem efeitos colaterais e não deve ser otimizada.
+
+[volatile]: https://en.wikipedia.org/wiki/Volatile_(computer_programming)
+
+Para usar escritas volatile para o buffer VGA, usamos a biblioteca [volatile][volatile crate]. Esta _crate_ (é assim que os pacotes são chamados no mundo Rust) fornece um tipo wrapper `Volatile` com métodos `read` e `write`. Esses métodos usam internamente as funções [read_volatile] e [write_volatile] da biblioteca core e, portanto, garantem que as leituras/escritas não sejam otimizadas.
+
+[volatile crate]: https://docs.rs/volatile
+[read_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.read_volatile.html
+[write_volatile]: https://doc.rust-lang.org/nightly/core/ptr/fn.write_volatile.html
+
+Podemos adicionar uma dependência na crate `volatile` adicionando-a à seção `dependencies` do nosso `Cargo.toml`:
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+volatile = "0.2.6"
+```
+
+Certifique-se de especificar a versão `0.2.6` do `volatile`. Versões mais novas da crate não são compatíveis com este post.
+`0.2.6` é o número de versão [semântico]. Para mais informações, veja o guia [Specifying Dependencies] da documentação do cargo.
+
+[semântico]: https://semver.org/
+[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
+
+Vamos usá-lo para tornar as escritas no buffer VGA volatile. Atualizamos nosso tipo `Buffer` da seguinte forma:
+
+```rust
+// em src/vga_buffer.rs
+
+use volatile::Volatile;
+
+struct Buffer {
+ chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT],
+}
+```
+Em vez de um `ScreenChar`, agora estamos usando um `Volatile`. (O tipo `Volatile` é [genérico] e pode envolver (quase) qualquer tipo). Isso garante que não possamos escrever nele "normalmente" acidentalmente. Em vez disso, temos que usar o método `write` agora.
+
+[genérico]: https://doc.rust-lang.org/book/ch10-01-syntax.html
+
+Isso significa que temos que atualizar nosso método `Writer::write_byte`:
+
+```rust
+// em src/vga_buffer.rs
+
+impl Writer {
+ pub fn write_byte(&mut self, byte: u8) {
+ match byte {
+ b'\n' => self.new_line(),
+ byte => {
+ ...
+
+ self.buffer.chars[row][col].write(ScreenChar {
+ ascii_character: byte,
+ color_code,
+ });
+ ...
+ }
+ }
+ }
+ ...
+}
+```
+
+Em vez de uma atribuição típica usando `=`, agora estamos usando o método `write`. Agora podemos garantir que o compilador nunca otimizará esta escrita.
+
+### Macros de Formatação
+Seria bom suportar as macros de formatação do Rust também. Dessa forma, podemos facilmente imprimir diferentes tipos, como inteiros ou floats. Para suportá-las, precisamos implementar a trait [`core::fmt::Write`]. O único método necessário desta trait é `write_str`, que se parece bastante com nosso método `write_string`, apenas com um tipo de retorno `fmt::Result`:
+
+[`core::fmt::Write`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html
+
+```rust
+// em src/vga_buffer.rs
+
+use core::fmt;
+
+impl fmt::Write for Writer {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.write_string(s);
+ Ok(())
+ }
+}
+```
+O `Ok(())` é apenas um Result `Ok` contendo o tipo `()`.
+
+Agora podemos usar as macros de formatação `write!`/`writeln!` embutidas do Rust:
+
+```rust
+// em src/vga_buffer.rs
+
+pub fn print_something() {
+ use core::fmt::Write;
+ let mut writer = Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ };
+
+ writer.write_byte(b'H');
+ writer.write_string("ello! ");
+ write!(writer, "The numbers are {} and {}", 42, 1.0/3.0).unwrap();
+}
+```
+
+Agora você deve ver um `Hello! The numbers are 42 and 0.3333333333333333` na parte inferior da tela. A chamada `write!` retorna um `Result` que causa um aviso se não usado, então chamamos a função [`unwrap`] nele, que entra em panic se ocorrer um erro. Isso não é um problema no nosso caso, pois escritas no buffer VGA nunca falham.
+
+[`unwrap`]: https://doc.rust-lang.org/core/result/enum.Result.html#method.unwrap
+
+### Newlines
+Agora, simplesmente ignoramos newlines e caracteres que não cabem mais na linha. Em vez disso, queremos mover cada caractere uma linha para cima (a linha superior é excluída) e começar no início da última linha novamente. Para fazer isso, adicionamos uma implementação para o método `new_line` do `Writer`:
+
+```rust
+// em src/vga_buffer.rs
+
+impl Writer {
+ fn new_line(&mut self) {
+ for row in 1..BUFFER_HEIGHT {
+ for col in 0..BUFFER_WIDTH {
+ let character = self.buffer.chars[row][col].read();
+ self.buffer.chars[row - 1][col].write(character);
+ }
+ }
+ self.clear_row(BUFFER_HEIGHT - 1);
+ self.column_position = 0;
+ }
+
+ fn clear_row(&mut self, row: usize) {/* TODO */}
+}
+```
+Iteramos sobre todos os caracteres da tela e movemos cada caractere uma linha para cima. Note que o limite superior da notação de intervalo (`..`) é exclusivo. Também omitimos a linha 0 (o primeiro intervalo começa em `1`) porque é a linha que é deslocada para fora da tela.
+
+Para finalizar o código de newline, adicionamos o método `clear_row`:
+
+```rust
+// em src/vga_buffer.rs
+
+impl Writer {
+ fn clear_row(&mut self, row: usize) {
+ let blank = ScreenChar {
+ ascii_character: b' ',
+ color_code: self.color_code,
+ };
+ for col in 0..BUFFER_WIDTH {
+ self.buffer.chars[row][col].write(blank);
+ }
+ }
+}
+```
+Este método limpa uma linha sobrescrevendo todos os seus caracteres com um caractere de espaço.
+
+## Uma Interface Global
+Para fornecer um writer global que possa ser usado como uma interface de outros módulos sem carregar uma instância `Writer` por aí, tentamos criar um `WRITER` static:
+
+```rust
+// em src/vga_buffer.rs
+
+pub static WRITER: Writer = Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+};
+```
+
+No entanto, se tentarmos compilá-lo agora, os seguintes erros ocorrem:
+
+```
+error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+ --> src/vga_buffer.rs:7:17
+ |
+7 | color_code: ColorCode::new(Color::Yellow, Color::Black),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0396]: raw pointers cannot be dereferenced in statics
+ --> src/vga_buffer.rs:8:22
+ |
+8 | buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer in constant
+
+error[E0017]: references in statics may only refer to immutable values
+ --> src/vga_buffer.rs:8:22
+ |
+8 | buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ statics require immutable values
+
+error[E0017]: references in statics may only refer to immutable values
+ --> src/vga_buffer.rs:8:13
+ |
+8 | buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ statics require immutable values
+```
+
+Para entender o que está acontecendo aqui, precisamos saber que statics são inicializados em tempo de compilação, ao contrário de variáveis normais que são inicializadas em tempo de execução. O componente do compilador Rust que avalia tais expressões de inicialização é chamado de "[const evaluator]". Sua funcionalidade ainda é limitada, mas há trabalho contínuo para expandi-la, por exemplo no RFC "[Allow panicking in constants]".
+
+[const evaluator]: https://rustc-dev-guide.rust-lang.org/const-eval.html
+[Allow panicking in constants]: https://github.com/rust-lang/rfcs/pull/2345
+
+O problema com `ColorCode::new` seria solucionável usando [funções `const`], mas o problema fundamental aqui é que o const evaluator do Rust não é capaz de converter ponteiros brutos em referências em tempo de compilação. Talvez funcione algum dia, mas até lá, precisamos encontrar outra solução.
+
+[funções `const`]: https://doc.rust-lang.org/reference/const_eval.html#const-functions
+
+### Lazy Statics
+A inicialização única de statics com funções não const é um problema comum em Rust. Felizmente, já existe uma boa solução em uma crate chamada [lazy_static]. Esta crate fornece uma macro `lazy_static!` que define um `static` inicializado lazily. Em vez de calcular seu valor em tempo de compilação, o `static` se inicializa lazily quando é acessado pela primeira vez. Assim, a inicialização acontece em tempo de execução, então código de inicialização arbitrariamente complexo é possível.
+
+[lazy_static]: https://docs.rs/lazy_static/1.0.1/lazy_static/
+
+Vamos adicionar a crate `lazy_static` ao nosso projeto:
+
+```toml
+# em Cargo.toml
+
+[dependencies.lazy_static]
+version = "1.0"
+features = ["spin_no_std"]
+```
+
+Precisamos do recurso `spin_no_std`, já que não vinculamos a biblioteca padrão.
+
+Com `lazy_static`, podemos definir nosso `WRITER` static sem problemas:
+
+```rust
+// em src/vga_buffer.rs
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+ pub static ref WRITER: Writer = Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ };
+}
+```
+
+No entanto, este `WRITER` é praticamente inútil, pois é imutável. Isso significa que não podemos escrever nada nele (já que todos os métodos de escrita recebem `&mut self`). Uma solução possível seria usar um [static mutável]. Mas então cada leitura e escrita nele seria unsafe, pois poderia facilmente introduzir data races e outras coisas ruins. Usar `static mut` é altamente desencorajado. Até houve propostas para [removê-lo][remove static mut]. Mas quais são as alternativas? Poderíamos tentar usar um static imutável com um tipo de célula como [RefCell] ou até [UnsafeCell] que fornece [mutabilidade interior]. Mas esses tipos não são [Sync] \(com boa razão), então não podemos usá-los em statics.
+
+[static mutável]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
+[remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437
+[RefCell]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt
+[UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
+[mutabilidade interior]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
+[Sync]: https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html
+
+### Spinlocks
+Para obter mutabilidade interior sincronizada, usuários da biblioteca padrão podem usar [Mutex]. Ele fornece exclusão mútua bloqueando threads quando o recurso já está bloqueado. Mas nosso kernel básico não tem nenhum suporte de bloqueio ou mesmo um conceito de threads, então também não podemos usá-lo. No entanto, há um tipo realmente básico de mutex na ciência da computação que não requer nenhum recurso de sistema operacional: o [spinlock]. Em vez de bloquear, as threads simplesmente tentam bloqueá-lo novamente e novamente em um loop apertado, queimando assim tempo de CPU até que o mutex esteja livre novamente.
+
+[Mutex]: https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html
+[spinlock]: https://en.wikipedia.org/wiki/Spinlock
+
+Para usar um spinning mutex, podemos adicionar a [crate spin] como uma dependência:
+
+[crate spin]: https://crates.io/crates/spin
+
+```toml
+# em Cargo.toml
+[dependencies]
+spin = "0.5.2"
+```
+
+Então podemos usar o spinning mutex para adicionar [mutabilidade interior] segura ao nosso `WRITER` static:
+
+```rust
+// em src/vga_buffer.rs
+
+use spin::Mutex;
+...
+lazy_static! {
+ pub static ref WRITER: Mutex = Mutex::new(Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ });
+}
+```
+Agora podemos deletar a função `print_something` e imprimir diretamente da nossa função `_start`:
+
+```rust
+// em src/main.rs
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ use core::fmt::Write;
+ vga_buffer::WRITER.lock().write_str("Hello again").unwrap();
+ write!(vga_buffer::WRITER.lock(), ", some numbers: {} {}", 42, 1.337).unwrap();
+
+ loop {}
+}
+```
+Precisamos importar a trait `fmt::Write` para poder usar suas funções.
+
+### Segurança
+Note que temos apenas um bloco unsafe no nosso código, que é necessário para criar uma referência `Buffer` apontando para `0xb8000`. Depois disso, todas as operações são seguras. Rust usa verificação de limites para acessos a arrays por padrão, então não podemos escrever acidentalmente fora do buffer. Assim, codificamos as condições necessárias no sistema de tipos e somos capazes de fornecer uma interface segura para o exterior.
+
+### Uma Macro println
+Agora que temos um writer global, podemos adicionar uma macro `println` que pode ser usada de qualquer lugar na base de código. A [sintaxe de macro] do Rust é um pouco estranha, então não tentaremos escrever uma macro do zero. Em vez disso, olhamos para o código-fonte da [macro `println!`] na biblioteca padrão:
+
+[sintaxe de macro]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming
+[macro `println!`]: https://doc.rust-lang.org/nightly/std/macro.println!.html
+
+```rust
+#[macro_export]
+macro_rules! println {
+ () => (print!("\n"));
+ ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*)));
+}
+```
+
+Macros são definidas através de uma ou mais regras, semelhantes a braços `match`. A macro `println` tem duas regras: A primeira regra é para invocações sem argumentos, por exemplo, `println!()`, que é expandida para `print!("\n")` e, portanto, apenas imprime um newline. A segunda regra é para invocações com parâmetros como `println!("Hello")` ou `println!("Number: {}", 4)`. Ela também é expandida para uma invocação da macro `print!`, passando todos os argumentos e um newline `\n` adicional no final.
+
+O atributo `#[macro_export]` torna a macro disponível para toda a crate (não apenas o módulo em que é definida) e crates externas. Ele também coloca a macro na raiz da crate, o que significa que temos que importar a macro através de `use std::println` em vez de `std::macros::println`.
+
+A [macro `print!`] é definida como:
+
+[macro `print!`]: https://doc.rust-lang.org/nightly/std/macro.print!.html
+
+```rust
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
+}
+```
+
+A macro se expande para uma chamada da [função `_print`] no módulo `io`. A [variável `$crate`] garante que a macro também funcione de fora da crate `std` ao se expandir para `std` quando é usada em outras crates.
+
+A [macro `format_args`] constrói um tipo [fmt::Arguments] dos argumentos passados, que é passado para `_print`. A [função `_print`] da libstd chama `print_to`, que é bastante complicado porque suporta diferentes dispositivos `Stdout`. Não precisamos dessa complexidade, pois só queremos imprimir no buffer VGA.
+
+[função `_print`]: https://github.com/rust-lang/rust/blob/29f5c699b11a6a148f097f82eaa05202f8799bbc/src/libstd/io/stdio.rs#L698
+[variável `$crate`]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#the-variable-crate
+[macro `format_args`]: https://doc.rust-lang.org/nightly/std/macro.format_args.html
+[fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html
+
+Para imprimir no buffer VGA, apenas copiamos as macros `println!` e `print!`, mas as modificamos para usar nossa própria função `_print`:
+
+```rust
+// em src/vga_buffer.rs
+
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
+}
+
+#[macro_export]
+macro_rules! println {
+ () => ($crate::print!("\n"));
+ ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
+}
+
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+ use core::fmt::Write;
+ WRITER.lock().write_fmt(args).unwrap();
+}
+```
+
+Uma coisa que mudamos da definição original de `println` é que também prefixamos as invocações da macro `print!` com `$crate`. Isso garante que não precisamos importar a macro `print!` também se quisermos usar apenas `println`.
+
+Como na biblioteca padrão, adicionamos o atributo `#[macro_export]` a ambas as macros para torná-las disponíveis em todo lugar na nossa crate. Note que isso coloca as macros no namespace raiz da crate, então importá-las via `use crate::vga_buffer::println` não funciona. Em vez disso, temos que fazer `use crate::println`.
+
+A função `_print` bloqueia nosso `WRITER` static e chama o método `write_fmt` nele. Este método é da trait `Write`, que precisamos importar. O `unwrap()` adicional no final entra em panic se a impressão não for bem-sucedida. Mas como sempre retornamos `Ok` em `write_str`, isso não deve acontecer.
+
+Como as macros precisam ser capazes de chamar `_print` de fora do módulo, a função precisa ser pública. No entanto, como consideramos isso um detalhe de implementação privado, adicionamos o [atributo `doc(hidden)`] para ocultá-la da documentação gerada.
+
+[atributo `doc(hidden)`]: https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#hidden
+
+### Hello World usando `println`
+Agora podemos usar `println` na nossa função `_start`:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Hello World{}", "!");
+
+ loop {}
+}
+```
+
+Note que não precisamos importar a macro na função main, porque ela já vive no namespace raiz.
+
+Como esperado, agora vemos um _"Hello World!"_ na tela:
+
+
+
+### Imprimindo Mensagens de Panic
+
+Agora que temos uma macro `println`, podemos usá-la na nossa função panic para imprimir a mensagem de panic e a localização do panic:
+
+```rust
+// em main.rs
+
+/// Esta função é chamada em caso de pânico.
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ println!("{}", info);
+ loop {}
+}
+```
+
+Quando agora inserimos `panic!("Some panic message");` na nossa função `_start`, obtemos a seguinte saída:
+
+
+
+Então sabemos não apenas que um panic ocorreu, mas também a mensagem de panic e onde no código aconteceu.
+
+## Resumo
+Neste post, aprendemos sobre a estrutura do buffer de texto VGA e como ele pode ser escrito através do mapeamento de memória no endereço `0xb8000`. Criamos um módulo Rust que encapsula a unsafety de escrever neste buffer mapeado em memória e apresenta uma interface segura e conveniente para o exterior.
+
+Graças ao cargo, também vimos como é fácil adicionar dependências em bibliotecas de terceiros. As duas dependências que adicionamos, `lazy_static` e `spin`, são muito úteis no desenvolvimento de SO e as usaremos em mais lugares em posts futuros.
+
+## O que vem a seguir?
+O próximo post explica como configurar o framework de testes unitários embutido do Rust. Criaremos então alguns testes unitários básicos para o módulo de buffer VGA deste post.
\ No newline at end of file
From 0aaf8e8d466ee5111b57f7317c95894e377aadeb Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:45:18 -0300
Subject: [PATCH 05/26] [Translation][Portuguese pt-BR] post-4 (edition-2)
---
.../edition-2/posts/04-testing/index.pt-BR.md | 1044 +++++++++++++++++
1 file changed, 1044 insertions(+)
create mode 100644 blog/content/edition-2/posts/04-testing/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/04-testing/index.pt-BR.md b/blog/content/edition-2/posts/04-testing/index.pt-BR.md
new file mode 100644
index 00000000..8d20f803
--- /dev/null
+++ b/blog/content/edition-2/posts/04-testing/index.pt-BR.md
@@ -0,0 +1,1044 @@
++++
+title = "Testes"
+weight = 4
+path = "pt-BR/testing"
+date = 2019-04-27
+
+[extra]
+chapter = "Bare Bones"
+comments_search_term = 1009
+# Please update this when updating the translation
+translation_based_on_commit = "33b7979468235b8637584e91e4c599cef37d9687"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Este post explora testes unitários e de integração em executáveis `no_std`. Usaremos o suporte do Rust para frameworks de teste customizados para executar funções de teste dentro do nosso kernel. Para reportar os resultados para fora do QEMU, usaremos diferentes recursos do QEMU e da ferramenta `bootimage`.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-04`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-04
+
+
+
+## Requisitos
+
+Este post substitui os posts (agora deprecados) [_Unit Testing_] e [_Integration Tests_]. Ele assume que você seguiu o post [_A Minimal Rust Kernel_] depois de 27-04-2019. Principalmente, ele requer que você tenha um arquivo `.cargo/config.toml` que [define um alvo padrão] e [define um executável runner].
+
+[_Unit Testing_]: @/edition-2/posts/deprecated/04-unit-testing/index.md
+[_Integration Tests_]: @/edition-2/posts/deprecated/05-integration-tests/index.md
+[_A Minimal Rust Kernel_]: @/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md
+[define um alvo padrão]: @/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md#definir-um-alvo-padrao
+[define um executável runner]: @/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md#usando-cargo-run
+
+## Testes em Rust
+
+Rust tem um [framework de testes integrado] que é capaz de executar testes unitários sem a necessidade de configurar nada. Basta criar uma função que verifica alguns resultados através de assertions e adicionar o atributo `#[test]` ao cabeçalho da função. Então `cargo test` automaticamente encontrará e executará todas as funções de teste da sua crate.
+
+[framework de testes integrado]: https://doc.rust-lang.org/book/ch11-00-testing.html
+
+Para habilitar testes para nosso binário kernel, podemos definir a flag `test` no Cargo.toml como `true`:
+
+```toml
+# em Cargo.toml
+
+[[bin]]
+name = "blog_os"
+test = true
+bench = false
+```
+
+Esta [seção `[[bin]]`](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target) especifica como o `cargo` deve compilar nosso executável `blog_os`. O campo `test` especifica se testes são suportados para este executável. Definimos `test = false` no primeiro post para [deixar o `rust-analyzer` feliz](@/edition-2/posts/01-freestanding-rust-binary/index.pt-BR.md#deixando-rust-analyzer-feliz), mas agora queremos habilitar testes, então o definimos de volta para `true`.
+
+Infelizmente, testes são um pouco mais complicados para aplicações `no_std` como nosso kernel. O problema é que o framework de testes do Rust usa implicitamente a biblioteca [`test`] integrada, que depende da biblioteca padrão. Isso significa que não podemos usar o framework de testes padrão para nosso kernel `#[no_std]`.
+
+[`test`]: https://doc.rust-lang.org/test/index.html
+
+Podemos ver isso quando tentamos executar `cargo test` no nosso projeto:
+
+```
+> cargo test
+ Compiling blog_os v0.1.0 (/…/blog_os)
+error[E0463]: can't find crate for `test`
+```
+
+Como a crate `test` depende da biblioteca padrão, ela não está disponível para nosso alvo bare metal. Embora portar a crate `test` para um contexto `#[no_std]` [seja possível][utest], é altamente instável e requer alguns hacks, como redefinir a macro `panic`.
+
+[utest]: https://github.com/japaric/utest
+
+### Frameworks de Teste Customizados
+
+Felizmente, Rust suporta substituir o framework de testes padrão através do recurso instável [`custom_test_frameworks`]. Este recurso não requer bibliotecas externas e, portanto, também funciona em ambientes `#[no_std]`. Funciona coletando todas as funções anotadas com um atributo `#[test_case]` e então invocando uma função runner especificada pelo usuário com a lista de testes como argumento. Assim, dá à implementação controle máximo sobre o processo de teste.
+
+[`custom_test_frameworks`]: https://doc.rust-lang.org/unstable-book/language-features/custom-test-frameworks.html
+
+A desvantagem comparada ao framework de testes padrão é que muitos recursos avançados, como [testes `should_panic`], não estão disponíveis. Em vez disso, cabe à implementação fornecer tais recursos ela mesma se necessário. Isso é ideal para nós, pois temos um ambiente de execução muito especial onde as implementações padrão de tais recursos avançados provavelmente não funcionariam de qualquer forma. Por exemplo, o atributo `#[should_panic]` depende de stack unwinding para capturar os panics, que desabilitamos para nosso kernel.
+
+[testes `should_panic`]: https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic
+
+Para implementar um framework de testes customizado para nosso kernel, adicionamos o seguinte ao nosso `main.rs`:
+
+```rust
+// em src/main.rs
+
+#![feature(custom_test_frameworks)]
+#![test_runner(crate::test_runner)]
+
+#[cfg(test)]
+pub fn test_runner(tests: &[&dyn Fn()]) {
+ println!("Running {} tests", tests.len());
+ for test in tests {
+ test();
+ }
+}
+```
+
+Nosso runner apenas imprime uma breve mensagem de debug e então chama cada função de teste na lista. O tipo de argumento `&[&dyn Fn()]` é uma [_slice_] de referências a [_trait object_] da trait [_Fn()_]. É basicamente uma lista de referências a tipos que podem ser chamados como uma função. Como a função é inútil para execuções não-teste, usamos o atributo `#[cfg(test)]` para incluí-la apenas para testes.
+
+[_slice_]: https://doc.rust-lang.org/std/primitive.slice.html
+[_trait object_]: https://doc.rust-lang.org/1.30.0/book/first-edition/trait-objects.html
+[_Fn()_]: https://doc.rust-lang.org/std/ops/trait.Fn.html
+
+Quando executamos `cargo test` agora, vemos que ele agora é bem-sucedido (se não for, veja a nota abaixo). No entanto, ainda vemos nosso "Hello World" em vez da mensagem do nosso `test_runner`. A razão é que nossa função `_start` ainda é usada como ponto de entrada. O recurso de frameworks de teste customizados gera uma função `main` que chama `test_runner`, mas esta função é ignorada porque usamos o atributo `#[no_main]` e fornecemos nosso próprio ponto de entrada.
+
+
+
+**Nota:** Atualmente há um bug no cargo que leva a erros de "duplicate lang item" no `cargo test` em alguns casos. Ocorre quando você definiu `panic = "abort"` para um profile no seu `Cargo.toml`. Tente removê-lo, então `cargo test` deve funcionar. Alternativamente, se isso não funcionar, então adicione `panic-abort-tests = true` à seção `[unstable]` do seu arquivo `.cargo/config.toml`. Veja o [issue do cargo](https://github.com/rust-lang/cargo/issues/7359) para mais informações sobre isso.
+
+
+
+Para corrigir isso, primeiro precisamos mudar o nome da função gerada para algo diferente de `main` através do atributo `reexport_test_harness_main`. Então podemos chamar a função renomeada da nossa função `_start`:
+
+```rust
+// em src/main.rs
+
+#![reexport_test_harness_main = "test_main"]
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Hello World{}", "!");
+
+ #[cfg(test)]
+ test_main();
+
+ loop {}
+}
+```
+
+Definimos o nome da função de entrada do framework de testes como `test_main` e a chamamos do nosso ponto de entrada `_start`. Usamos [compilação condicional] para adicionar a chamada a `test_main` apenas em contextos de teste porque a função não é gerada em uma execução normal.
+
+Quando agora executamos `cargo test`, vemos a mensagem "Running 0 tests" do nosso `test_runner` na tela. Agora estamos prontos para criar nossa primeira função de teste:
+
+```rust
+// em src/main.rs
+
+#[test_case]
+fn trivial_assertion() {
+ print!("trivial assertion... ");
+ assert_eq!(1, 1);
+ println!("[ok]");
+}
+```
+
+Quando executamos `cargo test` agora, vemos a seguinte saída:
+
+![QEMU imprimindo "Hello World!", "Running 1 tests" e "trivial assertion... [ok]"](qemu-test-runner-output.png)
+
+A slice `tests` passada para nossa função `test_runner` agora contém uma referência à função `trivial_assertion`. Da saída `trivial assertion... [ok]` na tela, vemos que o teste foi chamado e que foi bem-sucedido.
+
+Após executar os testes, nosso `test_runner` retorna à função `test_main`, que por sua vez retorna à nossa função de ponto de entrada `_start`. No final de `_start`, entramos em um loop infinito porque a função de ponto de entrada não tem permissão para retornar. Isso é um problema, porque queremos que `cargo test` saia após executar todos os testes.
+
+## Saindo do QEMU
+
+Agora, temos um loop infinito no final da nossa função `_start` e precisamos fechar o QEMU manualmente em cada execução de `cargo test`. Isso é infeliz porque também queremos executar `cargo test` em scripts sem interação do usuário. A solução limpa para isso seria implementar uma maneira adequada de desligar nosso SO. Infelizmente, isso é relativamente complexo porque requer implementar suporte para o padrão de gerenciamento de energia [APM] ou [ACPI].
+
+[APM]: https://wiki.osdev.org/APM
+[ACPI]: https://wiki.osdev.org/ACPI
+
+Felizmente, há uma saída de emergência: O QEMU suporta um dispositivo especial `isa-debug-exit`, que fornece uma maneira fácil de sair do QEMU do sistema guest. Para habilitá-lo, precisamos passar um argumento `-device` ao QEMU. Podemos fazer isso adicionando uma chave de configuração `package.metadata.bootimage.test-args` no nosso `Cargo.toml`:
+
+```toml
+# em Cargo.toml
+
+[package.metadata.bootimage]
+test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]
+```
+
+O `bootimage runner` anexa os `test-args` ao comando QEMU padrão para todos os executáveis de teste. Para um `cargo run` normal, os argumentos são ignorados.
+
+Junto com o nome do dispositivo (`isa-debug-exit`), passamos os dois parâmetros `iobase` e `iosize` que especificam a _porta I/O_ através da qual o dispositivo pode ser alcançado do nosso kernel.
+
+### Portas I/O
+
+Existem duas abordagens diferentes para comunicação entre a CPU e hardware periférico no x86, **I/O mapeado em memória** e **I/O mapeado em porta**. Já usamos I/O mapeado em memória para acessar o [buffer de texto VGA] através do endereço de memória `0xb8000`. Este endereço não é mapeado para RAM, mas para alguma memória no dispositivo VGA.
+
+[buffer de texto VGA]: @/edition-2/posts/03-vga-text-buffer/index.pt-BR.md
+
+Em contraste, I/O mapeado em porta usa um barramento I/O separado para comunicação. Cada periférico conectado tem um ou mais números de porta. Para comunicar com tal porta I/O, existem instruções especiais de CPU chamadas `in` e `out`, que recebem um número de porta e um byte de dados (também há variações desses comandos que permitem enviar um `u16` ou `u32`).
+
+O dispositivo `isa-debug-exit` usa I/O mapeado em porta. O parâmetro `iobase` especifica em qual endereço de porta o dispositivo deve viver (`0xf4` é uma porta [geralmente não utilizada][lista de portas I/O x86] no barramento IO do x86) e o `iosize` especifica o tamanho da porta (`0x04` significa quatro bytes).
+
+[lista de portas I/O x86]: https://wiki.osdev.org/I/O_Ports#The_list
+
+### Usando o Dispositivo de Saída
+
+A funcionalidade do dispositivo `isa-debug-exit` é muito simples. Quando um `value` é escrito na porta I/O especificada por `iobase`, ele faz com que o QEMU saia com [status de saída] `(value << 1) | 1`. Então, quando escrevemos `0` na porta, o QEMU sairá com status de saída `(0 << 1) | 1 = 1`, e quando escrevemos `1` na porta, ele sairá com status de saída `(1 << 1) | 1 = 3`.
+
+[status de saída]: https://en.wikipedia.org/wiki/Exit_status
+
+Em vez de invocar manualmente as instruções assembly `in` e `out`, usamos as abstrações fornecidas pela crate [`x86_64`]. Para adicionar uma dependência nessa crate, a adicionamos à seção `dependencies` no nosso `Cargo.toml`:
+
+[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+x86_64 = "0.14.2"
+```
+
+Agora podemos usar o tipo [`Port`] fornecido pela crate para criar uma função `exit_qemu`:
+
+[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
+
+```rust
+// em src/main.rs
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum QemuExitCode {
+ Success = 0x10,
+ Failed = 0x11,
+}
+
+pub fn exit_qemu(exit_code: QemuExitCode) {
+ use x86_64::instructions::port::Port;
+
+ unsafe {
+ let mut port = Port::new(0xf4);
+ port.write(exit_code as u32);
+ }
+}
+```
+
+A função cria um novo [`Port`] em `0xf4`, que é o `iobase` do dispositivo `isa-debug-exit`. Então ela escreve o código de saída passado para a porta. Usamos `u32` porque especificamos o `iosize` do dispositivo `isa-debug-exit` como 4 bytes. Ambas as operações são unsafe porque escrever em uma porta I/O geralmente pode resultar em comportamento arbitrário.
+
+Para especificar o status de saída, criamos um enum `QemuExitCode`. A ideia é sair com o código de saída de sucesso se todos os testes foram bem-sucedidos e com o código de saída de falha caso contrário. O enum é marcado como `#[repr(u32)]` para representar cada variante por um inteiro `u32`. Usamos o código de saída `0x10` para sucesso e `0x11` para falha. Os códigos de saída reais não importam muito, desde que não colidam com os códigos de saída padrão do QEMU. Por exemplo, usar código de saída `0` para sucesso não é uma boa ideia porque ele se torna `(0 << 1) | 1 = 1` após a transformação, que é o código de saída padrão quando o QEMU falha ao executar. Então não poderíamos diferenciar um erro do QEMU de uma execução de teste bem-sucedida.
+
+Agora podemos atualizar nosso `test_runner` para sair do QEMU após todos os testes terem sido executados:
+
+```rust
+// em src/main.rs
+
+fn test_runner(tests: &[&dyn Fn()]) {
+ println!("Running {} tests", tests.len());
+ for test in tests {
+ test();
+ }
+ /// novo
+ exit_qemu(QemuExitCode::Success);
+}
+```
+
+Quando executamos `cargo test` agora, vemos que o QEMU fecha imediatamente após executar os testes. O problema é que `cargo test` interpreta o teste como falhado mesmo que tenhamos passado nosso código de saída `Success`:
+
+```
+> cargo test
+ Finished dev [unoptimized + debuginfo] target(s) in 0.03s
+ Running target/x86_64-blog_os/debug/deps/blog_os-5804fc7d2dd4c9be
+Building bootloader
+ Compiling bootloader v0.5.3 (/home/philipp/Documents/bootloader)
+ Finished release [optimized + debuginfo] target(s) in 1.07s
+Running: `qemu-system-x86_64 -drive format=raw,file=/…/target/x86_64-blog_os/debug/
+ deps/bootimage-blog_os-5804fc7d2dd4c9be.bin -device isa-debug-exit,iobase=0xf4,
+ iosize=0x04`
+error: test failed, to rerun pass '--bin blog_os'
+```
+
+O problema é que `cargo test` considera todos os códigos de erro diferentes de `0` como falha.
+
+### Código de Saída de Sucesso
+
+Para contornar isso, `bootimage` fornece uma chave de configuração `test-success-exit-code` que mapeia um código de saída especificado para o código de saída `0`:
+
+```toml
+# em Cargo.toml
+
+[package.metadata.bootimage]
+test-args = […]
+test-success-exit-code = 33 # (0x10 << 1) | 1
+```
+
+Com esta configuração, `bootimage` mapeia nosso código de saída de sucesso para o código de saída 0, para que `cargo test` reconheça corretamente o caso de sucesso e não conte o teste como falhado.
+
+Nosso test runner agora fecha automaticamente o QEMU e reporta corretamente os resultados do teste. Ainda vemos a janela do QEMU abrir por um tempo muito curto, mas não é suficiente para ler os resultados. Seria bom se pudéssemos imprimir os resultados do teste no console em vez disso, para que ainda possamos vê-los após o QEMU sair.
+
+## Imprimindo no Console
+
+Para ver a saída do teste no console, precisamos enviar os dados do nosso kernel para o sistema host de alguma forma. Existem várias maneiras de conseguir isso, por exemplo, enviando os dados por uma interface de rede TCP. No entanto, configurar uma pilha de rede é uma tarefa bastante complexa, então escolheremos uma solução mais simples em vez disso.
+
+### Porta Serial
+
+Uma maneira simples de enviar os dados é usar a [porta serial], um antigo padrão de interface que não é mais encontrado em computadores modernos. É fácil de programar e o QEMU pode redirecionar os bytes enviados pela porta serial para a saída padrão do host ou um arquivo.
+
+[porta serial]: https://en.wikipedia.org/wiki/Serial_port
+
+Os chips que implementam uma interface serial são chamados [UARTs]. Existem [muitos modelos de UART] no x86, mas felizmente as únicas diferenças entre eles são alguns recursos avançados que não precisamos. Os UARTs comuns hoje são todos compatíveis com o [UART 16550], então usaremos esse modelo para nosso framework de testes.
+
+[UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter
+[muitos modelos de UART]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models
+[UART 16550]: https://en.wikipedia.org/wiki/16550_UART
+
+Usaremos a crate [`uart_16550`] para inicializar o UART e enviar dados pela porta serial. Para adicioná-la como dependência, atualizamos nosso `Cargo.toml` e `main.rs`:
+
+[`uart_16550`]: https://docs.rs/uart_16550
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+uart_16550 = "0.2.0"
+```
+
+A crate `uart_16550` contém uma struct `SerialPort` que representa os registradores UART, mas ainda precisamos construir uma instância dela nós mesmos. Para isso, criamos um novo módulo `serial` com o seguinte conteúdo:
+
+```rust
+// em src/main.rs
+
+mod serial;
+```
+
+```rust
+// em src/serial.rs
+
+use uart_16550::SerialPort;
+use spin::Mutex;
+use lazy_static::lazy_static;
+
+lazy_static! {
+ pub static ref SERIAL1: Mutex = {
+ let mut serial_port = unsafe { SerialPort::new(0x3F8) };
+ serial_port.init();
+ Mutex::new(serial_port)
+ };
+}
+```
+
+Como com o [buffer de texto VGA][vga lazy-static], usamos `lazy_static` e um spinlock para criar uma instância writer `static`. Ao usar `lazy_static` podemos garantir que o método `init` seja chamado exatamente uma vez em seu primeiro uso.
+
+Como o dispositivo `isa-debug-exit`, o UART é programado usando I/O de porta. Como o UART é mais complexo, ele usa múltiplas portas I/O para programar diferentes registradores do dispositivo. A função unsafe `SerialPort::new` espera o endereço da primeira porta I/O do UART como argumento, a partir do qual ela pode calcular os endereços de todas as portas necessárias. Estamos passando o endereço de porta `0x3F8`, que é o número de porta padrão para a primeira interface serial.
+
+[vga lazy-static]: @/edition-2/posts/03-vga-text-buffer/index.pt-BR.md#lazy-statics
+
+Para tornar a porta serial facilmente utilizável, adicionamos macros `serial_print!` e `serial_println!`:
+
+```rust
+// em src/serial.rs
+
+#[doc(hidden)]
+pub fn _print(args: ::core::fmt::Arguments) {
+ use core::fmt::Write;
+ SERIAL1.lock().write_fmt(args).expect("Printing to serial failed");
+}
+
+/// Imprime no host através da interface serial.
+#[macro_export]
+macro_rules! serial_print {
+ ($($arg:tt)*) => {
+ $crate::serial::_print(format_args!($($arg)*));
+ };
+}
+
+/// Imprime no host através da interface serial, anexando uma newline.
+#[macro_export]
+macro_rules! serial_println {
+ () => ($crate::serial_print!("\n"));
+ ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
+ ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
+ concat!($fmt, "\n"), $($arg)*));
+}
+```
+
+A implementação é muito similar à implementação das nossas macros `print` e `println`. Como o tipo `SerialPort` já implementa a trait [`fmt::Write`], não precisamos fornecer nossa própria implementação.
+
+[`fmt::Write`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html
+
+Agora podemos imprimir na interface serial em vez do buffer de texto VGA no nosso código de teste:
+
+```rust
+// em src/main.rs
+
+#[cfg(test)]
+fn test_runner(tests: &[&dyn Fn()]) {
+ serial_println!("Running {} tests", tests.len());
+ […]
+}
+
+#[test_case]
+fn trivial_assertion() {
+ serial_print!("trivial assertion... ");
+ assert_eq!(1, 1);
+ serial_println!("[ok]");
+}
+```
+
+Note que a macro `serial_println` vive diretamente sob o namespace raiz porque usamos o atributo `#[macro_export]`, então importá-la através de `use crate::serial::serial_println` não funcionará.
+
+### Argumentos do QEMU
+
+Para ver a saída serial do QEMU, precisamos usar o argumento `-serial` para redirecionar a saída para stdout:
+
+```toml
+# em Cargo.toml
+
+[package.metadata.bootimage]
+test-args = [
+ "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio"
+]
+```
+
+Quando executamos `cargo test` agora, vemos a saída do teste diretamente no console:
+
+```
+> cargo test
+ Finished dev [unoptimized + debuginfo] target(s) in 0.02s
+ Running target/x86_64-blog_os/debug/deps/blog_os-7b7c37b4ad62551a
+Building bootloader
+ Finished release [optimized + debuginfo] target(s) in 0.02s
+Running: `qemu-system-x86_64 -drive format=raw,file=/…/target/x86_64-blog_os/debug/
+ deps/bootimage-blog_os-7b7c37b4ad62551a.bin -device
+ isa-debug-exit,iobase=0xf4,iosize=0x04 -serial stdio`
+Running 1 tests
+trivial assertion... [ok]
+```
+
+No entanto, quando um teste falha, ainda vemos a saída dentro do QEMU porque nosso handler de panic ainda usa `println`. Para simular isso, podemos mudar a assertion no nosso teste `trivial_assertion` para `assert_eq!(0, 1)`:
+
+
+
+Vemos que a mensagem de panic ainda é impressa no buffer VGA, enquanto a outra saída de teste é impressa na porta serial. A mensagem de panic é bastante útil, então seria útil vê-la no console também.
+
+### Imprimir uma Mensagem de Erro no Panic
+
+Para sair do QEMU com uma mensagem de erro em um panic, podemos usar [compilação condicional] para usar um handler de panic diferente no modo de teste:
+
+[compilação condicional]: https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html
+
+```rust
+// em src/main.rs
+
+// nosso handler de panic existente
+#[cfg(not(test))] // novo atributo
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ println!("{}", info);
+ loop {}
+}
+
+// nosso handler de panic em modo de teste
+#[cfg(test)]
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ serial_println!("[failed]\n");
+ serial_println!("Error: {}\n", info);
+ exit_qemu(QemuExitCode::Failed);
+ loop {}
+}
+```
+
+Para nosso handler de panic de teste, usamos `serial_println` em vez de `println` e então saímos do QEMU com um código de saída de falha. Note que ainda precisamos de um `loop` infinito após a chamada `exit_qemu` porque o compilador não sabe que o dispositivo `isa-debug-exit` causa uma saída do programa.
+
+Agora o QEMU também sai para testes falhados e imprime uma mensagem de erro útil no console:
+
+```
+> cargo test
+ Finished dev [unoptimized + debuginfo] target(s) in 0.02s
+ Running target/x86_64-blog_os/debug/deps/blog_os-7b7c37b4ad62551a
+Building bootloader
+ Finished release [optimized + debuginfo] target(s) in 0.02s
+Running: `qemu-system-x86_64 -drive format=raw,file=/…/target/x86_64-blog_os/debug/
+ deps/bootimage-blog_os-7b7c37b4ad62551a.bin -device
+ isa-debug-exit,iobase=0xf4,iosize=0x04 -serial stdio`
+Running 1 tests
+trivial assertion... [failed]
+
+Error: panicked at 'assertion failed: `(left == right)`
+ left: `0`,
+ right: `1`', src/main.rs:65:5
+```
+
+Como agora vemos toda a saída do teste no console, não precisamos mais da janela do QEMU que aparece por um curto tempo. Então podemos ocultá-la completamente.
+
+### Ocultando o QEMU
+
+Como reportamos os resultados completos do teste usando o dispositivo `isa-debug-exit` e a porta serial, não precisamos mais da janela do QEMU. Podemos ocultá-la passando o argumento `-display none` ao QEMU:
+
+```toml
+# em Cargo.toml
+
+[package.metadata.bootimage]
+test-args = [
+ "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",
+ "-display", "none"
+]
+```
+
+Agora o QEMU executa completamente em segundo plano e nenhuma janela é mais aberta. Isso não é apenas menos irritante, mas também permite que nosso framework de testes execute em ambientes sem interface gráfica do usuário, como serviços de CI ou conexões [SSH].
+
+[SSH]: https://en.wikipedia.org/wiki/Secure_Shell
+
+### Timeouts
+
+Como `cargo test` espera até que o test runner saia, um teste que nunca retorna pode bloquear o test runner para sempre. Isso é infeliz, mas não é um grande problema na prática, pois geralmente é fácil evitar loops infinitos. No nosso caso, no entanto, loops infinitos podem ocorrer em várias situações:
+
+- O bootloader falha ao carregar nosso kernel, o que causa o sistema reiniciar infinitamente.
+- O firmware BIOS/UEFI falha ao carregar o bootloader, o que causa a mesma reinicialização infinita.
+- A CPU entra em uma declaração `loop {}` no final de algumas das nossas funções, por exemplo porque o dispositivo de saída do QEMU não funciona corretamente.
+- O hardware causa um reset do sistema, por exemplo quando uma exceção de CPU não é capturada (explicado em um post futuro).
+
+Como loops infinitos podem ocorrer em tantas situações, a ferramenta `bootimage` define um timeout de 5 minutos para cada executável de teste por padrão. Se o teste não terminar dentro deste tempo, ele é marcado como falhado e um erro "Timed Out" é impresso no console. Este recurso garante que testes que estão presos em um loop infinito não bloqueiem `cargo test` para sempre.
+
+Você pode tentar você mesmo adicionando uma declaração `loop {}` no teste `trivial_assertion`. Quando você executa `cargo test`, vê que o teste é marcado como timed out após 5 minutos. A duração do timeout é [configurável][bootimage config] através de uma chave `test-timeout` no Cargo.toml:
+
+[bootimage config]: https://github.com/rust-osdev/bootimage#configuration
+
+```toml
+# em Cargo.toml
+
+[package.metadata.bootimage]
+test-timeout = 300 # (em segundos)
+```
+
+Se você não quiser esperar 5 minutos para o teste `trivial_assertion` dar timeout, pode diminuir temporariamente o valor acima.
+
+### Inserir Impressão Automaticamente
+
+Nosso teste `trivial_assertion` atualmente precisa imprimir suas próprias informações de status usando `serial_print!`/`serial_println!`:
+
+```rust
+#[test_case]
+fn trivial_assertion() {
+ serial_print!("trivial assertion... ");
+ assert_eq!(1, 1);
+ serial_println!("[ok]");
+}
+```
+
+Adicionar manualmente essas declarações de impressão para cada teste que escrevemos é trabalhoso, então vamos atualizar nosso `test_runner` para imprimir essas mensagens automaticamente. Para fazer isso, precisamos criar uma nova trait `Testable`:
+
+```rust
+// em src/main.rs
+
+pub trait Testable {
+ fn run(&self) -> ();
+}
+```
+
+O truque agora é implementar esta trait para todos os tipos `T` que implementam a [trait `Fn()`]:
+
+[trait `Fn()`]: https://doc.rust-lang.org/stable/core/ops/trait.Fn.html
+
+```rust
+// em src/main.rs
+
+impl Testable for T
+where
+ T: Fn(),
+{
+ fn run(&self) {
+ serial_print!("{}...\t", core::any::type_name::());
+ self();
+ serial_println!("[ok]");
+ }
+}
+```
+
+Implementamos a função `run` primeiro imprimindo o nome da função usando a função [`any::type_name`]. Esta função é implementada diretamente no compilador e retorna uma descrição em string de cada tipo. Para funções, o tipo é seu nome, então isso é exatamente o que queremos neste caso. O caractere `\t` é o [caractere tab], que adiciona algum alinhamento às mensagens `[ok]`.
+
+[`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html
+[caractere tab]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters
+
+Após imprimir o nome da função, invocamos a função de teste através de `self()`. Isso só funciona porque exigimos que `self` implemente a trait `Fn()`. Após a função de teste retornar, imprimimos `[ok]` para indicar que a função não entrou em panic.
+
+O último passo é atualizar nosso `test_runner` para usar a nova trait `Testable`:
+
+```rust
+// em src/main.rs
+
+#[cfg(test)]
+pub fn test_runner(tests: &[&dyn Testable]) { // novo
+ serial_println!("Running {} tests", tests.len());
+ for test in tests {
+ test.run(); // novo
+ }
+ exit_qemu(QemuExitCode::Success);
+}
+```
+
+As únicas duas mudanças são o tipo do argumento `tests` de `&[&dyn Fn()]` para `&[&dyn Testable]` e o fato de que agora chamamos `test.run()` em vez de `test()`.
+
+Agora podemos remover as declarações de impressão do nosso teste `trivial_assertion` já que elas são impressas automaticamente:
+
+```rust
+// em src/main.rs
+
+#[test_case]
+fn trivial_assertion() {
+ assert_eq!(1, 1);
+}
+```
+
+A saída de `cargo test` agora se parece com isto:
+
+```
+Running 1 tests
+blog_os::trivial_assertion... [ok]
+```
+
+O nome da função agora inclui o caminho completo para a função, o que é útil quando funções de teste em diferentes módulos têm o mesmo nome. Caso contrário, a saída parece igual a antes, mas não precisamos mais adicionar declarações de impressão aos nossos testes manualmente.
+
+## Testando o Buffer VGA
+
+Agora que temos um framework de testes funcionando, podemos criar alguns testes para nossa implementação de buffer VGA. Primeiro, criamos um teste muito simples para verificar que `println` funciona sem entrar em panic:
+
+```rust
+// em src/vga_buffer.rs
+
+#[test_case]
+fn test_println_simple() {
+ println!("test_println_simple output");
+}
+```
+
+O teste apenas imprime algo no buffer VGA. Se ele terminar sem entrar em panic, significa que a invocação de `println` também não entrou em panic.
+
+Para garantir que nenhum panic ocorra mesmo se muitas linhas forem impressas e as linhas forem deslocadas para fora da tela, podemos criar outro teste:
+
+```rust
+// em src/vga_buffer.rs
+
+#[test_case]
+fn test_println_many() {
+ for _ in 0..200 {
+ println!("test_println_many output");
+ }
+}
+```
+
+Também podemos criar uma função de teste para verificar que as linhas impressas realmente aparecem na tela:
+
+```rust
+// em src/vga_buffer.rs
+
+#[test_case]
+fn test_println_output() {
+ let s = "Some test string that fits on a single line";
+ println!("{}", s);
+ for (i, c) in s.chars().enumerate() {
+ let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
+ assert_eq!(char::from(screen_char.ascii_character), c);
+ }
+}
+```
+
+A função define uma string de teste, a imprime usando `println`, e então itera sobre os caracteres da tela do `WRITER` static, que representa o buffer de texto VGA. Como `println` imprime na última linha da tela e então anexa imediatamente uma newline, a string deve aparecer na linha `BUFFER_HEIGHT - 2`.
+
+Ao usar [`enumerate`], contamos o número de iterações na variável `i`, que então usamos para carregar o caractere da tela correspondente a `c`. Ao comparar o `ascii_character` do caractere da tela com `c`, garantimos que cada caractere da string realmente aparece no buffer de texto VGA.
+
+[`enumerate`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate
+
+Como você pode imaginar, poderíamos criar muitas mais funções de teste. Por exemplo, uma função que testa que nenhum panic ocorre ao imprimir linhas muito longas e que elas são quebradas corretamente, ou uma função para testar que newlines, caracteres não imprimíveis e caracteres não-unicode são tratados corretamente.
+
+Para o resto deste post, no entanto, explicaremos como criar _testes de integração_ para testar a interação de diferentes componentes juntos.
+
+## Testes de Integração
+
+A convenção para [testes de integração] em Rust é colocá-los em um diretório `tests` na raiz do projeto (ou seja, ao lado do diretório `src`). Tanto o framework de testes padrão quanto frameworks de testes customizados detectarão e executarão automaticamente todos os testes naquele diretório.
+
+[testes de integração]: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests
+
+Todos os testes de integração são seus próprios executáveis e completamente separados do nosso `main.rs`. Isso significa que cada teste precisa definir sua própria função de ponto de entrada. Vamos criar um teste de integração de exemplo chamado `basic_boot` para ver como funciona em detalhes:
+
+```rust
+// em tests/basic_boot.rs
+
+#![no_std]
+#![no_main]
+#![feature(custom_test_frameworks)]
+#![test_runner(crate::test_runner)]
+#![reexport_test_harness_main = "test_main"]
+
+use core::panic::PanicInfo;
+
+#[unsafe(no_mangle)] // não altere (mangle) o nome desta função
+pub extern "C" fn _start() -> ! {
+ test_main();
+
+ loop {}
+}
+
+fn test_runner(tests: &[&dyn Fn()]) {
+ unimplemented!();
+}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+Como testes de integração são executáveis separados, precisamos fornecer todos os atributos da crate (`no_std`, `no_main`, `test_runner`, etc.) novamente. Também precisamos criar uma nova função de ponto de entrada `_start`, que chama a função de ponto de entrada de teste `test_main`. Não precisamos de nenhum atributo `cfg(test)` porque executáveis de teste de integração nunca são construídos em modo não-teste.
+
+Usamos a macro [`unimplemented`] que sempre entra em panic como placeholder para a função `test_runner` e apenas fazemos `loop` no handler de `panic` por enquanto. Idealmente, queremos implementar essas funções exatamente como fizemos no nosso `main.rs` usando a macro `serial_println` e a função `exit_qemu`. O problema é que não temos acesso a essas funções porque os testes são construídos completamente separados do nosso executável `main.rs`.
+
+[`unimplemented`]: https://doc.rust-lang.org/core/macro.unimplemented.html
+
+Se você executar `cargo test` neste estágio, entrará em um loop infinito porque o handler de panic faz loop infinitamente. Você precisa usar o atalho de teclado `ctrl+c` para sair do QEMU.
+
+### Criar uma Biblioteca
+
+Para tornar as funções necessárias disponíveis para nosso teste de integração, precisamos separar uma biblioteca do nosso `main.rs`, que pode ser incluída por outras crates e executáveis de teste de integração. Para fazer isso, criamos um novo arquivo `src/lib.rs`:
+
+```rust
+// src/lib.rs
+
+#![no_std]
+
+```
+
+Como o `main.rs`, o `lib.rs` é um arquivo especial que é automaticamente reconhecido pelo cargo. A biblioteca é uma unidade de compilação separada, então precisamos especificar o atributo `#![no_std]` novamente.
+
+Para fazer nossa biblioteca funcionar com `cargo test`, precisamos também mover as funções de teste e atributos de `main.rs` para `lib.rs`:
+
+```rust
+// em src/lib.rs
+
+#![cfg_attr(test, no_main)]
+#![feature(custom_test_frameworks)]
+#![test_runner(crate::test_runner)]
+#![reexport_test_harness_main = "test_main"]
+
+use core::panic::PanicInfo;
+
+pub trait Testable {
+ fn run(&self) -> ();
+}
+
+impl Testable for T
+where
+ T: Fn(),
+{
+ fn run(&self) {
+ serial_print!("{}...\t", core::any::type_name::());
+ self();
+ serial_println!("[ok]");
+ }
+}
+
+pub fn test_runner(tests: &[&dyn Testable]) {
+ serial_println!("Running {} tests", tests.len());
+ for test in tests {
+ test.run();
+ }
+ exit_qemu(QemuExitCode::Success);
+}
+
+pub fn test_panic_handler(info: &PanicInfo) -> ! {
+ serial_println!("[failed]\n");
+ serial_println!("Error: {}\n", info);
+ exit_qemu(QemuExitCode::Failed);
+ loop {}
+}
+
+/// Ponto de entrada para `cargo test`
+#[cfg(test)]
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ test_main();
+ loop {}
+}
+
+#[cfg(test)]
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ test_panic_handler(info)
+}
+```
+
+Para tornar nosso `test_runner` disponível para executáveis e testes de integração, o tornamos público e não aplicamos o atributo `cfg(test)` a ele. Também fatoramos a implementação do nosso handler de panic em uma função pública `test_panic_handler`, para que ela esteja disponível para executáveis também.
+
+Como nosso `lib.rs` é testado independentemente do nosso `main.rs`, precisamos adicionar um ponto de entrada `_start` e um handler de panic quando a biblioteca é compilada em modo de teste. Ao usar o atributo de crate [`cfg_attr`], habilitamos condicionalmente o atributo `no_main` neste caso.
+
+[`cfg_attr`]: https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute
+
+Também movemos o enum `QemuExitCode` e a função `exit_qemu` e os tornamos públicos:
+
+```rust
+// em src/lib.rs
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum QemuExitCode {
+ Success = 0x10,
+ Failed = 0x11,
+}
+
+pub fn exit_qemu(exit_code: QemuExitCode) {
+ use x86_64::instructions::port::Port;
+
+ unsafe {
+ let mut port = Port::new(0xf4);
+ port.write(exit_code as u32);
+ }
+}
+```
+
+Agora executáveis e testes de integração podem importar essas funções da biblioteca e não precisam definir suas próprias implementações. Para também tornar `println` e `serial_println` disponíveis, movemos as declarações de módulo também:
+
+```rust
+// em src/lib.rs
+
+pub mod serial;
+pub mod vga_buffer;
+```
+
+Tornamos os módulos públicos para torná-los utilizáveis fora da nossa biblioteca. Isso também é necessário para tornar nossas macros `println` e `serial_println` utilizáveis, já que elas usam as funções `_print` dos módulos.
+
+Agora podemos atualizar nosso `main.rs` para usar a biblioteca:
+
+```rust
+// em src/main.rs
+
+#![no_std]
+#![no_main]
+#![feature(custom_test_frameworks)]
+#![test_runner(blog_os::test_runner)]
+#![reexport_test_harness_main = "test_main"]
+
+use core::panic::PanicInfo;
+use blog_os::println;
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Hello World{}", "!");
+
+ #[cfg(test)]
+ test_main();
+
+ loop {}
+}
+
+/// Esta função é chamada em caso de pânico.
+#[cfg(not(test))]
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ println!("{}", info);
+ loop {}
+}
+
+#[cfg(test)]
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ blog_os::test_panic_handler(info)
+}
+```
+
+A biblioteca é utilizável como uma crate externa normal. É chamada `blog_os`, como nossa crate. O código acima usa a função `blog_os::test_runner` no atributo `test_runner` e a função `blog_os::test_panic_handler` no nosso handler de `panic` `cfg(test)`. Também importa a macro `println` para torná-la disponível para nossas funções `_start` e `panic`.
+
+Neste ponto, `cargo run` e `cargo test` devem funcionar novamente. É claro que `cargo test` ainda faz loop infinitamente (você pode sair com `ctrl+c`). Vamos corrigir isso usando as funções necessárias da biblioteca no nosso teste de integração.
+
+### Completando o Teste de Integração
+
+Como nosso `src/main.rs`, nosso executável `tests/basic_boot.rs` pode importar tipos da nossa nova biblioteca. Isso nos permite importar os componentes faltantes para completar nosso teste:
+
+```rust
+// em tests/basic_boot.rs
+
+#![test_runner(blog_os::test_runner)]
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ blog_os::test_panic_handler(info)
+}
+```
+
+Em vez de reimplementar o test runner, usamos a função `test_runner` da nossa biblioteca mudando o atributo `#![test_runner(crate::test_runner)]` para `#![test_runner(blog_os::test_runner)]`. Então não precisamos mais da função stub `test_runner` em `basic_boot.rs`, então podemos removê-la. Para nosso handler de `panic`, chamamos a função `blog_os::test_panic_handler` como fizemos no nosso `main.rs`.
+
+Agora `cargo test` sai normalmente novamente. Quando você o executa, verá que ele constrói e executa os testes para nosso `lib.rs`, `main.rs` e `basic_boot.rs` separadamente um após o outro. Para o `main.rs` e os testes de integração `basic_boot`, ele reporta "Running 0 tests" já que esses arquivos não têm nenhuma função anotada com `#[test_case]`.
+
+Agora podemos adicionar testes ao nosso `basic_boot.rs`. Por exemplo, podemos testar que `println` funciona sem entrar em panic, como fizemos nos testes do buffer VGA:
+
+```rust
+// em tests/basic_boot.rs
+
+use blog_os::println;
+
+#[test_case]
+fn test_println() {
+ println!("test_println output");
+}
+```
+
+Quando executamos `cargo test` agora, vemos que ele encontra e executa a função de teste.
+
+O teste pode parecer um pouco inútil agora já que é quase idêntico a um dos testes do buffer VGA. No entanto, no futuro, as funções `_start` do nosso `main.rs` e `lib.rs` podem crescer e chamar várias rotinas de inicialização antes de executar a função `test_main`, então os dois testes são executados em ambientes muito diferentes.
+
+Ao testar `println` em um ambiente `basic_boot` sem chamar nenhuma rotina de inicialização em `_start`, podemos garantir que `println` funciona logo após o boot. Isso é importante porque dependemos dele, por exemplo, para imprimir mensagens de panic.
+
+### Testes Futuros
+
+O poder dos testes de integração é que eles são tratados como executáveis completamente separados. Isso lhes dá controle completo sobre o ambiente, o que torna possível testar que o código interage corretamente com a CPU ou dispositivos de hardware.
+
+Nosso teste `basic_boot` é um exemplo muito simples de um teste de integração. No futuro, nosso kernel se tornará muito mais cheio de recursos e interagirá com o hardware de várias maneiras. Ao adicionar testes de integração, podemos garantir que essas interações funcionem (e continuem funcionando) como esperado. Algumas ideias para possíveis testes futuros são:
+
+- **Exceções de CPU**: Quando o código executa operações inválidas (por exemplo, divide por zero), a CPU lança uma exceção. O kernel pode registrar funções handler para tais exceções. Um teste de integração poderia verificar que o handler de exceção correto é chamado quando uma exceção de CPU ocorre ou que a execução continua corretamente após uma exceção resolvível.
+- **Tabelas de Página**: Tabelas de página definem quais regiões de memória são válidas e acessíveis. Ao modificar as tabelas de página, é possível alocar novas regiões de memória, por exemplo ao lançar programas. Um teste de integração poderia modificar as tabelas de página na função `_start` e verificar que as modificações têm os efeitos desejados nas funções `#[test_case]`.
+- **Programas Userspace**: Programas userspace são programas com acesso limitado aos recursos do sistema. Por exemplo, eles não têm acesso a estruturas de dados do kernel ou à memória de outros programas. Um teste de integração poderia lançar programas userspace que executam operações proibidas e verificar que o kernel as impede todas.
+
+Como você pode imaginar, muitos mais testes são possíveis. Ao adicionar tais testes, podemos garantir que não os quebramos acidentalmente quando adicionamos novos recursos ao nosso kernel ou refatoramos nosso código. Isso é especialmente importante quando nosso kernel se torna maior e mais complexo.
+
+### Testes que Devem Entrar em Panic
+
+O framework de testes da biblioteca padrão suporta um [atributo `#[should_panic]`][should_panic] que permite construir testes que devem falhar. Isso é útil, por exemplo, para verificar que uma função falha quando um argumento inválido é passado. Infelizmente, este atributo não é suportado em crates `#[no_std]` porque requer suporte da biblioteca padrão.
+
+[should_panic]: https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html#testing-panics
+
+Embora não possamos usar o atributo `#[should_panic]` no nosso kernel, podemos obter comportamento similar criando um teste de integração que sai com um código de erro de sucesso do handler de panic. Vamos começar a criar tal teste com o nome `should_panic`:
+
+```rust
+// em tests/should_panic.rs
+
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+use blog_os::{QemuExitCode, exit_qemu, serial_println};
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ serial_println!("[ok]");
+ exit_qemu(QemuExitCode::Success);
+ loop {}
+}
+```
+
+Este teste ainda está incompleto, pois não define uma função `_start` ou nenhum dos atributos customizados de test runner ainda. Vamos adicionar as partes faltantes:
+
+```rust
+// em tests/should_panic.rs
+
+#![feature(custom_test_frameworks)]
+#![test_runner(test_runner)]
+#![reexport_test_harness_main = "test_main"]
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ test_main();
+
+ loop {}
+}
+
+pub fn test_runner(tests: &[&dyn Fn()]) {
+ serial_println!("Running {} tests", tests.len());
+ for test in tests {
+ test();
+ serial_println!("[test did not panic]");
+ exit_qemu(QemuExitCode::Failed);
+ }
+ exit_qemu(QemuExitCode::Success);
+}
+```
+
+Em vez de reutilizar o `test_runner` do nosso `lib.rs`, o teste define sua própria função `test_runner` que sai com um código de saída de falha quando um teste retorna sem entrar em panic (queremos que nossos testes entrem em panic). Se nenhuma função de teste for definida, o runner sai com um código de erro de sucesso. Como o runner sempre sai após executar um único teste, não faz sentido definir mais de uma função `#[test_case]`.
+
+Agora podemos criar um teste que deveria falhar:
+
+```rust
+// em tests/should_panic.rs
+
+use blog_os::serial_print;
+
+#[test_case]
+fn should_fail() {
+ serial_print!("should_panic::should_fail...\t");
+ assert_eq!(0, 1);
+}
+```
+
+O teste usa `assert_eq` para afirmar que `0` e `1` são iguais. É claro que isso falha, então nosso teste entra em panic como desejado. Note que precisamos imprimir manualmente o nome da função usando `serial_print!` aqui porque não usamos a trait `Testable`.
+
+Quando executamos o teste através de `cargo test --test should_panic` vemos que ele é bem-sucedido porque o teste entrou em panic como esperado. Quando comentamos a assertion e executamos o teste novamente, vemos que ele de fato falha com a mensagem _"test did not panic"_.
+
+Uma desvantagem significativa desta abordagem é que ela só funciona para uma única função de teste. Com múltiplas funções `#[test_case]`, apenas a primeira função é executada porque a execução não pode continuar após o handler de panic ter sido chamado. Atualmente não conheço uma boa maneira de resolver este problema, então me avise se você tiver uma ideia!
+
+### Testes Sem Harness
+
+Para testes de integração que têm apenas uma única função de teste (como nosso teste `should_panic`), o test runner realmente não é necessário. Para casos como este, podemos desabilitar o test runner completamente e executar nosso teste diretamente na função `_start`.
+
+A chave para isso é desabilitar a flag `harness` para o teste no `Cargo.toml`, que define se um test runner é usado para um teste de integração. Quando está definido como `false`, tanto o test runner padrão quanto o recurso de test runner customizado são desabilitados, de modo que o teste é tratado como um executável normal.
+
+Vamos desabilitar a flag `harness` para nosso teste `should_panic`:
+
+```toml
+# em Cargo.toml
+
+[[test]]
+name = "should_panic"
+harness = false
+```
+
+Agora simplificamos vastamente nosso teste `should_panic` removendo o código relacionado ao `test_runner`. O resultado se parece com isto:
+
+```rust
+// em tests/should_panic.rs
+
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+use blog_os::{exit_qemu, serial_print, serial_println, QemuExitCode};
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ should_fail();
+ serial_println!("[test did not panic]");
+ exit_qemu(QemuExitCode::Failed);
+ loop{}
+}
+
+fn should_fail() {
+ serial_print!("should_panic::should_fail...\t");
+ assert_eq!(0, 1);
+}
+
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ serial_println!("[ok]");
+ exit_qemu(QemuExitCode::Success);
+ loop {}
+}
+```
+
+Agora chamamos a função `should_fail` diretamente da nossa função `_start` e saímos com um código de saída de falha se ela retornar. Quando executamos `cargo test --test should_panic` agora, vemos que o teste se comporta exatamente como antes.
+
+Além de criar testes `should_panic`, desabilitar o atributo `harness` também pode ser útil para testes de integração complexos, por exemplo, quando as funções de teste individuais têm efeitos colaterais e precisam ser executadas em uma ordem especificada.
+
+## Resumo
+
+Testes são uma técnica muito útil para garantir que certos componentes tenham o comportamento desejado. Mesmo que não possam mostrar a ausência de bugs, ainda são uma ferramenta útil para encontrá-los e especialmente para evitar regressões.
+
+Este post explicou como configurar um framework de testes para nosso kernel Rust. Usamos o recurso de frameworks de teste customizados do Rust para implementar suporte para um atributo `#[test_case]` simples no nosso ambiente bare metal. Usando o dispositivo `isa-debug-exit` do QEMU, nosso test runner pode sair do QEMU após executar os testes e reportar o status do teste. Para imprimir mensagens de erro no console em vez do buffer VGA, criamos um driver básico para a porta serial.
+
+Após criar alguns testes para nossa macro `println`, exploramos testes de integração na segunda metade do post. Aprendemos que eles vivem no diretório `tests` e são tratados como executáveis completamente separados. Para dar a eles acesso à função `exit_qemu` e à macro `serial_println`, movemos a maior parte do nosso código para uma biblioteca que pode ser importada por todos os executáveis e testes de integração. Como testes de integração são executados em seu próprio ambiente separado, eles tornam possível testar interações com o hardware ou criar testes que devem entrar em panic.
+
+Agora temos um framework de testes que executa em um ambiente realista dentro do QEMU. Ao criar mais testes em posts futuros, podemos manter nosso kernel sustentável quando ele se tornar mais complexo.
+
+## O que vem a seguir?
+
+No próximo post, exploraremos _exceções de CPU_. Essas exceções são lançadas pela CPU quando algo ilegal acontece, como uma divisão por zero ou um acesso a uma página de memória não mapeada (um chamado "page fault"). Ser capaz de capturar e examinar essas exceções é muito importante para depuração de erros futuros. O tratamento de exceções também é muito similar ao tratamento de interrupções de hardware, que é necessário para suporte a teclado.
\ No newline at end of file
From c780856a8b05b208dccd13c4b6dea5dd5293a6c2 Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 23:04:28 -0300
Subject: [PATCH 06/26] [Translation][Portuguese pt-BR] post-5 (edition-2)
---
.../posts/05-cpu-exceptions/index.pt-BR.md | 474 ++++++++++++++++++
1 file changed, 474 insertions(+)
create mode 100644 blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md
new file mode 100644
index 00000000..84cf6f2a
--- /dev/null
+++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md
@@ -0,0 +1,474 @@
++++
+title = "Exceções de CPU"
+weight = 5
+path = "pt-BR/cpu-exceptions"
+date = 2018-06-17
+
+[extra]
+chapter = "Interrupções"
+# Please update this when updating the translation
+translation_based_on_commit = "9753695744854686a6b80012c89b0d850a44b4b0"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Exceções de CPU ocorrem em várias situações errôneas, por exemplo, ao acessar um endereço de memória inválido ou ao dividir por zero. Para reagir a elas, precisamos configurar uma _tabela de descritores de interrupção_ que fornece funções manipuladoras. Ao final desta postagem, nosso kernel será capaz de capturar [exceções de breakpoint] e retomar a execução normal posteriormente.
+
+[exceções de breakpoint]: https://wiki.osdev.org/Exceptions#Breakpoint
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-05`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-05
+
+
+
+## Visão Geral
+Uma exceção sinaliza que algo está errado com a instrução atual. Por exemplo, a CPU emite uma exceção se a instrução atual tenta dividir por 0. Quando uma exceção ocorre, a CPU interrompe seu trabalho atual e imediatamente chama uma função manipuladora de exceção específica, dependendo do tipo de exceção.
+
+No x86, existem cerca de 20 tipos diferentes de exceções de CPU. As mais importantes são:
+
+- **Page Fault**: Um page fault ocorre em acessos ilegais à memória. Por exemplo, se a instrução atual tenta ler de uma página não mapeada ou tenta escrever em uma página somente leitura.
+- **Invalid Opcode**: Esta exceção ocorre quando a instrução atual é inválida, por exemplo, quando tentamos usar novas [instruções SSE] em uma CPU antiga que não as suporta.
+- **General Protection Fault**: Esta é a exceção com a gama mais ampla de causas. Ela ocorre em vários tipos de violações de acesso, como tentar executar uma instrução privilegiada em código de nível de usuário ou escrever em campos reservados de registradores de configuração.
+- **Double Fault**: Quando uma exceção ocorre, a CPU tenta chamar a função manipuladora correspondente. Se outra exceção ocorre _enquanto chama o manipulador de exceção_, a CPU levanta uma exceção de double fault. Esta exceção também ocorre quando não há função manipuladora registrada para uma exceção.
+- **Triple Fault**: Se uma exceção ocorre enquanto a CPU tenta chamar a função manipuladora de double fault, ela emite um _triple fault_ fatal. Não podemos capturar ou manipular um triple fault. A maioria dos processadores reage redefinindo-se e reinicializando o sistema operacional.
+
+[instruções SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
+
+Para a lista completa de exceções, consulte a [wiki do OSDev][exceptions].
+
+[exceptions]: https://wiki.osdev.org/Exceptions
+
+### A Tabela de Descritores de Interrupção
+Para capturar e manipular exceções, precisamos configurar uma chamada _Tabela de Descritores de Interrupção_ (IDT - Interrupt Descriptor Table). Nesta tabela, podemos especificar uma função manipuladora para cada exceção de CPU. O hardware usa esta tabela diretamente, então precisamos seguir um formato predefinido. Cada entrada deve ter a seguinte estrutura de 16 bytes:
+
+Tipo| Nome | Descrição
+----|--------------------------|-----------------------------------
+u16 | Function Pointer [0:15] | Os bits inferiores do ponteiro para a função manipuladora.
+u16 | GDT selector | Seletor de um segmento de código na [tabela de descritores globais].
+u16 | Options | (veja abaixo)
+u16 | Function Pointer [16:31] | Os bits do meio do ponteiro para a função manipuladora.
+u32 | Function Pointer [32:63] | Os bits restantes do ponteiro para a função manipuladora.
+u32 | Reserved |
+
+[tabela de descritores globais]: https://en.wikipedia.org/wiki/Global_Descriptor_Table
+
+O campo options tem o seguinte formato:
+
+Bits | Nome | Descrição
+------|-----------------------------------|-----------------------------------
+0-2 | Interrupt Stack Table Index | 0: Não troca stacks, 1-7: Troca para a n-ésima stack na Interrupt Stack Table quando este manipulador é chamado.
+3-7 | Reserved |
+8 | 0: Interrupt Gate, 1: Trap Gate | Se este bit é 0, as interrupções são desativadas quando este manipulador é chamado.
+9-11 | must be one |
+12 | must be zero |
+13‑14 | Descriptor Privilege Level (DPL) | O nível mínimo de privilégio necessário para chamar este manipulador.
+15 | Present |
+
+Cada exceção tem um índice predefinido na IDT. Por exemplo, a exceção invalid opcode tem índice de tabela 6 e a exceção page fault tem índice de tabela 14. Assim, o hardware pode automaticamente carregar a entrada IDT correspondente para cada exceção. A [Tabela de Exceções][exceptions] na wiki do OSDev mostra os índices IDT de todas as exceções na coluna "Vector nr.".
+
+Quando uma exceção ocorre, a CPU aproximadamente faz o seguinte:
+
+1. Empurra alguns registradores na pilha, incluindo o ponteiro de instrução e o registrador [RFLAGS]. (Usaremos esses valores mais tarde nesta postagem.)
+2. Lê a entrada correspondente da Tabela de Descritores de Interrupção (IDT). Por exemplo, a CPU lê a 14ª entrada quando ocorre um page fault.
+3. Verifica se a entrada está presente e, se não estiver, levanta um double fault.
+4. Desativa interrupções de hardware se a entrada é um interrupt gate (bit 40 não está definido).
+5. Carrega o seletor [GDT] especificado no CS (segmento de código).
+6. Pula para a função manipuladora especificada.
+
+[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register
+[GDT]: https://en.wikipedia.org/wiki/Global_Descriptor_Table
+
+Não se preocupe com os passos 4 e 5 por enquanto; aprenderemos sobre a tabela de descritores globais e interrupções de hardware em postagens futuras.
+
+## Um Tipo IDT
+Em vez de criar nosso próprio tipo IDT, usaremos a [struct `InterruptDescriptorTable`] da crate `x86_64`, que se parece com isto:
+
+[struct `InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
+
+``` rust
+#[repr(C)]
+pub struct InterruptDescriptorTable {
+ pub divide_by_zero: Entry,
+ pub debug: Entry,
+ pub non_maskable_interrupt: Entry,
+ pub breakpoint: Entry,
+ pub overflow: Entry,
+ pub bound_range_exceeded: Entry,
+ pub invalid_opcode: Entry,
+ pub device_not_available: Entry,
+ pub double_fault: Entry,
+ pub invalid_tss: Entry,
+ pub segment_not_present: Entry,
+ pub stack_segment_fault: Entry,
+ pub general_protection_fault: Entry,
+ pub page_fault: Entry,
+ pub x87_floating_point: Entry,
+ pub alignment_check: Entry,
+ pub machine_check: Entry,
+ pub simd_floating_point: Entry,
+ pub virtualization: Entry,
+ pub security_exception: Entry,
+ // alguns campos omitidos
+}
+```
+
+Os campos têm o tipo [`idt::Entry`], que é uma struct que representa os campos de uma entrada IDT (veja a tabela acima). O parâmetro de tipo `F` define o tipo de função manipuladora esperado. Vemos que algumas entradas requerem uma [`HandlerFunc`] e algumas entradas requerem uma [`HandlerFuncWithErrCode`]. O page fault tem até seu próprio tipo especial: [`PageFaultHandlerFunc`].
+
+[`idt::Entry`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.Entry.html
+[`HandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFunc.html
+[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
+[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
+
+Vamos olhar primeiro para o tipo `HandlerFunc`:
+
+```rust
+type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame);
+```
+
+É um [type alias] para um tipo `extern "x86-interrupt" fn`. A palavra-chave `extern` define uma função com uma [convenção de chamada estrangeira] e é frequentemente usada para se comunicar com código C (`extern "C" fn`). Mas o que é a convenção de chamada `x86-interrupt`?
+
+[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases
+[convenção de chamada estrangeira]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions
+
+## A Convenção de Chamada de Interrupção
+Exceções são bastante similares a chamadas de função: A CPU pula para a primeira instrução da função chamada e a executa. Depois, a CPU pula para o endereço de retorno e continua a execução da função pai.
+
+No entanto, há uma diferença importante entre exceções e chamadas de função: Uma chamada de função é invocada voluntariamente por uma instrução `call` inserida pelo compilador, enquanto uma exceção pode ocorrer em _qualquer_ instrução. Para entender as consequências desta diferença, precisamos examinar as chamadas de função em mais detalhes.
+
+[Convenções de chamada] especificam os detalhes de uma chamada de função. Por exemplo, elas especificam onde os parâmetros da função são colocados (por exemplo, em registradores ou na pilha) e como os resultados são retornados. No x86_64 Linux, as seguintes regras se aplicam para funções C (especificadas no [System V ABI]):
+
+[Convenções de chamada]: https://en.wikipedia.org/wiki/Calling_convention
+[System V ABI]: https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
+
+- os primeiros seis argumentos inteiros são passados nos registradores `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
+- argumentos adicionais são passados na pilha
+- resultados são retornados em `rax` e `rdx`
+
+Note que Rust não segue a ABI do C (na verdade, [nem existe uma ABI Rust ainda][rust abi]), então essas regras se aplicam apenas a funções declaradas como `extern "C" fn`.
+
+[rust abi]: https://github.com/rust-lang/rfcs/issues/600
+
+### Registradores Preservados e Scratch
+A convenção de chamada divide os registradores em duas partes: registradores _preservados_ e _scratch_.
+
+Os valores dos registradores _preservados_ devem permanecer inalterados entre chamadas de função. Portanto, uma função chamada (a _"callee"_) só tem permissão para sobrescrever esses registradores se restaurar seus valores originais antes de retornar. Portanto, esses registradores são chamados de _"callee-saved"_. Um padrão comum é salvar esses registradores na pilha no início da função e restaurá-los logo antes de retornar.
+
+Em contraste, uma função chamada tem permissão para sobrescrever registradores _scratch_ sem restrições. Se o chamador quiser preservar o valor de um registrador scratch entre uma chamada de função, ele precisa fazer backup e restaurá-lo antes da chamada de função (por exemplo, empurrando-o para a pilha). Portanto, os registradores scratch são _caller-saved_.
+
+No x86_64, a convenção de chamada C especifica os seguintes registradores preservados e scratch:
+
+registradores preservados | registradores scratch
+---|---
+`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11`
+_callee-saved_ | _caller-saved_
+
+O compilador conhece essas regras, então gera o código de acordo. Por exemplo, a maioria das funções começa com um `push rbp`, que faz backup de `rbp` na pilha (porque é um registrador callee-saved).
+
+### Preservando Todos os Registradores
+Em contraste com chamadas de função, exceções podem ocorrer em _qualquer_ instrução. Na maioria dos casos, não sabemos nem em tempo de compilação se o código gerado causará uma exceção. Por exemplo, o compilador não pode saber se uma instrução causa um stack overflow ou um page fault.
+
+Como não sabemos quando uma exceção ocorre, não podemos fazer backup de nenhum registrador antes. Isso significa que não podemos usar uma convenção de chamada que depende de registradores caller-saved para manipuladores de exceção. Em vez disso, precisamos de uma convenção de chamada que preserva _todos os registradores_. A convenção de chamada `x86-interrupt` é tal convenção de chamada, então garante que todos os valores de registrador são restaurados para seus valores originais no retorno da função.
+
+Note que isso não significa que todos os registradores são salvos na pilha na entrada da função. Em vez disso, o compilador apenas faz backup dos registradores que são sobrescritos pela função. Desta forma, código muito eficiente pode ser gerado para funções curtas que usam apenas alguns registradores.
+
+### O Stack Frame de Interrupção
+Em uma chamada de função normal (usando a instrução `call`), a CPU empurra o endereço de retorno antes de pular para a função alvo. No retorno da função (usando a instrução `ret`), a CPU retira este endereço de retorno e pula para ele. Então o stack frame de uma chamada de função normal se parece com isto:
+
+
+
+Para manipuladores de exceção e interrupção, no entanto, empurrar um endereço de retorno não seria suficiente, já que manipuladores de interrupção frequentemente executam em um contexto diferente (ponteiro de pilha, flags da CPU, etc.). Em vez disso, a CPU executa os seguintes passos quando uma interrupção ocorre:
+
+0. **Salvando o antigo ponteiro de pilha**: A CPU lê os valores dos registradores ponteiro de pilha (`rsp`) e segmento de pilha (`ss`) e os lembra em um buffer interno.
+1. **Alinhando o ponteiro de pilha**: Uma interrupção pode ocorrer em qualquer instrução, então o ponteiro de pilha pode ter qualquer valor também. No entanto, algumas instruções de CPU (por exemplo, algumas instruções SSE) requerem que o ponteiro de pilha esteja alinhado em um limite de 16 bytes, então a CPU realiza tal alinhamento logo após a interrupção.
+2. **Trocando pilhas** (em alguns casos): Uma troca de pilha ocorre quando o nível de privilégio da CPU muda, por exemplo, quando uma exceção de CPU ocorre em um programa em modo usuário. Também é possível configurar trocas de pilha para interrupções específicas usando a chamada _Interrupt Stack Table_ (descrita na próxima postagem).
+3. **Empurrando o antigo ponteiro de pilha**: A CPU empurra os valores `rsp` e `ss` do passo 0 para a pilha. Isso torna possível restaurar o ponteiro de pilha original ao retornar de um manipulador de interrupção.
+4. **Empurrando e atualizando o registrador `RFLAGS`**: O registrador [`RFLAGS`] contém vários bits de controle e status. Na entrada de interrupção, a CPU muda alguns bits e empurra o valor antigo.
+5. **Empurrando o ponteiro de instrução**: Antes de pular para a função manipuladora de interrupção, a CPU empurra o ponteiro de instrução (`rip`) e o segmento de código (`cs`). Isso é comparável ao push de endereço de retorno de uma chamada de função normal.
+6. **Empurrando um código de erro** (para algumas exceções): Para algumas exceções específicas, como page faults, a CPU empurra um código de erro, que descreve a causa da exceção.
+7. **Invocando o manipulador de interrupção**: A CPU lê o endereço e o descritor de segmento da função manipuladora de interrupção do campo correspondente na IDT. Ela então invoca este manipulador carregando os valores nos registradores `rip` e `cs`.
+
+[`RFLAGS`]: https://en.wikipedia.org/wiki/FLAGS_register
+
+Então o _interrupt stack frame_ se parece com isto:
+
+
+
+Na crate `x86_64`, o interrupt stack frame é representado pela struct [`InterruptStackFrame`]. Ela é passada para manipuladores de interrupção como `&mut` e pode ser usada para recuperar informações adicionais sobre a causa da exceção. A struct não contém campo de código de erro, já que apenas algumas exceções empurram um código de erro. Essas exceções usam o tipo de função [`HandlerFuncWithErrCode`] separado, que tem um argumento adicional `error_code`.
+
+[`InterruptStackFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptStackFrame.html
+
+### Por Trás das Cortinas
+A convenção de chamada `x86-interrupt` é uma abstração poderosa que esconde quase todos os detalhes confusos do processo de manipulação de exceção. No entanto, às vezes é útil saber o que está acontecendo por trás das cortinas. Aqui está uma breve visão geral das coisas das quais a convenção de chamada `x86-interrupt` cuida:
+
+- **Recuperando os argumentos**: A maioria das convenções de chamada espera que os argumentos sejam passados em registradores. Isso não é possível para manipuladores de exceção, já que não devemos sobrescrever nenhum valor de registrador antes de fazer backup deles na pilha. Em vez disso, a convenção de chamada `x86-interrupt` está ciente de que os argumentos já estão na pilha em um deslocamento específico.
+- **Retornando usando `iretq`**: Como o interrupt stack frame difere completamente dos stack frames de chamadas de função normais, não podemos retornar de funções manipuladoras através da instrução `ret` normal. Então, em vez disso, a instrução `iretq` deve ser usada.
+- **Manipulando o código de erro**: O código de erro, que é empurrado para algumas exceções, torna as coisas muito mais complexas. Ele muda o alinhamento da pilha (veja o próximo ponto) e precisa ser retirado da pilha antes de retornar. A convenção de chamada `x86-interrupt` manipula toda essa complexidade. No entanto, ela não sabe qual função manipuladora é usada para qual exceção, então precisa deduzir essa informação do número de argumentos da função. Isso significa que o programador ainda é responsável por usar o tipo de função correto para cada exceção. Felizmente, o tipo `InterruptDescriptorTable` definido pela crate `x86_64` garante que os tipos de função corretos são usados.
+- **Alinhando a pilha**: Algumas instruções (especialmente instruções SSE) requerem um alinhamento de pilha de 16 bytes. A CPU garante esse alinhamento sempre que uma exceção ocorre, mas para algumas exceções ela o destrói novamente mais tarde quando empurra um código de erro. A convenção de chamada `x86-interrupt` cuida disso realinhando a pilha neste caso.
+
+Se você estiver interessado em mais detalhes, também temos uma série de postagens que explica a manipulação de exceção usando [funções nuas] vinculadas [no final desta postagem][too-much-magic].
+
+[funções nuas]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
+[too-much-magic]: #muita-magia
+
+## Implementação
+Agora que entendemos a teoria, é hora de manipular exceções de CPU em nosso kernel. Começaremos criando um novo módulo interrupts em `src/interrupts.rs`, que primeiro cria uma função `init_idt` que cria uma nova `InterruptDescriptorTable`:
+
+``` rust
+// em src/lib.rs
+
+pub mod interrupts;
+
+// em src/interrupts.rs
+
+use x86_64::structures::idt::InterruptDescriptorTable;
+
+pub fn init_idt() {
+ let mut idt = InterruptDescriptorTable::new();
+}
+```
+
+Agora podemos adicionar funções manipuladoras. Começamos adicionando um manipulador para a [exceção de breakpoint]. A exceção de breakpoint é a exceção perfeita para testar a manipulação de exceção. Seu único propósito é pausar temporariamente um programa quando a instrução de breakpoint `int3` é executada.
+
+[exceção de breakpoint]: https://wiki.osdev.org/Exceptions#Breakpoint
+
+A exceção de breakpoint é comumente usada em debuggers: Quando o usuário define um breakpoint, o debugger sobrescreve a instrução correspondente com a instrução `int3` para que a CPU lance a exceção de breakpoint quando atinge aquela linha. Quando o usuário quer continuar o programa, o debugger substitui a instrução `int3` pela instrução original novamente e continua o programa. Para mais detalhes, veja a série ["_How debuggers work_"].
+
+["_How debuggers work_"]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
+
+Para nosso caso de uso, não precisamos sobrescrever nenhuma instrução. Em vez disso, queremos apenas imprimir uma mensagem quando a instrução de breakpoint é executada e então continuar o programa. Então vamos criar uma função `breakpoint_handler` simples e adicioná-la à nossa IDT:
+
+```rust
+// em src/interrupts.rs
+
+use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
+use crate::println;
+
+pub fn init_idt() {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+}
+
+extern "x86-interrupt" fn breakpoint_handler(
+ stack_frame: InterruptStackFrame)
+{
+ println!("EXCEÇÃO: BREAKPOINT\n{:#?}", stack_frame);
+}
+```
+
+Nosso manipulador apenas produz uma mensagem e imprime de forma bonita o interrupt stack frame.
+
+Quando tentamos compilá-lo, o seguinte erro ocorre:
+
+```
+error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue #40180)
+ --> src/main.rs:53:1
+ |
+53 | / extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
+54 | | println!("EXCEÇÃO: BREAKPOINT\n{:#?}", stack_frame);
+55 | | }
+ | |_^
+ |
+ = help: add #![feature(abi_x86_interrupt)] to the crate attributes to enable
+```
+
+Este erro ocorre porque a convenção de chamada `x86-interrupt` ainda é instável. Para usá-la de qualquer forma, temos que habilitá-la explicitamente adicionando `#![feature(abi_x86_interrupt)]` no topo do nosso `lib.rs`.
+
+### Carregando a IDT
+Para que a CPU use nossa nova tabela de descritores de interrupção, precisamos carregá-la usando a instrução [`lidt`]. A struct `InterruptDescriptorTable` da crate `x86_64` fornece um método [`load`][InterruptDescriptorTable::load] para isso. Vamos tentar usá-lo:
+
+[`lidt`]: https://www.felixcloutier.com/x86/lgdt:lidt
+[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
+
+```rust
+// em src/interrupts.rs
+
+pub fn init_idt() {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ idt.load();
+}
+```
+
+Quando tentamos compilar agora, o seguinte erro ocorre:
+
+```
+error: `idt` does not live long enough
+ --> src/interrupts/mod.rs:43:5
+ |
+43 | idt.load();
+ | ^^^ does not live long enough
+44 | }
+ | - borrowed value only lives until here
+ |
+ = note: borrowed value must be valid for the static lifetime...
+```
+
+Então o método `load` espera um `&'static self`, isto é, uma referência válida para o tempo de execução completo do programa. A razão é que a CPU acessará esta tabela em cada interrupção até carregarmos uma IDT diferente. Então usar um tempo de vida menor que `'static` poderia levar a bugs de use-after-free.
+
+De fato, isso é exatamente o que acontece aqui. Nossa `idt` é criada na pilha, então ela é válida apenas dentro da função `init`. Depois, a memória da pilha é reutilizada para outras funções, então a CPU interpretaria memória aleatória da pilha como IDT. Felizmente, o método `InterruptDescriptorTable::load` codifica este requisito de tempo de vida em sua definição de função, para que o compilador Rust seja capaz de prevenir este possível bug em tempo de compilação.
+
+Para corrigir este problema, precisamos armazenar nossa `idt` em um lugar onde ela tenha um tempo de vida `'static`. Para conseguir isso, poderíamos alocar nossa IDT no heap usando [`Box`] e então convertê-la para uma referência `'static`, mas estamos escrevendo um kernel de SO e, portanto, não temos um heap (ainda).
+
+[`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
+
+
+Como alternativa, poderíamos tentar armazenar a IDT como uma `static`:
+
+```rust
+static IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
+
+pub fn init_idt() {
+ IDT.breakpoint.set_handler_fn(breakpoint_handler);
+ IDT.load();
+}
+```
+
+No entanto, há um problema: Statics são imutáveis, então não podemos modificar a entrada de breakpoint da nossa função `init`. Poderíamos resolver este problema usando uma [`static mut`]:
+
+[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
+
+```rust
+static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
+
+pub fn init_idt() {
+ unsafe {
+ IDT.breakpoint.set_handler_fn(breakpoint_handler);
+ IDT.load();
+ }
+}
+```
+
+Esta variante compila sem erros, mas está longe de ser idiomática. `static mut`s são muito propensas a data races, então precisamos de um [bloco `unsafe`] em cada acesso.
+
+[bloco `unsafe`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
+
+#### Lazy Statics ao Resgate
+Felizmente, a macro `lazy_static` existe. Em vez de avaliar uma `static` em tempo de compilação, a macro realiza a inicialização quando a `static` é referenciada pela primeira vez. Assim, podemos fazer quase tudo no bloco de inicialização e somos até capazes de ler valores de tempo de execução.
+
+Já importamos a crate `lazy_static` quando [criamos uma abstração para o buffer de texto VGA][vga text buffer lazy static]. Então podemos usar diretamente a macro `lazy_static!` para criar nossa IDT estática:
+
+[vga text buffer lazy static]: @/edition-2/posts/03-vga-text-buffer/index.md#lazy-statics
+
+```rust
+// em src/interrupts.rs
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ idt
+ };
+}
+
+pub fn init_idt() {
+ IDT.load();
+}
+```
+
+Note como esta solução não requer blocos `unsafe`. A macro `lazy_static!` usa `unsafe` por trás dos panos, mas é abstraída em uma interface segura.
+
+### Executando
+
+O último passo para fazer exceções funcionarem em nosso kernel é chamar a função `init_idt` do nosso `main.rs`. Em vez de chamá-la diretamente, introduzimos uma função geral `init` em nosso `lib.rs`:
+
+```rust
+// em src/lib.rs
+
+pub fn init() {
+ interrupts::init_idt();
+}
+```
+
+Com esta função, agora temos um lugar central para rotinas de inicialização que podem ser compartilhadas entre as diferentes funções `_start` em nosso `main.rs`, `lib.rs` e testes de integração.
+
+Agora podemos atualizar a função `_start` do nosso `main.rs` para chamar `init` e então disparar uma exceção de breakpoint:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Olá Mundo{}", "!");
+
+ blog_os::init(); // novo
+
+ // invoca uma exceção de breakpoint
+ x86_64::instructions::interrupts::int3(); // novo
+
+ // como antes
+ #[cfg(test)]
+ test_main();
+
+ println!("Não crashou!");
+ loop {}
+}
+```
+
+Quando executamos agora no QEMU (usando `cargo run`), vemos o seguinte:
+
+
+
+Funciona! A CPU invoca com sucesso nosso manipulador de breakpoint, que imprime a mensagem, e então retorna de volta para a função `_start`, onde a mensagem `Não crashou!` é impressa.
+
+Vemos que o interrupt stack frame nos diz os ponteiros de instrução e pilha no momento em que a exceção ocorreu. Esta informação é muito útil ao depurar exceções inesperadas.
+
+### Adicionando um Teste
+
+Vamos criar um teste que garante que o acima continue funcionando. Primeiro, atualizamos a função `_start` para também chamar `init`:
+
+```rust
+// em src/lib.rs
+
+/// Ponto de entrada para `cargo test`
+#[cfg(test)]
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ init(); // novo
+ test_main();
+ loop {}
+}
+```
+
+Lembre-se, esta função `_start` é usada quando executamos `cargo test --lib`, já que Rust testa o `lib.rs` completamente independente do `main.rs`. Precisamos chamar `init` aqui para configurar uma IDT antes de executar os testes.
+
+Agora podemos criar um teste `test_breakpoint_exception`:
+
+```rust
+// em src/interrupts.rs
+
+#[test_case]
+fn test_breakpoint_exception() {
+ // invoca uma exceção de breakpoint
+ x86_64::instructions::interrupts::int3();
+}
+```
+
+O teste invoca a função `int3` para disparar uma exceção de breakpoint. Ao verificar que a execução continua depois, verificamos que nosso manipulador de breakpoint está funcionando corretamente.
+
+Você pode tentar este novo teste executando `cargo test` (todos os testes) ou `cargo test --lib` (apenas testes de `lib.rs` e seus módulos). Você deve ver o seguinte na saída:
+
+```
+blog_os::interrupts::test_breakpoint_exception... [ok]
+```
+
+## Muita Mágica?
+A convenção de chamada `x86-interrupt` e o tipo [`InterruptDescriptorTable`] tornaram o processo de manipulação de exceção relativamente simples e indolor. Se isso foi muita mágica para você e você gostaria de aprender todos os detalhes sórdidos da manipulação de exceção, nós temos você coberto: Nossa série ["Manipulando Exceções com Funções Nuas"] mostra como manipular exceções sem a convenção de chamada `x86-interrupt` e também cria seu próprio tipo IDT. Historicamente, essas postagens eram as principais postagens de manipulação de exceção antes que a convenção de chamada `x86-interrupt` e a crate `x86_64` existissem. Note que essas postagens são baseadas na [primeira edição] deste blog e podem estar desatualizadas.
+
+["Manipulando Exceções com Funções Nuas"]: @/edition-1/extra/naked-exceptions/_index.md
+[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
+[primeira edição]: @/edition-1/_index.md
+
+## O Que Vem a Seguir?
+Capturamos com sucesso nossa primeira exceção e retornamos dela! O próximo passo é garantir que capturemos todas as exceções porque uma exceção não capturada causa um [triple fault] fatal, que leva a uma redefinição do sistema. A próxima postagem explica como podemos evitar isso capturando corretamente [double faults].
+
+[triple fault]: https://wiki.osdev.org/Triple_Fault
+[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
\ No newline at end of file
From 30eaf1b4e70131325e99187140fdf9ca38f1595e Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 23:07:43 -0300
Subject: [PATCH 07/26] [Translation][Portuguese pt-BR] post-6 (edition-2)
---
.../posts/06-double-faults/index.pt-BR.md | 556 ++++++++++++++++++
1 file changed, 556 insertions(+)
create mode 100644 blog/content/edition-2/posts/06-double-faults/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/06-double-faults/index.pt-BR.md b/blog/content/edition-2/posts/06-double-faults/index.pt-BR.md
new file mode 100644
index 00000000..e9d2a90d
--- /dev/null
+++ b/blog/content/edition-2/posts/06-double-faults/index.pt-BR.md
@@ -0,0 +1,556 @@
++++
+title = "Double Faults"
+weight = 6
+path = "pt-BR/double-fault-exceptions"
+date = 2018-06-18
+
+[extra]
+chapter = "Interrupções"
+# Please update this when updating the translation
+translation_based_on_commit = "9753695744854686a6b80012c89b0d850a44b4b0"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Esta postagem explora a exceção de double fault em detalhes, que ocorre quando a CPU falha ao invocar um manipulador de exceção. Ao manipular esta exceção, evitamos _triple faults_ fatais que causam uma redefinição do sistema. Para prevenir triple faults em todos os casos, também configuramos uma _Interrupt Stack Table_ para capturar double faults em uma pilha de kernel separada.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-06`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-06
+
+
+
+## O Que é um Double Fault?
+Em termos simplificados, um double fault é uma exceção especial que ocorre quando a CPU falha ao invocar um manipulador de exceção. Por exemplo, ele ocorre quando um page fault é disparado mas não há manipulador de page fault registrado na [Tabela de Descritores de Interrupção][IDT] (IDT). Então é meio similar aos blocos catch-all em linguagens de programação com exceções, por exemplo, `catch(...)` em C++ ou `catch(Exception e)` em Java ou C#.
+
+[IDT]: @/edition-2/posts/05-cpu-exceptions/index.md#the-interrupt-descriptor-table
+
+Um double fault se comporta como uma exceção normal. Ele tem o número de vetor `8` e podemos definir uma função manipuladora normal para ele na IDT. É realmente importante fornecer um manipulador de double fault, porque se um double fault não é manipulado, ocorre um _triple fault_ fatal. Triple faults não podem ser capturados, e a maioria do hardware reage com uma redefinição do sistema.
+
+### Disparando um Double Fault
+Vamos provocar um double fault disparando uma exceção para a qual não definimos uma função manipuladora:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Olá Mundo{}", "!");
+
+ blog_os::init();
+
+ // dispara um page fault
+ unsafe {
+ *(0xdeadbeef as *mut u8) = 42;
+ };
+
+ // como antes
+ #[cfg(test)]
+ test_main();
+
+ println!("Não crashou!");
+ loop {}
+}
+```
+
+Usamos `unsafe` para escrever no endereço inválido `0xdeadbeef`. O endereço virtual não está mapeado para um endereço físico nas tabelas de página, então ocorre um page fault. Não registramos um manipulador de page fault em nossa [IDT], então ocorre um double fault.
+
+Quando iniciamos nosso kernel agora, vemos que ele entra em um loop de boot infinito. A razão para o loop de boot é a seguinte:
+
+1. A CPU tenta escrever em `0xdeadbeef`, o que causa um page fault.
+2. A CPU olha para a entrada correspondente na IDT e vê que nenhuma função manipuladora está especificada. Assim, ela não pode chamar o manipulador de page fault e ocorre um double fault.
+3. A CPU olha para a entrada IDT do manipulador de double fault, mas esta entrada também não especifica uma função manipuladora. Assim, ocorre um _triple_ fault.
+4. Um triple fault é fatal. QEMU reage a ele como a maioria do hardware real e emite uma redefinição do sistema.
+
+Então, para prevenir este triple fault, precisamos fornecer uma função manipuladora para page faults ou um manipulador de double fault. Queremos evitar triple faults em todos os casos, então vamos começar com um manipulador de double fault que é invocado para todos os tipos de exceção não manipulados.
+
+## Um Manipulador de Double Fault
+Um double fault é uma exceção normal com um código de erro, então podemos especificar uma função manipuladora similar ao nosso manipulador de breakpoint:
+
+```rust
+// em src/interrupts.rs
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ idt.double_fault.set_handler_fn(double_fault_handler); // novo
+ idt
+ };
+}
+
+// novo
+extern "x86-interrupt" fn double_fault_handler(
+ stack_frame: InterruptStackFrame, _error_code: u64) -> !
+{
+ panic!("EXCEÇÃO: DOUBLE FAULT\n{:#?}", stack_frame);
+}
+```
+
+Nosso manipulador imprime uma mensagem de erro curta e despeja o exception stack frame. O código de erro do manipulador de double fault é sempre zero, então não há razão para imprimi-lo. Uma diferença para o manipulador de breakpoint é que o manipulador de double fault é [_divergente_]. A razão é que a arquitetura `x86_64` não permite retornar de uma exceção de double fault.
+
+[_divergente_]: https://doc.rust-lang.org/stable/rust-by-example/fn/diverging.html
+
+Quando iniciamos nosso kernel agora, devemos ver que o manipulador de double fault é invocado:
+
+
+
+Funcionou! Aqui está o que aconteceu desta vez:
+
+1. A CPU tenta escrever em `0xdeadbeef`, o que causa um page fault.
+2. Como antes, a CPU olha para a entrada correspondente na IDT e vê que nenhuma função manipuladora está definida. Assim, ocorre um double fault.
+3. A CPU pula para o manipulador de double fault – agora presente.
+
+O triple fault (e o loop de boot) não ocorre mais, já que a CPU agora pode chamar o manipulador de double fault.
+
+Isso foi bem direto! Então por que precisamos de uma postagem inteira para este tópico? Bem, agora somos capazes de capturar a _maioria_ dos double faults, mas há alguns casos onde nossa abordagem atual não é suficiente.
+
+## Causas de Double Faults
+Antes de olharmos para os casos especiais, precisamos conhecer as causas exatas de double faults. Acima, usamos uma definição bem vaga:
+
+> Um double fault é uma exceção especial que ocorre quando a CPU falha ao invocar um manipulador de exceção.
+
+O que _"falha ao invocar"_ significa exatamente? O manipulador não está presente? O manipulador está [trocado para fora]? E o que acontece se um manipulador causa exceções ele mesmo?
+
+[trocado para fora]: http://pages.cs.wisc.edu/~remzi/OSTEP/vm-beyondphys.pdf
+
+Por exemplo, o que acontece se:
+
+1. uma exceção de breakpoint ocorre, mas a função manipuladora correspondente está trocada para fora?
+2. um page fault ocorre, mas o manipulador de page fault está trocado para fora?
+3. um manipulador de divide-by-zero causa uma exceção de breakpoint, mas o manipulador de breakpoint está trocado para fora?
+4. nosso kernel estoura sua pilha e a _guard page_ é atingida?
+
+Felizmente, o manual AMD64 ([PDF][AMD64 manual]) tem uma definição exata (na Seção 8.2.9). De acordo com ele, uma "exceção de double fault _pode_ ocorrer quando uma segunda exceção ocorre durante a manipulação de um manipulador de exceção anterior (primeira)". O _"pode"_ é importante: Apenas combinações muito específicas de exceções levam a um double fault. Essas combinações são:
+
+| Primeira Exceção | Segunda Exceção |
+| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
+| [Divide-by-zero], [Invalid TSS], [Segment Not Present], [Stack-Segment Fault], [General Protection Fault] | [Invalid TSS], [Segment Not Present], [Stack-Segment Fault], [General Protection Fault] |
+| [Page Fault] | [Page Fault], [Invalid TSS], [Segment Not Present], [Stack-Segment Fault], [General Protection Fault] |
+
+[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
+[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
+[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
+[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
+[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
+[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
+
+
+[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
+
+Então, por exemplo, uma falha de divide-by-zero seguida de um page fault está ok (o manipulador de page fault é invocado), mas uma falha de divide-by-zero seguida de um general-protection fault leva a um double fault.
+
+Com a ajuda desta tabela, podemos responder às primeiras três das questões acima:
+
+1. Se uma exceção de breakpoint ocorre e a função manipuladora correspondente está trocada para fora, ocorre um _page fault_ e o _manipulador de page fault_ é invocado.
+2. Se um page fault ocorre e o manipulador de page fault está trocado para fora, ocorre um _double fault_ e o _manipulador de double fault_ é invocado.
+3. Se um manipulador de divide-by-zero causa uma exceção de breakpoint, a CPU tenta invocar o manipulador de breakpoint. Se o manipulador de breakpoint está trocado para fora, ocorre um _page fault_ e o _manipulador de page fault_ é invocado.
+
+De fato, até o caso de uma exceção sem função manipuladora na IDT segue este esquema: Quando a exceção ocorre, a CPU tenta ler a entrada IDT correspondente. Como a entrada é 0, que não é uma entrada IDT válida, ocorre um _general protection fault_. Não definimos uma função manipuladora para o general protection fault também, então outro general protection fault ocorre. De acordo com a tabela, isso leva a um double fault.
+
+### Kernel Stack Overflow
+Vamos olhar para a quarta questão:
+
+> O que acontece se nosso kernel estoura sua pilha e a guard page é atingida?
+
+Uma guard page é uma página de memória especial na parte inferior de uma pilha que torna possível detectar estouros de pilha. A página não está mapeada para nenhum frame físico, então acessá-la causa um page fault em vez de silenciosamente corromper outra memória. O bootloader configura uma guard page para nossa pilha de kernel, então um stack overflow causa um _page fault_.
+
+Quando um page fault ocorre, a CPU olha para o manipulador de page fault na IDT e tenta empurrar o [interrupt stack frame] na pilha. No entanto, o ponteiro de pilha atual ainda aponta para a guard page não presente. Assim, ocorre um segundo page fault, que causa um double fault (de acordo com a tabela acima).
+
+[interrupt stack frame]: @/edition-2/posts/05-cpu-exceptions/index.md#the-interrupt-stack-frame
+
+Então a CPU tenta chamar o _manipulador de double fault_ agora. No entanto, em um double fault, a CPU tenta empurrar o exception stack frame também. O ponteiro de pilha ainda aponta para a guard page, então ocorre um _terceiro_ page fault, que causa um _triple fault_ e uma reinicialização do sistema. Então nosso manipulador de double fault atual não pode evitar um triple fault neste caso.
+
+Vamos tentar nós mesmos! Podemos facilmente provocar um kernel stack overflow chamando uma função que recursa infinitamente:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)] // não altere (mangle) o nome desta função
+pub extern "C" fn _start() -> ! {
+ println!("Olá Mundo{}", "!");
+
+ blog_os::init();
+
+ fn stack_overflow() {
+ stack_overflow(); // para cada recursão, o endereço de retorno é empurrado
+ }
+
+ // dispara um stack overflow
+ stack_overflow();
+
+ […] // test_main(), println(…), e loop {}
+}
+```
+
+Quando tentamos este código no QEMU, vemos que o sistema entra em um bootloop novamente.
+
+Então como podemos evitar este problema? Não podemos omitir o empurrar do exception stack frame, já que a própria CPU faz isso. Então precisamos garantir de alguma forma que a pilha esteja sempre válida quando uma exceção de double fault ocorre. Felizmente, a arquitetura x86_64 tem uma solução para este problema.
+
+## Trocando Pilhas
+A arquitetura x86_64 é capaz de trocar para uma pilha predefinida e conhecida como boa quando uma exceção ocorre. Esta troca acontece em nível de hardware, então pode ser realizada antes que a CPU empurre o exception stack frame.
+
+O mecanismo de troca é implementado como uma _Interrupt Stack Table_ (IST). A IST é uma tabela de 7 ponteiros para pilhas conhecidas como boas. Em pseudocódigo similar a Rust:
+
+```rust
+struct InterruptStackTable {
+ stack_pointers: [Option; 7],
+}
+```
+
+Para cada manipulador de exceção, podemos escolher uma pilha da IST através do campo `stack_pointers` na [entrada IDT] correspondente. Por exemplo, nosso manipulador de double fault poderia usar a primeira pilha na IST. Então a CPU automaticamente troca para esta pilha sempre que ocorre um double fault. Esta troca aconteceria antes de qualquer coisa ser empurrada, prevenindo o triple fault.
+
+[entrada IDT]: @/edition-2/posts/05-cpu-exceptions/index.md#the-interrupt-descriptor-table
+
+### A IST e TSS
+A Interrupt Stack Table (IST) é parte de uma antiga estrutura legada chamada _[Task State Segment]_ \(TSS). A TSS costumava armazenar várias informações (por exemplo, estado de registradores do processador) sobre uma tarefa no modo de 32 bits e era, por exemplo, usada para [troca de contexto de hardware]. No entanto, a troca de contexto de hardware não é mais suportada no modo de 64 bits e o formato da TSS mudou completamente.
+
+[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
+[troca de contexto de hardware]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
+
+No x86_64, a TSS não armazena mais nenhuma informação específica de tarefa. Em vez disso, ela armazena duas tabelas de pilha (a IST é uma delas). O único campo comum entre a TSS de 32 bits e 64 bits é o ponteiro para o [bitmap de permissões de porta I/O].
+
+[bitmap de permissões de porta I/O]: https://en.wikipedia.org/wiki/Task_state_segment#I.2FO_port_permissions
+
+A TSS de 64 bits tem o seguinte formato:
+
+| Campo | Tipo |
+| -------------------------------------------- | ---------- |
+| (reservado) | `u32` |
+| Privilege Stack Table | `[u64; 3]` |
+| (reservado) | `u64` |
+| Interrupt Stack Table | `[u64; 7]` |
+| (reservado) | `u64` |
+| (reservado) | `u16` |
+| I/O Map Base Address | `u16` |
+
+A _Privilege Stack Table_ é usada pela CPU quando o nível de privilégio muda. Por exemplo, se uma exceção ocorre enquanto a CPU está em modo usuário (nível de privilégio 3), a CPU normalmente troca para o modo kernel (nível de privilégio 0) antes de invocar o manipulador de exceção. Nesse caso, a CPU trocaria para a 0ª pilha na Privilege Stack Table (já que 0 é o nível de privilégio alvo). Ainda não temos nenhum programa em modo usuário, então ignoraremos esta tabela por enquanto.
+
+### Criando uma TSS
+Vamos criar uma nova TSS que contém uma pilha de double fault separada em sua interrupt stack table. Para isso, precisamos de uma struct TSS. Felizmente, a crate `x86_64` já contém uma [struct `TaskStateSegment`] que podemos usar.
+
+[struct `TaskStateSegment`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/tss/struct.TaskStateSegment.html
+
+Criamos a TSS em um novo módulo `gdt` (o nome fará sentido mais tarde):
+
+```rust
+// em src/lib.rs
+
+pub mod gdt;
+
+// em src/gdt.rs
+
+use x86_64::VirtAddr;
+use x86_64::structures::tss::TaskStateSegment;
+use lazy_static::lazy_static;
+
+pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
+
+lazy_static! {
+ static ref TSS: TaskStateSegment = {
+ let mut tss = TaskStateSegment::new();
+ tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
+ const STACK_SIZE: usize = 4096 * 5;
+ static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
+
+ let stack_start = VirtAddr::from_ptr(&raw const STACK);
+ let stack_end = stack_start + STACK_SIZE;
+ stack_end
+ };
+ tss
+ };
+}
+```
+
+Usamos `lazy_static` porque o avaliador const de Rust ainda não é poderoso o suficiente para fazer esta inicialização em tempo de compilação. Definimos que a 0ª entrada IST é a pilha de double fault (qualquer outro índice IST funcionaria também). Então escrevemos o endereço superior de uma pilha de double fault na 0ª entrada. Escrevemos o endereço superior porque pilhas no x86 crescem para baixo, isto é, de endereços altos para endereços baixos.
+
+Ainda não implementamos gerenciamento de memória, então não temos uma forma apropriada de alocar uma nova pilha. Em vez disso, usamos um array `static mut` como armazenamento de pilha por enquanto. É importante que seja uma `static mut` e não uma `static` imutável, porque caso contrário o bootloader a mapearia para uma página somente leitura. Substituiremos isto por uma alocação de pilha apropriada em uma postagem posterior.
+
+Note que esta pilha de double fault não tem guard page que proteja contra stack overflow. Isso significa que não devemos fazer nada intensivo em pilha em nosso manipulador de double fault porque um stack overflow poderia corromper a memória abaixo da pilha.
+
+#### Carregando a TSS
+Agora que criamos uma nova TSS, precisamos de uma forma de dizer à CPU que ela deve usá-la. Infelizmente, isto é um pouco trabalhoso, já que a TSS usa o sistema de segmentação (por razões históricas). Em vez de carregar a tabela diretamente, precisamos adicionar um novo descritor de segmento à [Tabela de Descritores Globais] \(GDT). Então podemos carregar nossa TSS invocando a [instrução `ltr`] com o respectivo índice GDT. (Esta é a razão pela qual nomeamos nosso módulo `gdt`.)
+
+[Tabela de Descritores Globais]: https://web.archive.org/web/20190217233448/https://www.flingos.co.uk/docs/reference/Global-Descriptor-Table/
+[instrução `ltr`]: https://www.felixcloutier.com/x86/ltr
+
+### A Tabela de Descritores Globais
+A Tabela de Descritores Globais (GDT - Global Descriptor Table) é uma relíquia que foi usada para [segmentação de memória] antes de paginação se tornar o padrão de fato. No entanto, ela ainda é necessária no modo de 64 bits para várias coisas, como configuração de modo kernel/usuário ou carregamento de TSS.
+
+[segmentação de memória]: https://en.wikipedia.org/wiki/X86_memory_segmentation
+
+A GDT é uma estrutura que contém os _segmentos_ do programa. Ela foi usada em arquiteturas mais antigas para isolar programas uns dos outros antes de paginação se tornar o padrão. Para mais informações sobre segmentação, confira o capítulo de mesmo nome do livro gratuito ["Three Easy Pieces"]. Embora a segmentação não seja mais suportada no modo de 64 bits, a GDT ainda existe. Ela é usada principalmente para duas coisas: Trocar entre espaço de kernel e espaço de usuário, e carregar uma estrutura TSS.
+
+["Three Easy Pieces"]: http://pages.cs.wisc.edu/~remzi/OSTEP/
+
+#### Criando uma GDT
+Vamos criar uma `GDT` estática que inclui um segmento para nossa `TSS` estática:
+
+```rust
+// em src/gdt.rs
+
+use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
+
+lazy_static! {
+ static ref GDT: GlobalDescriptorTable = {
+ let mut gdt = GlobalDescriptorTable::new();
+ gdt.add_entry(Descriptor::kernel_code_segment());
+ gdt.add_entry(Descriptor::tss_segment(&TSS));
+ gdt
+ };
+}
+```
+
+Como antes, usamos `lazy_static` novamente. Criamos uma nova GDT com um segmento de código e um segmento TSS.
+
+#### Carregando a GDT
+
+Para carregar nossa GDT, criamos uma nova função `gdt::init` que chamamos de nossa função `init`:
+
+```rust
+// em src/gdt.rs
+
+pub fn init() {
+ GDT.load();
+}
+
+// em src/lib.rs
+
+pub fn init() {
+ gdt::init();
+ interrupts::init_idt();
+}
+```
+
+Agora nossa GDT está carregada (já que a função `_start` chama `init`), mas ainda vemos o loop de boot no stack overflow.
+
+### Os Passos Finais
+
+O problema é que os segmentos GDT ainda não estão ativos porque os registradores de segmento e TSS ainda contêm os valores da GDT antiga. Também precisamos modificar a entrada IDT de double fault para que ela use a nova pilha.
+
+Em resumo, precisamos fazer o seguinte:
+
+1. **Recarregar registrador de segmento de código**: Mudamos nossa GDT, então devemos recarregar `cs`, o registrador de segmento de código. Isso é necessário já que o antigo seletor de segmento poderia agora apontar para um descritor GDT diferente (por exemplo, um descritor TSS).
+2. **Carregar a TSS**: Carregamos uma GDT que contém um seletor TSS, mas ainda precisamos dizer à CPU que ela deve usar essa TSS.
+3. **Atualizar a entrada IDT**: Assim que nossa TSS é carregada, a CPU tem acesso a uma interrupt stack table (IST) válida. Então podemos dizer à CPU que ela deve usar nossa nova pilha de double fault modificando nossa entrada IDT de double fault.
+
+Para os dois primeiros passos, precisamos de acesso às variáveis `code_selector` e `tss_selector` em nossa função `gdt::init`. Podemos conseguir isso tornando-as parte da static através de uma nova struct `Selectors`:
+
+```rust
+// em src/gdt.rs
+
+use x86_64::structures::gdt::SegmentSelector;
+
+lazy_static! {
+ static ref GDT: (GlobalDescriptorTable, Selectors) = {
+ let mut gdt = GlobalDescriptorTable::new();
+ let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
+ let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
+ (gdt, Selectors { code_selector, tss_selector })
+ };
+}
+
+struct Selectors {
+ code_selector: SegmentSelector,
+ tss_selector: SegmentSelector,
+}
+```
+
+Agora podemos usar os seletores para recarregar o registrador `cs` e carregar nossa `TSS`:
+
+```rust
+// em src/gdt.rs
+
+pub fn init() {
+ use x86_64::instructions::tables::load_tss;
+ use x86_64::instructions::segmentation::{CS, Segment};
+
+ GDT.0.load();
+ unsafe {
+ CS::set_reg(GDT.1.code_selector);
+ load_tss(GDT.1.tss_selector);
+ }
+}
+```
+
+Recarregamos o registrador de segmento de código usando [`CS::set_reg`] e carregamos a TSS usando [`load_tss`]. As funções são marcadas como `unsafe`, então precisamos de um bloco `unsafe` para invocá-las. A razão é que pode ser possível quebrar a segurança de memória carregando seletores inválidos.
+
+[`CS::set_reg`]: https://docs.rs/x86_64/0.14.5/x86_64/instructions/segmentation/struct.CS.html#method.set_reg
+[`load_tss`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tables/fn.load_tss.html
+
+Agora que carregamos uma TSS e interrupt stack table válidas, podemos definir o índice de pilha para nosso manipulador de double fault na IDT:
+
+```rust
+// em src/interrupts.rs
+
+use crate::gdt;
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ unsafe {
+ idt.double_fault.set_handler_fn(double_fault_handler)
+ .set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX); // novo
+ }
+
+ idt
+ };
+}
+```
+
+O método `set_stack_index` é unsafe porque o chamador deve garantir que o índice usado é válido e não está já usado para outra exceção.
+
+É isso! Agora a CPU deve trocar para a pilha de double fault sempre que ocorre um double fault. Assim, somos capazes de capturar _todos_ os double faults, incluindo kernel stack overflows:
+
+
+
+De agora em diante, nunca devemos ver um triple fault novamente! Para garantir que não quebramos acidentalmente o acima, devemos adicionar um teste para isso.
+
+## Um Teste de Stack Overflow
+
+Para testar nosso novo módulo `gdt` e garantir que o manipulador de double fault é corretamente chamado em um stack overflow, podemos adicionar um teste de integração. A ideia é provocar um double fault na função de teste e verificar que o manipulador de double fault é chamado.
+
+Vamos começar com um esqueleto mínimo:
+
+```rust
+// em tests/stack_overflow.rs
+
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ unimplemented!();
+}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ blog_os::test_panic_handler(info)
+}
+```
+
+Como nosso teste `panic_handler`, o teste executará [sem um test harness]. A razão é que não podemos continuar a execução após um double fault, então mais de um teste não faz sentido. Para desativar o test harness para o teste, adicionamos o seguinte ao nosso `Cargo.toml`:
+
+```toml
+# em Cargo.toml
+
+[[test]]
+name = "stack_overflow"
+harness = false
+```
+
+[sem um test harness]: @/edition-2/posts/04-testing/index.md#no-harness-tests
+
+Agora `cargo test --test stack_overflow` deve compilar com sucesso. O teste falha, é claro, já que a macro `unimplemented` entra em panic.
+
+### Implementando `_start`
+
+A implementação da função `_start` se parece com isto:
+
+```rust
+// em tests/stack_overflow.rs
+
+use blog_os::serial_print;
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ serial_print!("stack_overflow::stack_overflow...\t");
+
+ blog_os::gdt::init();
+ init_test_idt();
+
+ // dispara um stack overflow
+ stack_overflow();
+
+ panic!("Execução continuou após stack overflow");
+}
+
+#[allow(unconditional_recursion)]
+fn stack_overflow() {
+ stack_overflow(); // para cada recursão, o endereço de retorno é empurrado
+ volatile::Volatile::new(0).read(); // previne otimizações de tail recursion
+}
+```
+
+Chamamos nossa função `gdt::init` para inicializar uma nova GDT. Em vez de chamar nossa função `interrupts::init_idt`, chamamos uma função `init_test_idt` que será explicada em um momento. A razão é que queremos registrar um manipulador de double fault customizado que faz um `exit_qemu(QemuExitCode::Success)` em vez de entrar em panic.
+
+A função `stack_overflow` é quase idêntica à função em nosso `main.rs`. A única diferença é que no final da função, realizamos uma leitura [volátil] adicional usando o tipo [`Volatile`] para prevenir uma otimização do compilador chamada [_tail call elimination_]. Entre outras coisas, esta otimização permite ao compilador transformar uma função cuja última instrução é uma chamada de função recursiva em um loop normal. Assim, nenhum stack frame adicional é criado para a chamada de função, então o uso de pilha permanece constante.
+
+[volátil]: https://en.wikipedia.org/wiki/Volatile_(computer_programming)
+[`Volatile`]: https://docs.rs/volatile/0.2.6/volatile/struct.Volatile.html
+[_tail call elimination_]: https://en.wikipedia.org/wiki/Tail_call
+
+No nosso caso, no entanto, queremos que o stack overflow aconteça, então adicionamos uma instrução de leitura volátil fictícia no final da função, que o compilador não tem permissão para remover. Assim, a função não é mais _tail recursive_, e a transformação em um loop é prevenida. Também adicionamos o atributo `allow(unconditional_recursion)` para silenciar o aviso do compilador de que a função recursa infinitamente.
+
+### A IDT de Teste
+
+Como notado acima, o teste precisa de sua própria IDT com um manipulador de double fault customizado. A implementação se parece com isto:
+
+```rust
+// em tests/stack_overflow.rs
+
+use lazy_static::lazy_static;
+use x86_64::structures::idt::InterruptDescriptorTable;
+
+lazy_static! {
+ static ref TEST_IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ unsafe {
+ idt.double_fault
+ .set_handler_fn(test_double_fault_handler)
+ .set_stack_index(blog_os::gdt::DOUBLE_FAULT_IST_INDEX);
+ }
+
+ idt
+ };
+}
+
+pub fn init_test_idt() {
+ TEST_IDT.load();
+}
+```
+
+A implementação é muito similar à nossa IDT normal em `interrupts.rs`. Como na IDT normal, definimos um índice de pilha na IST para o manipulador de double fault para trocar para uma pilha separada. A função `init_test_idt` carrega a IDT na CPU através do método `load`.
+
+### O Manipulador de Double Fault
+
+A única peça que falta é nosso manipulador de double fault. Ele se parece com isto:
+
+```rust
+// em tests/stack_overflow.rs
+
+use blog_os::{exit_qemu, QemuExitCode, serial_println};
+use x86_64::structures::idt::InterruptStackFrame;
+
+extern "x86-interrupt" fn test_double_fault_handler(
+ _stack_frame: InterruptStackFrame,
+ _error_code: u64,
+) -> ! {
+ serial_println!("[ok]");
+ exit_qemu(QemuExitCode::Success);
+ loop {}
+}
+```
+
+Quando o manipulador de double fault é chamado, saímos do QEMU com um código de saída de sucesso, o que marca o teste como aprovado. Como testes de integração são executáveis completamente separados, precisamos definir o atributo `#![feature(abi_x86_interrupt)]` novamente no topo do nosso arquivo de teste.
+
+Agora podemos executar nosso teste através de `cargo test --test stack_overflow` (ou `cargo test` para executar todos os testes). Como esperado, vemos a saída `stack_overflow... [ok]` no console. Tente comentar a linha `set_stack_index`; isso deve fazer o teste falhar.
+
+## Resumo
+Nesta postagem, aprendemos o que é um double fault e sob quais condições ele ocorre. Adicionamos um manipulador de double fault básico que imprime uma mensagem de erro e adicionamos um teste de integração para ele.
+
+Também habilitamos a troca de pilha suportada por hardware em exceções de double fault para que também funcione em stack overflow. Enquanto implementávamos isso, aprendemos sobre o segmento de estado de tarefa (TSS), a interrupt stack table (IST) contida nele, e a tabela de descritores globais (GDT), que foi usada para segmentação em arquiteturas mais antigas.
+
+## O Que Vem a Seguir?
+A próxima postagem explica como manipular interrupções de dispositivos externos como temporizadores, teclados ou controladores de rede. Essas interrupções de hardware são muito similares a exceções, por exemplo, elas também são despachadas através da IDT. No entanto, ao contrário de exceções, elas não surgem diretamente na CPU. Em vez disso, um _controlador de interrupção_ agrega essas interrupções e as encaminha para a CPU dependendo de sua prioridade. Na próxima postagem, exploraremos o controlador de interrupções [Intel 8259] \("PIC") e aprenderemos como implementar suporte a teclado.
+
+[Intel 8259]: https://en.wikipedia.org/wiki/Intel_8259
\ No newline at end of file
From f46d979c2b85619073cea8c29bbd414c998b849c Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Sun, 9 Nov 2025 23:21:03 -0300
Subject: [PATCH 08/26] [Translation][Portuguese pt-BR] post-7 (edition-2)
---
.../07-hardware-interrupts/index.pt-BR.md | 740 ++++++++++++++++++
1 file changed, 740 insertions(+)
create mode 100644 blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md
new file mode 100644
index 00000000..69377eb5
--- /dev/null
+++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md
@@ -0,0 +1,740 @@
++++
+title = "Interrupções de Hardware"
+weight = 7
+path = "pt-BR/hardware-interrupts"
+date = 2018-10-22
+
+[extra]
+chapter = "Interrupções"
+# Please update this when updating the translation
+translation_based_on_commit = "9753695744854686a6b80012c89b0d850a44b4b0"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Nesta postagem, configuramos o controlador de interrupção programável para encaminhar corretamente interrupções de hardware para a CPU. Para manipular essas interrupções, adicionamos novas entradas à nossa tabela de descritores de interrupção, assim como fizemos para nossos manipuladores de exceção. Aprenderemos como obter interrupções periódicas de timer e como obter entrada do teclado.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-07`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-07
+
+
+
+## Visão Geral
+
+Interrupções fornecem uma forma de notificar a CPU de dispositivos de hardware conectados. Então, em vez de deixar o kernel verificar periodicamente o teclado por novos caracteres (um processo chamado [_polling_]), o teclado pode notificar o kernel de cada pressionamento de tecla. Isso é muito mais eficiente porque o kernel só precisa agir quando algo aconteceu. Também permite tempos de reação mais rápidos, já que o kernel pode reagir imediatamente e não apenas na próxima verificação.
+
+[_polling_]: https://en.wikipedia.org/wiki/Polling_(computer_science)
+
+Conectar todos os dispositivos de hardware diretamente à CPU não é possível. Em vez disso, um _controlador de interrupção_ separado agrega as interrupções de todos os dispositivos e então notifica a CPU:
+
+```
+ ____________ _____
+ Timer ------------> | | | |
+ Teclado ----------> | Controlador|---------> | CPU |
+ Outro Hardware ---> | de | |_____|
+ Etc. -------------> | Interrupção|
+ |____________|
+
+```
+
+A maioria dos controladores de interrupção são programáveis, o que significa que suportam diferentes níveis de prioridade para interrupções. Por exemplo, isso permite dar às interrupções de timer uma prioridade mais alta que as interrupções de teclado para garantir cronometragem precisa.
+
+Ao contrário de exceções, interrupções de hardware ocorrem _assincronamente_. Isso significa que são completamente independentes do código executado e podem ocorrer a qualquer momento. Assim, temos repentinamente uma forma de concorrência em nosso kernel com todos os potenciais bugs relacionados à concorrência. O modelo estrito de ownership de Rust nos ajuda aqui porque proíbe estado global mutável. No entanto, deadlocks ainda são possíveis, como veremos mais tarde nesta postagem.
+
+## O 8259 PIC
+
+O [Intel 8259] é um controlador de interrupção programável (PIC) introduzido em 1976. Ele foi há muito tempo substituído pelo mais novo [APIC], mas sua interface ainda é suportada em sistemas atuais por razões de compatibilidade retroativa. O 8259 PIC é significativamente mais fácil de configurar que o APIC, então o usaremos para nos introduzir a interrupções antes de mudarmos para o APIC em uma postagem posterior.
+
+[APIC]: https://en.wikipedia.org/wiki/Intel_APIC_Architecture
+
+O 8259 tem oito linhas de interrupção e várias linhas para se comunicar com a CPU. Os sistemas típicos daquela época eram equipados com duas instâncias do 8259 PIC, um PIC primário e um secundário, conectado a uma das linhas de interrupção do primário:
+
+[Intel 8259]: https://en.wikipedia.org/wiki/Intel_8259
+
+```
+ ____________ ____________
+Real Time Clock --> | | Timer -------------> | |
+ACPI -------------> | | Teclado-----------> | | _____
+Disponível -------> | Controlador|----------------------> | Controlador| | |
+Disponível -------> | de | Porta Serial 2 ----> | de |---> | CPU |
+Mouse ------------> | Interrupção| Porta Serial 1 ----> | Interrupção| |_____|
+Co-Processador ---> | Secundário | Porta Paralela 2/3 > | Primário |
+ATA Primário -----> | | Disquete ---------> | |
+ATA Secundário ---> |____________| Porta Paralela 1---> |____________|
+
+```
+
+Este gráfico mostra a atribuição típica de linhas de interrupção. Vemos que a maioria das 15 linhas têm um mapeamento fixo, por exemplo, a linha 4 do PIC secundário é atribuída ao mouse.
+
+Cada controlador pode ser configurado através de duas [portas I/O], uma porta "comando" e uma porta "dados". Para o controlador primário, essas portas são `0x20` (comando) e `0x21` (dados). Para o controlador secundário, elas são `0xa0` (comando) e `0xa1` (dados). Para mais informações sobre como os PICs podem ser configurados, veja o [artigo em osdev.org].
+
+[portas I/O]: @/edition-2/posts/04-testing/index.md#i-o-ports
+[artigo em osdev.org]: https://wiki.osdev.org/8259_PIC
+
+### Implementação
+
+A configuração padrão dos PICs não é utilizável porque envia números de vetor de interrupção no intervalo de 0–15 para a CPU. Esses números já estão ocupados por exceções de CPU. Por exemplo, o número 8 corresponde a um double fault. Para corrigir esse problema de sobreposição, precisamos remapear as interrupções PIC para números diferentes. O intervalo real não importa desde que não se sobreponha às exceções, mas tipicamente o intervalo de 32–47 é escolhido, porque esses são os primeiros números livres após os 32 slots de exceção.
+
+A configuração acontece escrevendo valores especiais nas portas de comando e dados dos PICs. Felizmente, já existe uma crate chamada [`pic8259`], então não precisamos escrever a sequência de inicialização nós mesmos. No entanto, se você estiver interessado em como funciona, confira [seu código-fonte][pic crate source]. Ele é bastante pequeno e bem documentado.
+
+[pic crate source]: https://docs.rs/crate/pic8259/0.10.1/source/src/lib.rs
+
+Para adicionar a crate como dependência, adicionamos o seguinte ao nosso projeto:
+
+[`pic8259`]: https://docs.rs/pic8259/0.10.1/pic8259/
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+pic8259 = "0.10.1"
+```
+
+A principal abstração fornecida pela crate é a struct [`ChainedPics`] que representa o layout primário/secundário de PIC que vimos acima. Ela é projetada para ser usada da seguinte forma:
+
+[`ChainedPics`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html
+
+```rust
+// em src/interrupts.rs
+
+use pic8259::ChainedPics;
+use spin;
+
+pub const PIC_1_OFFSET: u8 = 32;
+pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
+
+pub static PICS: spin::Mutex =
+ spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
+```
+
+Como notado acima, estamos definindo os offsets para os PICs no intervalo 32–47. Ao envolver a struct `ChainedPics` em um `Mutex`, obtemos acesso mutável seguro (através do [método `lock`][spin mutex lock]), que precisamos no próximo passo. A função `ChainedPics::new` é unsafe porque offsets errados poderiam causar comportamento indefinido.
+
+[spin mutex lock]: https://docs.rs/spin/0.5.2/spin/struct.Mutex.html#method.lock
+
+Agora podemos inicializar o 8259 PIC em nossa função `init`:
+
+```rust
+// em src/lib.rs
+
+pub fn init() {
+ gdt::init();
+ interrupts::init_idt();
+ unsafe { interrupts::PICS.lock().initialize() }; // novo
+}
+```
+
+Usamos a função [`initialize`] para realizar a inicialização do PIC. Como a função `ChainedPics::new`, esta função também é unsafe porque pode causar comportamento indefinido se o PIC estiver mal configurado.
+
+[`initialize`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html#method.initialize
+
+Se tudo correr bem, devemos continuar a ver a mensagem "Não crashou!" ao executar `cargo run`.
+
+## Habilitando Interrupções
+
+Até agora, nada aconteceu porque as interrupções ainda estão desativadas na configuração da CPU. Isso significa que a CPU não escuta o controlador de interrupção de forma alguma, então nenhuma interrupção pode chegar à CPU. Vamos mudar isso:
+
+```rust
+// em src/lib.rs
+
+pub fn init() {
+ gdt::init();
+ interrupts::init_idt();
+ unsafe { interrupts::PICS.lock().initialize() };
+ x86_64::instructions::interrupts::enable(); // novo
+}
+```
+
+A função `interrupts::enable` da crate `x86_64` executa a instrução especial `sti` ("set interrupts") para habilitar interrupções externas. Quando tentamos `cargo run` agora, vemos que ocorre um double fault:
+
+
+
+A razão para este double fault é que o timer de hardware (o [Intel 8253], para ser exato) é habilitado por padrão, então começamos a receber interrupções de timer assim que habilitamos interrupções. Como ainda não definimos uma função manipuladora para ele, nosso manipulador de double fault é invocado.
+
+[Intel 8253]: https://en.wikipedia.org/wiki/Intel_8253
+
+## Manipulando Interrupções de Timer
+
+Como vemos do gráfico [acima](#o-8259-pic), o timer usa a linha 0 do PIC primário. Isso significa que ele chega à CPU como interrupção 32 (0 + offset 32). Em vez de codificar rigidamente o índice 32, o armazenamos em um enum `InterruptIndex`:
+
+```rust
+// em src/interrupts.rs
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u8)]
+pub enum InterruptIndex {
+ Timer = PIC_1_OFFSET,
+}
+
+impl InterruptIndex {
+ fn as_u8(self) -> u8 {
+ self as u8
+ }
+
+ fn as_usize(self) -> usize {
+ usize::from(self.as_u8())
+ }
+}
+```
+
+O enum é um [enum similar a C] para que possamos especificar diretamente o índice para cada variante. O atributo `repr(u8)` especifica que cada variante é representada como um `u8`. Adicionaremos mais variantes para outras interrupções no futuro.
+
+[enum similar a C]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
+
+Agora podemos adicionar uma função manipuladora para a interrupção de timer:
+
+```rust
+// em src/interrupts.rs
+
+use crate::print;
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ […]
+ idt[InterruptIndex::Timer.as_usize()]
+ .set_handler_fn(timer_interrupt_handler); // novo
+
+ idt
+ };
+}
+
+extern "x86-interrupt" fn timer_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ print!(".");
+}
+```
+
+Nosso `timer_interrupt_handler` tem a mesma assinatura que nossos manipuladores de exceção, porque a CPU reage identicamente a exceções e interrupções externas (a única diferença é que algumas exceções empurram um código de erro). A struct [`InterruptDescriptorTable`] implementa a trait [`IndexMut`], então podemos acessar entradas individuais através da sintaxe de indexação de array.
+
+[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
+[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html
+
+Em nosso manipulador de interrupção de timer, imprimimos um ponto na tela. Como a interrupção de timer acontece periodicamente, esperaríamos ver um ponto aparecendo a cada tick do timer. No entanto, quando o executamos, vemos que apenas um único ponto é impresso:
+
+
+
+### End of Interrupt
+
+A razão é que o PIC espera um sinal explícito de "end of interrupt" (EOI) do nosso manipulador de interrupção. Este sinal diz ao controlador que a interrupção foi processada e que o sistema está pronto para receber a próxima interrupção. Então o PIC pensa que ainda estamos ocupados processando a primeira interrupção de timer e espera pacientemente pelo sinal EOI antes de enviar a próxima.
+
+Para enviar o EOI, usamos nossa struct `PICS` estática novamente:
+
+```rust
+// em src/interrupts.rs
+
+extern "x86-interrupt" fn timer_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ print!(".");
+
+ unsafe {
+ PICS.lock()
+ .notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
+ }
+}
+```
+
+O `notify_end_of_interrupt` descobre se o PIC primário ou secundário enviou a interrupção e então usa as portas `command` e `data` para enviar um sinal EOI aos respectivos controladores. Se o PIC secundário enviou a interrupção, ambos os PICs precisam ser notificados porque o PIC secundário está conectado a uma linha de entrada do PIC primário.
+
+Precisamos ter cuidado para usar o número de vetor de interrupção correto, caso contrário poderíamos acidentalmente deletar uma importante interrupção não enviada ou fazer nosso sistema travar. Esta é a razão pela qual a função é unsafe.
+
+Quando agora executamos `cargo run` vemos pontos aparecendo periodicamente na tela:
+
+
+
+### Configurando o Timer
+
+O timer de hardware que usamos é chamado de _Programmable Interval Timer_, ou PIT, resumidamente. Como o nome diz, é possível configurar o intervalo entre duas interrupções. Não entraremos em detalhes aqui porque mudaremos em breve para o [APIC timer], mas a wiki do OSDev tem um artigo extenso sobre [configurando o PIT].
+
+[APIC timer]: https://wiki.osdev.org/APIC_timer
+[configurando o PIT]: https://wiki.osdev.org/Programmable_Interval_Timer
+
+## Deadlocks
+
+Agora temos uma forma de concorrência em nosso kernel: As interrupções de timer ocorrem assincronamente, então podem interromper nossa função `_start` a qualquer momento. Felizmente, o sistema de ownership de Rust previne muitos tipos de bugs relacionados à concorrência em tempo de compilação. Uma exceção notável são deadlocks. Deadlocks ocorrem se uma thread tenta adquirir um lock que nunca se tornará livre. Assim, a thread trava indefinidamente.
+
+Já podemos provocar um deadlock em nosso kernel. Lembre-se, nossa macro `println` chama a função `vga_buffer::_print`, que [trava um `WRITER` global][vga spinlock] usando um spinlock:
+
+[vga spinlock]: @/edition-2/posts/03-vga-text-buffer/index.md#spinlocks
+
+```rust
+// em src/vga_buffer.rs
+
+[…]
+
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+ use core::fmt::Write;
+ WRITER.lock().write_fmt(args).unwrap();
+}
+```
+
+Ela trava o `WRITER`, chama `write_fmt` nele, e implicitamente o destrava no final da função. Agora imagine que uma interrupção ocorre enquanto o `WRITER` está travado e o manipulador de interrupção tenta imprimir algo também:
+
+Passo de Tempo | _start | interrupt_handler
+---------|------|------------------
+0 | chama `println!` |
+1 | `print` trava `WRITER` |
+2 | | **interrupção ocorre**, manipulador começa a executar
+3 | | chama `println!` |
+4 | | `print` tenta travar `WRITER` (já travado)
+5 | | `print` tenta travar `WRITER` (já travado)
+… | | …
+_nunca_ | _destravar `WRITER`_ |
+
+O `WRITER` está travado, então o manipulador de interrupção espera até que se torne livre. Mas isso nunca acontece, porque a função `_start` só continua a executar após o manipulador de interrupção retornar. Assim, o sistema inteiro trava.
+
+### Provocando um Deadlock
+
+Podemos facilmente provocar tal deadlock em nosso kernel imprimindo algo no loop no final de nossa função `_start`:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ […]
+ loop {
+ use blog_os::print;
+ print!("-"); // novo
+ }
+}
+```
+
+Quando o executamos no QEMU, obtemos uma saída da forma:
+
+
+
+Vemos que apenas um número limitado de hífens são impressos até que a primeira interrupção de timer ocorre. Então o sistema trava porque o manipulador de interrupção de timer entra em deadlock quando tenta imprimir um ponto. Esta é a razão pela qual não vemos pontos na saída acima.
+
+O número real de hífens varia entre execuções porque a interrupção de timer ocorre assincronamente. Este não-determinismo é o que torna bugs relacionados à concorrência tão difíceis de depurar.
+
+### Corrigindo o Deadlock
+
+Para evitar este deadlock, podemos desativar interrupções enquanto o `Mutex` está travado:
+
+```rust
+// em src/vga_buffer.rs
+
+/// Imprime a string formatada dada no buffer de texto VGA
+/// através da instância global `WRITER`.
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+ use core::fmt::Write;
+ use x86_64::instructions::interrupts; // novo
+
+ interrupts::without_interrupts(|| { // novo
+ WRITER.lock().write_fmt(args).unwrap();
+ });
+}
+```
+
+A função [`without_interrupts`] recebe um [closure] e o executa em um ambiente livre de interrupções. Usamos isso para garantir que nenhuma interrupção pode ocorrer enquanto o `Mutex` está travado. Quando executamos nosso kernel agora, vemos que ele continua executando sem travar. (Ainda não notamos nenhum ponto, mas isso é porque eles estão rolando rápido demais. Tente diminuir a velocidade da impressão, por exemplo, colocando um `for _ in 0..10000 {}` dentro do loop.)
+
+[`without_interrupts`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/interrupts/fn.without_interrupts.html
+[closure]: https://doc.rust-lang.org/book/ch13-01-closures.html
+
+Podemos aplicar a mesma mudança à nossa função de impressão serial para garantir que nenhum deadlock ocorra com ela também:
+
+```rust
+// em src/serial.rs
+
+#[doc(hidden)]
+pub fn _print(args: ::core::fmt::Arguments) {
+ use core::fmt::Write;
+ use x86_64::instructions::interrupts; // novo
+
+ interrupts::without_interrupts(|| { // novo
+ SERIAL1
+ .lock()
+ .write_fmt(args)
+ .expect("Impressão para serial falhou");
+ });
+}
+```
+
+Note que desativar interrupções não deve ser uma solução geral. O problema é que isso aumenta a latência de interrupção no pior caso, isto é, o tempo até o sistema reagir a uma interrupção. Portanto, interrupções devem ser desativadas apenas por um tempo muito curto.
+
+## Corrigindo uma Race Condition
+
+Se você executar `cargo test`, pode ver o teste `test_println_output` falhar:
+
+```
+> cargo test --lib
+[…]
+Running 4 tests
+test_breakpoint_exception...[ok]
+test_println... [ok]
+test_println_many... [ok]
+test_println_output... [failed]
+
+Error: panicked at 'assertion failed: `(left == right)`
+ left: `'.'`,
+ right: `'S'`', src/vga_buffer.rs:205:9
+```
+
+A razão é uma _race condition_ entre o teste e nosso manipulador de timer. Lembre-se, o teste se parece com isto:
+
+```rust
+// em src/vga_buffer.rs
+
+#[test_case]
+fn test_println_output() {
+ let s = "Uma string de teste que cabe em uma única linha";
+ println!("{}", s);
+ for (i, c) in s.chars().enumerate() {
+ let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
+ assert_eq!(char::from(screen_char.ascii_character), c);
+ }
+}
+```
+
+O teste imprime uma string no buffer VGA e então verifica a saída iterando manualmente pelo array `buffer_chars`. A race condition ocorre porque o manipulador de interrupção de timer pode executar entre o `println` e a leitura dos caracteres de tela. Note que isso não é uma _data race_ perigosa, que Rust previne completamente em tempo de compilação. Veja o [_Rustonomicon_][nomicon-races] para detalhes.
+
+[nomicon-races]: https://doc.rust-lang.org/nomicon/races.html
+
+Para corrigir isso, precisamos manter o `WRITER` travado pela duração completa do teste, para que o manipulador de timer não possa escrever um `.` na tela no meio. O teste corrigido se parece com isto:
+
+```rust
+// em src/vga_buffer.rs
+
+#[test_case]
+fn test_println_output() {
+ use core::fmt::Write;
+ use x86_64::instructions::interrupts;
+
+ let s = "Uma string de teste que cabe em uma única linha";
+ interrupts::without_interrupts(|| {
+ let mut writer = WRITER.lock();
+ writeln!(writer, "\n{}", s).expect("writeln falhou");
+ for (i, c) in s.chars().enumerate() {
+ let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
+ assert_eq!(char::from(screen_char.ascii_character), c);
+ }
+ });
+}
+```
+
+Realizamos as seguintes mudanças:
+
+- Mantemos o writer travado pelo teste completo usando o método `lock()` explicitamente. Em vez de `println`, usamos a macro [`writeln`] que permite imprimir em um writer já travado.
+- Para evitar outro deadlock, desativamos interrupções pela duração do teste. Caso contrário, o teste poderia ser interrompido enquanto o writer ainda está travado.
+- Como o manipulador de interrupção de timer ainda pode executar antes do teste, imprimimos uma nova linha adicional `\n` antes de imprimir a string `s`. Desta forma, evitamos falha do teste quando o manipulador de timer já imprimiu alguns caracteres `.` na linha atual.
+
+[`writeln`]: https://doc.rust-lang.org/core/macro.writeln.html
+
+Com as mudanças acima, `cargo test` agora tem sucesso deterministicamente novamente.
+
+Esta foi uma race condition muito inofensiva que causou apenas uma falha de teste. Como você pode imaginar, outras race conditions podem ser muito mais difíceis de depurar devido à sua natureza não-determinística. Felizmente, Rust nos previne de data races, que são a classe mais séria de race conditions, já que podem causar todo tipo de comportamento indefinido, incluindo crashes de sistema e corrupções silenciosas de memória.
+
+## A Instrução `hlt`
+
+Até agora, usamos uma simples instrução de loop vazio no final de nossas funções `_start` e `panic`. Isso faz a CPU girar infinitamente, e assim funciona como esperado. Mas também é muito ineficiente, porque a CPU continua executando a velocidade máxima mesmo que não haja trabalho a fazer. Você pode ver este problema em seu gerenciador de tarefas quando executa seu kernel: O processo QEMU precisa de perto de 100% de CPU o tempo todo.
+
+O que realmente queremos fazer é parar a CPU até a próxima interrupção chegar. Isso permite que a CPU entre em um estado de sono no qual consome muito menos energia. A [instrução `hlt`] faz exatamente isso. Vamos usar esta instrução para criar um loop infinito eficiente em energia:
+
+[instrução `hlt`]: https://en.wikipedia.org/wiki/HLT_(x86_instruction)
+
+```rust
+// em src/lib.rs
+
+pub fn hlt_loop() -> ! {
+ loop {
+ x86_64::instructions::hlt();
+ }
+}
+```
+
+A função `instructions::hlt` é apenas um [wrapper fino] em torno da instrução assembly. Ela é segura porque não há forma de comprometer a segurança de memória.
+
+[wrapper fino]: https://github.com/rust-osdev/x86_64/blob/5e8e218381c5205f5777cb50da3ecac5d7e3b1ab/src/instructions/mod.rs#L16-L22
+
+Agora podemos usar este `hlt_loop` em vez dos loops infinitos em nossas funções `_start` e `panic`:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ […]
+
+ println!("Não crashou!");
+ blog_os::hlt_loop(); // novo
+}
+
+
+#[cfg(not(test))]
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ println!("{}", info);
+ blog_os::hlt_loop(); // novo
+}
+
+```
+
+Vamos atualizar nosso `lib.rs` também:
+
+```rust
+// em src/lib.rs
+
+/// Ponto de entrada para `cargo test`
+#[cfg(test)]
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ init();
+ test_main();
+ hlt_loop(); // novo
+}
+
+pub fn test_panic_handler(info: &PanicInfo) -> ! {
+ serial_println!("[failed]\n");
+ serial_println!("Error: {}\n", info);
+ exit_qemu(QemuExitCode::Failed);
+ hlt_loop(); // novo
+}
+```
+
+Quando executamos nosso kernel agora no QEMU, vemos um uso de CPU muito menor.
+
+## Entrada de Teclado
+
+Agora que somos capazes de manipular interrupções de dispositivos externos, finalmente podemos adicionar suporte para entrada de teclado. Isso nos permitirá interagir com nosso kernel pela primeira vez.
+
+
+
+[PS/2]: https://en.wikipedia.org/wiki/PS/2_port
+
+Como o timer de hardware, o controlador de teclado já está habilitado por padrão. Então quando você pressiona uma tecla, o controlador de teclado envia uma interrupção para o PIC, que a encaminha para a CPU. A CPU procura por uma função manipuladora na IDT, mas a entrada correspondente está vazia. Portanto, ocorre um double fault.
+
+Então vamos adicionar uma função manipuladora para a interrupção de teclado. É bem similar a como definimos o manipulador para a interrupção de timer; apenas usa um número de interrupção diferente:
+
+```rust
+// em src/interrupts.rs
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u8)]
+pub enum InterruptIndex {
+ Timer = PIC_1_OFFSET,
+ Keyboard, // novo
+}
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+ idt.breakpoint.set_handler_fn(breakpoint_handler);
+ […]
+ // novo
+ idt[InterruptIndex::Keyboard.as_usize()]
+ .set_handler_fn(keyboard_interrupt_handler);
+
+ idt
+ };
+}
+
+extern "x86-interrupt" fn keyboard_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ print!("k");
+
+ unsafe {
+ PICS.lock()
+ .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
+ }
+}
+```
+
+Como vemos do gráfico [acima](#o-8259-pic), o teclado usa a linha 1 do PIC primário. Isso significa que ele chega à CPU como interrupção 33 (1 + offset 32). Adicionamos este índice como uma nova variante `Keyboard` ao enum `InterruptIndex`. Não precisamos especificar o valor explicitamente, já que ele assume o valor anterior mais um por padrão, que também é 33. No manipulador de interrupção, imprimimos um `k` e enviamos o sinal end of interrupt para o controlador de interrupção.
+
+Agora vemos que um `k` aparece na tela quando pressionamos uma tecla. No entanto, isso só funciona para a primeira tecla que pressionamos. Mesmo se continuarmos a pressionar teclas, nenhum `k` adicional aparece na tela. Isso ocorre porque o controlador de teclado não enviará outra interrupção até lermos o chamado _scancode_ da tecla pressionada.
+
+### Lendo os Scancodes
+
+Para descobrir _qual_ tecla foi pressionada, precisamos consultar o controlador de teclado. Fazemos isso lendo da porta de dados do controlador PS/2, que é a [porta I/O] com o número `0x60`:
+
+[porta I/O]: @/edition-2/posts/04-testing/index.md#i-o-ports
+
+```rust
+// em src/interrupts.rs
+
+extern "x86-interrupt" fn keyboard_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ use x86_64::instructions::port::Port;
+
+ let mut port = Port::new(0x60);
+ let scancode: u8 = unsafe { port.read() };
+ print!("{}", scancode);
+
+ unsafe {
+ PICS.lock()
+ .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
+ }
+}
+```
+
+Usamos o tipo [`Port`] da crate `x86_64` para ler um byte da porta de dados do teclado. Este byte é chamado de [_scancode_] e representa o pressionamento/liberação de tecla. Ainda não fazemos nada com o scancode, apenas o imprimimos na tela:
+
+[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
+[_scancode_]: https://en.wikipedia.org/wiki/Scancode
+
+
+
+A imagem acima me mostra digitando lentamente "123". Vemos que teclas adjacentes têm scancodes adjacentes e que pressionar uma tecla causa um scancode diferente de liberá-la. Mas como traduzimos exatamente os scancodes para as ações reais de tecla?
+
+### Interpretando os Scancodes
+Existem três padrões diferentes para o mapeamento entre scancodes e teclas, os chamados _conjuntos de scancode_. Todos os três remontam aos teclados de computadores IBM antigos: o [IBM XT], o [IBM 3270 PC], e o [IBM AT]. Felizmente, computadores posteriores não continuaram a tendência de definir novos conjuntos de scancode, mas em vez disso emularam os conjuntos existentes e os estenderam. Hoje, a maioria dos teclados pode ser configurada para emular qualquer um dos três conjuntos.
+
+[IBM XT]: https://en.wikipedia.org/wiki/IBM_Personal_Computer_XT
+[IBM 3270 PC]: https://en.wikipedia.org/wiki/IBM_3270_PC
+[IBM AT]: https://en.wikipedia.org/wiki/IBM_Personal_Computer/AT
+
+Por padrão, teclados PS/2 emulam o conjunto de scancode 1 ("XT"). Neste conjunto, os 7 bits inferiores de um byte de scancode definem a tecla, e o bit mais significativo define se é um pressionamento ("0") ou uma liberação ("1"). Teclas que não estavam presentes no [IBM XT] original, como a tecla enter no teclado numérico, geram dois scancodes em sucessão: um byte de escape `0xe0` e então um byte representando a tecla. Para uma lista de todos os scancodes do conjunto 1 e suas teclas correspondentes, confira a [Wiki OSDev][scancode set 1].
+
+[scancode set 1]: https://wiki.osdev.org/Keyboard#Scan_Code_Set_1
+
+Para traduzir os scancodes para teclas, podemos usar uma instrução `match`:
+
+```rust
+// em src/interrupts.rs
+
+extern "x86-interrupt" fn keyboard_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ use x86_64::instructions::port::Port;
+
+ let mut port = Port::new(0x60);
+ let scancode: u8 = unsafe { port.read() };
+
+ // novo
+ let key = match scancode {
+ 0x02 => Some('1'),
+ 0x03 => Some('2'),
+ 0x04 => Some('3'),
+ 0x05 => Some('4'),
+ 0x06 => Some('5'),
+ 0x07 => Some('6'),
+ 0x08 => Some('7'),
+ 0x09 => Some('8'),
+ 0x0a => Some('9'),
+ 0x0b => Some('0'),
+ _ => None,
+ };
+ if let Some(key) = key {
+ print!("{}", key);
+ }
+
+ unsafe {
+ PICS.lock()
+ .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
+ }
+}
+```
+
+O código acima traduz pressionamentos das teclas numéricas 0-9 e ignora todas as outras teclas. Ele usa uma instrução [match] para atribuir um caractere ou `None` a cada scancode. Então usa [`if let`] para desestruturar o `key` opcional. Ao usar o mesmo nome de variável `key` no padrão, [sombreamos] a declaração anterior, que é um padrão comum para desestruturar tipos `Option` em Rust.
+
+[match]: https://doc.rust-lang.org/book/ch06-02-match.html
+[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions
+[sombreamos]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing
+
+Agora podemos escrever números:
+
+
+
+Traduzir as outras teclas funciona da mesma forma. Felizmente, existe uma crate chamada [`pc-keyboard`] para traduzir scancodes dos conjuntos de scancode 1 e 2, então não precisamos implementar isso nós mesmos. Para usar a crate, a adicionamos ao nosso `Cargo.toml` e a importamos em nosso `lib.rs`:
+
+[`pc-keyboard`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/
+
+```toml
+# em Cargo.toml
+
+[dependencies]
+pc-keyboard = "0.7.0"
+```
+
+Agora podemos usar esta crate para reescrever nosso `keyboard_interrupt_handler`:
+
+```rust
+// em/src/interrupts.rs
+
+extern "x86-interrupt" fn keyboard_interrupt_handler(
+ _stack_frame: InterruptStackFrame)
+{
+ use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
+ use spin::Mutex;
+ use x86_64::instructions::port::Port;
+
+ lazy_static! {
+ static ref KEYBOARD: Mutex> =
+ Mutex::new(Keyboard::new(ScancodeSet1::new(),
+ layouts::Us104Key, HandleControl::Ignore)
+ );
+ }
+
+ let mut keyboard = KEYBOARD.lock();
+ let mut port = Port::new(0x60);
+
+ let scancode: u8 = unsafe { port.read() };
+ if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
+ if let Some(key) = keyboard.process_keyevent(key_event) {
+ match key {
+ DecodedKey::Unicode(character) => print!("{}", character),
+ DecodedKey::RawKey(key) => print!("{:?}", key),
+ }
+ }
+ }
+
+ unsafe {
+ PICS.lock()
+ .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
+ }
+}
+```
+
+Usamos a macro `lazy_static` para criar um objeto [`Keyboard`] estático protegido por um Mutex. Inicializamos o `Keyboard` com um layout de teclado americano e o conjunto de scancode 1. O parâmetro [`HandleControl`] permite mapear `ctrl+[a-z]` aos caracteres Unicode `U+0001` através de `U+001A`. Não queremos fazer isso, então usamos a opção `Ignore` para manipular o `ctrl` como teclas normais.
+
+[`HandleControl`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/enum.HandleControl.html
+
+Em cada interrupção, travamos o Mutex, lemos o scancode do controlador de teclado, e o passamos para o método [`add_byte`], que traduz o scancode em um `Option`. O [`KeyEvent`] contém a tecla que causou o evento e se foi um evento de pressionamento ou liberação.
+
+[`Keyboard`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/struct.Keyboard.html
+[`add_byte`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/struct.Keyboard.html#method.add_byte
+[`KeyEvent`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/struct.KeyEvent.html
+
+Para interpretar este evento de tecla, o passamos para o método [`process_keyevent`], que traduz o evento de tecla em um caractere, se possível. Por exemplo, ele traduz um evento de pressionamento da tecla `A` em um caractere `a` minúsculo ou um caractere `A` maiúsculo, dependendo se a tecla shift foi pressionada.
+
+[`process_keyevent`]: https://docs.rs/pc-keyboard/0.7.0/pc_keyboard/struct.Keyboard.html#method.process_keyevent
+
+Com este manipulador de interrupção modificado, agora podemos escrever texto:
+
+
+
+### Configurando o Teclado
+
+É possível configurar alguns aspectos de um teclado PS/2, por exemplo, qual conjunto de scancode ele deve usar. Não cobriremos isso aqui porque esta postagem já está longa o suficiente, mas a Wiki do OSDev tem uma visão geral dos possíveis [comandos de configuração].
+
+[comandos de configuração]: https://wiki.osdev.org/PS/2_Keyboard#Commands
+
+## Resumo
+
+Esta postagem explicou como habilitar e manipular interrupções externas. Aprendemos sobre o 8259 PIC e seu layout primário/secundário, o remapeamento dos números de interrupção, e o sinal "end of interrupt". Implementamos manipuladores para o timer de hardware e o teclado e aprendemos sobre a instrução `hlt`, que para a CPU até a próxima interrupção.
+
+Agora somos capazes de interagir com nosso kernel e temos alguns blocos fundamentais para criar um pequeno shell ou jogos simples.
+
+## O Que Vem a Seguir?
+
+Interrupções de timer são essenciais para um sistema operacional porque fornecem uma forma de interromper periodicamente o processo em execução e deixar o kernel retomar o controle. O kernel pode então mudar para um processo diferente e criar a ilusão de múltiplos processos executando em paralelo.
+
+Mas antes de podermos criar processos ou threads, precisamos de uma forma de alocar memória para eles. As próximas postagens explorarão gerenciamento de memória para fornecer este bloco fundamental.
\ No newline at end of file
From 13ba1c6c2828f8a66419b2bac038a205ebdfac33 Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Mon, 10 Nov 2025 08:11:20 -0300
Subject: [PATCH 09/26] [Translation][Portuguese pt-BR] post-8 (edition-2)
---
.../08-paging-introduction/index.pt-BR.md | 419 ++++++++++++++++++
1 file changed, 419 insertions(+)
create mode 100644 blog/content/edition-2/posts/08-paging-introduction/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/08-paging-introduction/index.pt-BR.md b/blog/content/edition-2/posts/08-paging-introduction/index.pt-BR.md
new file mode 100644
index 00000000..7253681a
--- /dev/null
+++ b/blog/content/edition-2/posts/08-paging-introduction/index.pt-BR.md
@@ -0,0 +1,419 @@
++++
+title = "Introdução à Paginação"
+weight = 8
+path = "pt-BR/paging-introduction"
+date = 2019-01-14
+
+[extra]
+chapter = "Gerenciamento de Memória"
+# Please update this when updating the translation
+translation_based_on_commit = "9753695744854686a6b80012c89b0d850a44b4b0"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Esta postagem introduz _paginação_, um esquema de gerenciamento de memória muito comum que também usaremos para nosso sistema operacional. Ela explica por que o isolamento de memória é necessário, como _segmentação_ funciona, o que é _memória virtual_, e como paginação resolve problemas de fragmentação de memória. Também explora o layout de tabelas de página multinível na arquitetura x86_64.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-08`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-08
+
+
+
+## Proteção de Memória
+
+Uma tarefa principal de um sistema operacional é isolar programas uns dos outros. Seu navegador web não deveria ser capaz de interferir com seu editor de texto, por exemplo. Para alcançar este objetivo, sistemas operacionais utilizam funcionalidade de hardware para garantir que áreas de memória de um processo não sejam acessíveis por outros processos. Existem diferentes abordagens dependendo do hardware e da implementação do SO.
+
+Como exemplo, alguns processadores ARM Cortex-M (usados para sistemas embarcados) têm uma [_Memory Protection Unit_] (MPU), que permite definir um pequeno número (por exemplo, 8) de regiões de memória com diferentes permissões de acesso (por exemplo, sem acesso, somente leitura, leitura-escrita). Em cada acesso à memória, a MPU garante que o endereço está em uma região com permissões de acesso corretas e lança uma exceção caso contrário. Ao mudar as regiões e permissões de acesso em cada troca de processo, o sistema operacional pode garantir que cada processo acesse apenas sua própria memória e assim isole processos uns dos outros.
+
+[_Memory Protection Unit_]: https://developer.arm.com/docs/ddi0337/e/memory-protection-unit/about-the-mpu
+
+No x86, o hardware suporta duas abordagens diferentes para proteção de memória: [segmentação] e [paginação].
+
+[segmentação]: https://en.wikipedia.org/wiki/X86_memory_segmentation
+[paginação]: https://en.wikipedia.org/wiki/Virtual_memory#Paged_virtual_memory
+
+## Segmentação
+
+Segmentação já foi introduzida em 1978, originalmente para aumentar a quantidade de memória endereçável. A situação naquela época era que CPUs usavam apenas endereços de 16 bits, o que limitava a quantidade de memória endereçável a 64 KiB. Para tornar mais que esses 64 KiB acessíveis, registradores de segmento adicionais foram introduzidos, cada um contendo um endereço de deslocamento. A CPU automaticamente adicionava este deslocamento em cada acesso à memória, então até 1 MiB de memória era acessível.
+
+O registrador de segmento é escolhido automaticamente pela CPU dependendo do tipo de acesso à memória: Para buscar instruções, o segmento de código `CS` é usado, e para operações de pilha (push/pop), o segmento de pilha `SS` é usado. Outras instruções usam o segmento de dados `DS` ou o segmento extra `ES`. Posteriormente, dois registradores de segmento adicionais, `FS` e `GS`, foram adicionados, que podem ser usados livremente.
+
+Na primeira versão de segmentação, os registradores de segmento continham diretamente o deslocamento e nenhum controle de acesso era realizado. Isso mudou posteriormente com a introdução do [_modo protegido_]. Quando a CPU executa neste modo, os descritores de segmento contêm um índice em uma [_tabela de descritores_] local ou global, que contém – além de um endereço de deslocamento – o tamanho do segmento e permissões de acesso. Ao carregar tabelas de descritores globais/locais separadas para cada processo, que confinam acessos à memória às próprias áreas de memória do processo, o SO pode isolar processos uns dos outros.
+
+[_modo protegido_]: https://en.wikipedia.org/wiki/X86_memory_segmentation#Protected_mode
+[_tabela de descritores_]: https://en.wikipedia.org/wiki/Global_Descriptor_Table
+
+Ao modificar os endereços de memória antes do acesso real, segmentação já empregava uma técnica que agora é usada em quase todo lugar: _memória virtual_.
+
+### Memória Virtual
+
+A ideia por trás da memória virtual é abstrair os endereços de memória do dispositivo de armazenamento físico subjacente. Em vez de acessar diretamente o dispositivo de armazenamento, um passo de tradução é realizado primeiro. Para segmentação, o passo de tradução é adicionar o endereço de deslocamento do segmento ativo. Imagine um programa acessando o endereço de memória `0x1234000` em um segmento com deslocamento de `0x1111000`: O endereço que é realmente acessado é `0x2345000`.
+
+Para diferenciar os dois tipos de endereço, endereços antes da tradução são chamados _virtuais_, e endereços após a tradução são chamados _físicos_. Uma diferença importante entre esses dois tipos de endereços é que endereços físicos são únicos e sempre se referem à mesma localização de memória distinta. Endereços virtuais, por outro lado, dependem da função de tradução. É inteiramente possível que dois endereços virtuais diferentes se refiram ao mesmo endereço físico. Além disso, endereços virtuais idênticos podem se referir a endereços físicos diferentes quando usam funções de tradução diferentes.
+
+Um exemplo onde esta propriedade é útil é executar o mesmo programa duas vezes em paralelo:
+
+
+
+Aqui o mesmo programa executa duas vezes, mas com funções de tradução diferentes. A primeira instância tem um deslocamento de segmento de 100, então seus endereços virtuais 0–150 são traduzidos para os endereços físicos 100–250. A segunda instância tem um deslocamento de 300, que traduz seus endereços virtuais 0–150 para endereços físicos 300–450. Isso permite que ambos os programas executem o mesmo código e usem os mesmos endereços virtuais sem interferir uns com os outros.
+
+Outra vantagem é que programas agora podem ser colocados em localizações arbitrárias de memória física, mesmo se usarem endereços virtuais completamente diferentes. Assim, o SO pode utilizar a quantidade total de memória disponível sem precisar recompilar programas.
+
+### Fragmentação
+
+A diferenciação entre endereços virtuais e físicos torna a segmentação realmente poderosa. No entanto, ela tem o problema de fragmentação. Como exemplo, imagine que queremos executar uma terceira cópia do programa que vimos acima:
+
+
+
+Não há forma de mapear a terceira instância do programa para memória virtual sem sobreposição, mesmo que haja mais que memória livre suficiente disponível. O problema é que precisamos de memória _contínua_ e não podemos usar os pequenos pedaços livres.
+
+Uma forma de combater esta fragmentação é pausar a execução, mover as partes usadas da memória mais próximas, atualizar a tradução, e então retomar a execução:
+
+
+
+Agora há espaço contínuo suficiente para iniciar a terceira instância do nosso programa.
+
+A desvantagem deste processo de desfragmentação é que ele precisa copiar grandes quantidades de memória, o que diminui o desempenho. Também precisa ser feito regularmente antes que a memória se torne muito fragmentada. Isso torna o desempenho imprevisível já que programas são pausados em momentos aleatórios e podem se tornar não responsivos.
+
+O problema de fragmentação é uma das razões pelas quais segmentação não é mais usada pela maioria dos sistemas. Na verdade, segmentação nem é mais suportada no modo de 64 bits no x86. Em vez disso, _paginação_ é usada, que evita completamente o problema de fragmentação.
+
+## Paginação
+
+A ideia é dividir tanto o espaço de memória virtual quanto o físico em pequenos blocos de tamanho fixo. Os blocos do espaço de memória virtual são chamados _páginas_, e os blocos do espaço de endereço físico são chamados _frames_. Cada página pode ser individualmente mapeada para um frame, o que torna possível dividir regiões de memória maiores em frames físicos não contínuos.
+
+A vantagem disso se torna visível se recapitularmos o exemplo do espaço de memória fragmentado, mas usando paginação em vez de segmentação desta vez:
+
+
+
+Neste exemplo, temos um tamanho de página de 50 bytes, o que significa que cada uma de nossas regiões de memória é dividida em três páginas. Cada página é mapeada para um frame individualmente, então uma região de memória virtual contínua pode ser mapeada para frames físicos não contínuos. Isso nos permite iniciar a terceira instância do programa sem realizar nenhuma desfragmentação antes.
+
+### Fragmentação Escondida
+
+Comparado à segmentação, paginação usa muitas regiões de memória pequenas de tamanho fixo em vez de algumas regiões grandes de tamanho variável. Como cada frame tem o mesmo tamanho, não há frames que são muito pequenos para serem usados, então nenhuma fragmentação ocorre.
+
+Ou _parece_ que nenhuma fragmentação ocorre. Ainda há algum tipo escondido de fragmentação, a chamada _fragmentação interna_. Fragmentação interna ocorre porque nem toda região de memória é um múltiplo exato do tamanho da página. Imagine um programa de tamanho 101 no exemplo acima: Ele ainda precisaria de três páginas de tamanho 50, então ocuparia 49 bytes a mais que o necessário. Para diferenciar os dois tipos de fragmentação, o tipo de fragmentação que acontece ao usar segmentação é chamado _fragmentação externa_.
+
+Fragmentação interna é infeliz mas frequentemente melhor que a fragmentação externa que ocorre com segmentação. Ela ainda desperdiça memória, mas não requer desfragmentação e torna a quantidade de fragmentação previsível (em média metade de uma página por região de memória).
+
+### Tabelas de Página
+
+Vimos que cada uma das potencialmente milhões de páginas é individualmente mapeada para um frame. Esta informação de mapeamento precisa ser armazenada em algum lugar. Segmentação usa um registrador seletor de segmento individual para cada região de memória ativa, o que não é possível para paginação já que há muito mais páginas que registradores. Em vez disso, paginação usa uma estrutura de tabela chamada _tabela de página_ para armazenar a informação de mapeamento.
+
+Para nosso exemplo acima, as tabelas de página pareceriam com isto:
+
+
+
+Vemos que cada instância de programa tem sua própria tabela de página. Um ponteiro para a tabela atualmente ativa é armazenado em um registrador especial da CPU. No `x86`, este registrador é chamado `CR3`. É trabalho do sistema operacional carregar este registrador com o ponteiro para a tabela de página correta antes de executar cada instância de programa.
+
+Em cada acesso à memória, a CPU lê o ponteiro da tabela do registrador e procura o frame mapeado para a página acessada na tabela. Isso é inteiramente feito em hardware e completamente invisível para o programa em execução. Para acelerar o processo de tradução, muitas arquiteturas de CPU têm um cache especial que lembra os resultados das últimas traduções.
+
+Dependendo da arquitetura, entradas da tabela de página também podem armazenar atributos como permissões de acesso em um campo de flags. No exemplo acima, a flag "r/w" torna a página tanto legível quanto gravável.
+
+### Tabelas de Página Multinível
+
+As tabelas de página simples que acabamos de ver têm um problema em espaços de endereço maiores: elas desperdiçam memória. Por exemplo, imagine um programa que usa as quatro páginas virtuais `0`, `1_000_000`, `1_000_050`, e `1_000_100` (usamos `_` como separador de milhares):
+
+
+
+Ele precisa apenas de 4 frames físicos, mas a tabela de página tem mais de um milhão de entradas. Não podemos omitir as entradas vazias porque então a CPU não seria mais capaz de pular diretamente para a entrada correta no processo de tradução (por exemplo, não é mais garantido que a quarta página use a quarta entrada).
+
+Para reduzir a memória desperdiçada, podemos usar uma **tabela de página de dois níveis**. A ideia é que usamos tabelas de página diferentes para regiões de endereço diferentes. Uma tabela adicional chamada tabela de página de _nível 2_ contém o mapeamento entre regiões de endereço e tabelas de página (nível 1).
+
+Isso é melhor explicado por um exemplo. Vamos definir que cada tabela de página de nível 1 é responsável por uma região de tamanho `10_000`. Então as seguintes tabelas existiriam para o exemplo de mapeamento acima:
+
+
+
+A página 0 cai na primeira região de `10_000` bytes, então usa a primeira entrada da tabela de página de nível 2. Esta entrada aponta para a tabela de página de nível 1 T1, que especifica que a página `0` aponta para o frame `0`.
+
+As páginas `1_000_000`, `1_000_050`, e `1_000_100` todas caem na 100ª região de `10_000` bytes, então usam a 100ª entrada da tabela de página de nível 2. Esta entrada aponta para uma tabela de página de nível 1 diferente T2, que mapeia as três páginas para frames `100`, `150`, e `200`. Note que o endereço da página em tabelas de nível 1 não inclui o deslocamento da região. Por exemplo, a entrada para a página `1_000_050` é apenas `50`.
+
+Ainda temos 100 entradas vazias na tabela de nível 2, mas muito menos que o milhão de entradas vazias antes. A razão para essas economias é que não precisamos criar tabelas de página de nível 1 para as regiões de memória não mapeadas entre `10_000` e `1_000_000`.
+
+O princípio de tabelas de página de dois níveis pode ser estendido para três, quatro, ou mais níveis. Então o registrador de tabela de página aponta para a tabela de nível mais alto, que aponta para a tabela de próximo nível mais baixo, que aponta para o próximo nível mais baixo, e assim por diante. A tabela de página de nível 1 então aponta para o frame mapeado. O princípio em geral é chamado de tabela de página _multinível_ ou _hierárquica_.
+
+Agora que sabemos como paginação e tabelas de página multinível funcionam, podemos olhar como paginação é implementada na arquitetura x86_64 (assumimos no seguinte que a CPU executa no modo de 64 bits).
+
+## Paginação no x86_64
+
+A arquitetura x86_64 usa uma tabela de página de 4 níveis e um tamanho de página de 4 KiB. Cada tabela de página, independente do nível, tem um tamanho fixo de 512 entradas. Cada entrada tem um tamanho de 8 bytes, então cada tabela tem 512 * 8 B = 4 KiB de tamanho e assim cabe exatamente em uma página.
+
+O índice da tabela de página para cada nível é derivado diretamente do endereço virtual:
+
+
+
+Vemos que cada índice de tabela consiste de 9 bits, o que faz sentido porque cada tabela tem 2^9 = 512 entradas. Os 12 bits mais baixos são o deslocamento na página de 4 KiB (2^12 bytes = 4 KiB). Os bits 48 a 64 são descartados, o que significa que x86_64 não é realmente 64 bits já que suporta apenas endereços de 48 bits.
+
+Mesmo que os bits 48 a 64 sejam descartados, eles não podem ser definidos para valores arbitrários. Em vez disso, todos os bits nesta faixa devem ser cópias do bit 47 para manter endereços únicos e permitir extensões futuras como a tabela de página de 5 níveis. Isso é chamado _extensão de sinal_ porque é muito similar à [extensão de sinal em complemento de dois]. Quando um endereço não é corretamente estendido com sinal, a CPU lança uma exceção.
+
+[extensão de sinal em complemento de dois]: https://en.wikipedia.org/wiki/Two's_complement#Sign_extension
+
+Vale notar que as CPUs Intel "Ice Lake" recentes opcionalmente suportam [tabelas de página de 5 níveis] para estender endereços virtuais de 48 bits para 57 bits. Dado que otimizar nosso kernel para uma CPU específica não faz sentido neste estágio, trabalharemos apenas com tabelas de página padrão de 4 níveis nesta postagem.
+
+[tabelas de página de 5 níveis]: https://en.wikipedia.org/wiki/Intel_5-level_paging
+
+### Exemplo de Tradução
+
+Vamos passar por um exemplo para entender como o processo de tradução funciona em detalhes:
+
+
+
+O endereço físico da tabela de página de nível 4 atualmente ativa, que é a raiz da tabela de página de 4 níveis, é armazenado no registrador `CR3`. Cada entrada da tabela de página então aponta para o frame físico da tabela de próximo nível. A entrada da tabela de nível 1 então aponta para o frame mapeado. Note que todos os endereços nas tabelas de página são físicos em vez de virtuais, porque caso contrário a CPU precisaria traduzi-los também (o que poderia causar uma recursão sem fim).
+
+A hierarquia de tabela de página acima mapeia duas páginas (em azul). Dos índices da tabela de página, podemos deduzir que os endereços virtuais dessas duas páginas são `0x803FE7F000` e `0x803FE00000`. Vamos ver o que acontece quando o programa tenta ler do endereço `0x803FE7F5CE`. Primeiro, convertemos o endereço para binário e determinamos os índices da tabela de página e o deslocamento de página para o endereço:
+
+
+
+Com esses índices, agora podemos percorrer a hierarquia da tabela de página para determinar o frame mapeado para o endereço:
+
+- Começamos lendo o endereço da tabela de nível 4 do registrador `CR3`.
+- O índice de nível 4 é 1, então olhamos para a entrada com índice 1 daquela tabela, que nos diz que a tabela de nível 3 está armazenada no endereço 16 KiB.
+- Carregamos a tabela de nível 3 daquele endereço e olhamos para a entrada com índice 0, que nos aponta para a tabela de nível 2 em 24 KiB.
+- O índice de nível 2 é 511, então olhamos para a última entrada daquela página para descobrir o endereço da tabela de nível 1.
+- Através da entrada com índice 127 da tabela de nível 1, finalmente descobrimos que a página está mapeada para o frame 12 KiB, ou 0x3000 em hexadecimal.
+- O passo final é adicionar o deslocamento de página ao endereço do frame para obter o endereço físico 0x3000 + 0x5ce = 0x35ce.
+
+
+
+As permissões para a página na tabela de nível 1 são `r`, o que significa somente leitura. O hardware reforça essas permissões e lançaria uma exceção se tentássemos escrever naquela página. Permissões em páginas de nível mais alto restringem as permissões possíveis em níveis mais baixos, então se definirmos a entrada de nível 3 como somente leitura, nenhuma página que use esta entrada pode ser gravável, mesmo se níveis mais baixos especificarem permissões de leitura/escrita.
+
+É importante notar que mesmo que este exemplo usasse apenas uma única instância de cada tabela, tipicamente há múltiplas instâncias de cada nível em cada espaço de endereço. No máximo, há:
+
+- uma tabela de nível 4,
+- 512 tabelas de nível 3 (porque a tabela de nível 4 tem 512 entradas),
+- 512 * 512 tabelas de nível 2 (porque cada uma das 512 tabelas de nível 3 tem 512 entradas), e
+- 512 * 512 * 512 tabelas de nível 1 (512 entradas para cada tabela de nível 2).
+
+### Formato da Tabela de Página
+
+Tabelas de página na arquitetura x86_64 são basicamente um array de 512 entradas. Na sintaxe Rust:
+
+```rust
+#[repr(align(4096))]
+pub struct PageTable {
+ entries: [PageTableEntry; 512],
+}
+```
+
+Como indicado pelo atributo `repr`, tabelas de página precisam ser alinhadas por página, isto é, alinhadas em um limite de 4 KiB. Este requisito garante que uma tabela de página sempre preenche uma página completa e permite uma otimização que torna as entradas muito compactas.
+
+Cada entrada tem 8 bytes (64 bits) de tamanho e tem o seguinte formato:
+
+Bit(s) | Nome | Significado
+------ | ---- | -------
+0 | present | a página está atualmente na memória
+1 | writable | é permitido escrever nesta página
+2 | user accessible | se não definido, apenas código em modo kernel pode acessar esta página
+3 | write-through caching | escritas vão diretamente para a memória
+4 | disable cache | nenhum cache é usado para esta página
+5 | accessed | a CPU define este bit quando esta página é usada
+6 | dirty | a CPU define este bit quando uma escrita nesta página ocorre
+7 | huge page/null | deve ser 0 em P1 e P4, cria uma página de 1 GiB em P3, cria uma página de 2 MiB em P2
+8 | global | página não é removida dos caches em troca de espaço de endereço (bit PGE do registrador CR4 deve estar definido)
+9-11 | available | pode ser usado livremente pelo SO
+12-51 | physical address | o endereço físico de 52 bits alinhado por página do frame ou da próxima tabela de página
+52-62 | available | pode ser usado livremente pelo SO
+63 | no execute | proíbe executar código nesta página (o bit NXE no registrador EFER deve estar definido)
+
+Vemos que apenas os bits 12–51 são usados para armazenar o endereço físico do frame. Os bits restantes são usados como flags ou podem ser usados livremente pelo sistema operacional. Isso é possível porque sempre apontamos para um endereço alinhado em 4096 bytes, seja para uma tabela de página alinhada por página ou para o início de um frame mapeado. Isso significa que os bits 0–11 são sempre zero, então não há razão para armazenar esses bits porque o hardware pode simplesmente defini-los para zero antes de usar o endereço. O mesmo é verdade para os bits 52–63, porque a arquitetura x86_64 suporta apenas endereços físicos de 52 bits (similar a como suporta apenas endereços virtuais de 48 bits).
+
+Vamos olhar mais de perto as flags disponíveis:
+
+- A flag `present` diferencia páginas mapeadas de não mapeadas. Ela pode ser usada para temporariamente trocar páginas para o disco quando a memória principal fica cheia. Quando a página é acessada subsequentemente, uma exceção especial chamada _page fault_ ocorre, à qual o sistema operacional pode reagir recarregando a página faltante do disco e então continuando o programa.
+- As flags `writable` e `no execute` controlam se o conteúdo da página é gravável ou contém instruções executáveis, respectivamente.
+- As flags `accessed` e `dirty` são automaticamente definidas pela CPU quando uma leitura ou escrita na página ocorre. Esta informação pode ser aproveitada pelo sistema operacional, por exemplo, para decidir quais páginas trocar ou se o conteúdo da página foi modificado desde o último salvamento no disco.
+- As flags `write-through caching` e `disable cache` permitem o controle de caches para cada página individualmente.
+- A flag `user accessible` torna uma página disponível para código em espaço de usuário, caso contrário, é acessível apenas quando a CPU está em modo kernel. Este recurso pode ser usado para tornar [chamadas de sistema] mais rápidas mantendo o kernel mapeado enquanto um programa em espaço de usuário está executando. No entanto, a vulnerabilidade [Spectre] pode permitir que programas em espaço de usuário leiam essas páginas de qualquer forma.
+- A flag `global` sinaliza ao hardware que uma página está disponível em todos os espaços de endereço e assim não precisa ser removida do cache de tradução (veja a seção sobre o TLB abaixo) em trocas de espaço de endereço. Esta flag é comumente usada junto com uma flag `user accessible` desmarcada para mapear o código do kernel para todos os espaços de endereço.
+- A flag `huge page` permite a criação de páginas de tamanhos maiores permitindo que as entradas das tabelas de página de nível 2 ou nível 3 apontem diretamente para um frame mapeado. Com este bit definido, o tamanho da página aumenta por fator 512 para 2 MiB = 512 * 4 KiB para entradas de nível 2 ou até 1 GiB = 512 * 2 MiB para entradas de nível 3. A vantagem de usar páginas maiores é que menos linhas do cache de tradução e menos tabelas de página são necessárias.
+
+[chamadas de sistema]: https://en.wikipedia.org/wiki/System_call
+[Spectre]: https://en.wikipedia.org/wiki/Spectre_(security_vulnerability)
+
+A crate `x86_64` fornece tipos para [tabelas de página] e suas [entradas], então não precisamos criar essas estruturas nós mesmos.
+
+[tabelas de página]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTable.html
+[entradas]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTableEntry.html
+
+### O Translation Lookaside Buffer
+
+Uma tabela de página de 4 níveis torna a tradução de endereços virtuais cara porque cada tradução requer quatro acessos à memória. Para melhorar o desempenho, a arquitetura x86_64 armazena em cache as últimas traduções no chamado _translation lookaside buffer_ (TLB). Isso permite pular a tradução quando ela ainda está em cache.
+
+Ao contrário dos outros caches da CPU, o TLB não é totalmente transparente e não atualiza ou remove traduções quando o conteúdo das tabelas de página muda. Isso significa que o kernel deve atualizar manualmente o TLB sempre que modifica uma tabela de página. Para fazer isso, há uma instrução especial da CPU chamada [`invlpg`] ("invalidate page") que remove a tradução para a página especificada do TLB, para que seja carregada novamente da tabela de página no próximo acesso. O TLB também pode ser completamente esvaziado recarregando o registrador `CR3`, que simula uma troca de espaço de endereço. A crate `x86_64` fornece funções Rust para ambas as variantes no [módulo `tlb`].
+
+[`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html
+[módulo `tlb`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tlb/index.html
+
+É importante lembrar de esvaziar o TLB em cada modificação de tabela de página porque caso contrário a CPU pode continuar usando a tradução antiga, o que pode levar a bugs não-determinísticos que são muito difíceis de depurar.
+
+## Implementação
+
+Uma coisa que ainda não mencionamos: **Nosso kernel já executa em paginação**. O bootloader que adicionamos na postagem ["Um Kernel Rust Mínimo"] já configurou uma hierarquia de paginação de 4 níveis que mapeia cada página do nosso kernel para um frame físico. O bootloader faz isso porque paginação é obrigatória no modo de 64 bits no x86_64.
+
+["Um Kernel Rust Mínimo"]: @/edition-2/posts/02-minimal-rust-kernel/index.md#creating-a-bootimage
+
+Isso significa que cada endereço de memória que usamos em nosso kernel era um endereço virtual. Acessar o buffer VGA no endereço `0xb8000` só funcionou porque o bootloader fez _identity mapping_ daquela página de memória, o que significa que ele mapeou a página virtual `0xb8000` para o frame físico `0xb8000`.
+
+Paginação já torna nosso kernel relativamente seguro, já que cada acesso à memória que está fora dos limites causa uma exceção de page fault em vez de escrever em memória física aleatória. O bootloader até define as permissões de acesso corretas para cada página, o que significa que apenas as páginas contendo código são executáveis e apenas páginas de dados são graváveis.
+
+### Page Faults
+
+Vamos tentar causar um page fault acessando alguma memória fora do nosso kernel. Primeiro, criamos um manipulador de page fault e o registramos em nossa IDT, para que vejamos uma exceção de page fault em vez de um [double fault] genérico:
+
+[double fault]: @/edition-2/posts/06-double-faults/index.md
+
+```rust
+// em src/interrupts.rs
+
+lazy_static! {
+ static ref IDT: InterruptDescriptorTable = {
+ let mut idt = InterruptDescriptorTable::new();
+
+ […]
+
+ idt.page_fault.set_handler_fn(page_fault_handler); // novo
+
+ idt
+ };
+}
+
+use x86_64::structures::idt::PageFaultErrorCode;
+use crate::hlt_loop;
+
+extern "x86-interrupt" fn page_fault_handler(
+ stack_frame: InterruptStackFrame,
+ error_code: PageFaultErrorCode,
+) {
+ use x86_64::registers::control::Cr2;
+
+ println!("EXCEÇÃO: PAGE FAULT");
+ println!("Endereço Acessado: {:?}", Cr2::read());
+ println!("Código de Erro: {:?}", error_code);
+ println!("{:#?}", stack_frame);
+ hlt_loop();
+}
+```
+
+O registrador [`CR2`] é automaticamente definido pela CPU em um page fault e contém o endereço virtual acessado que causou o page fault. Usamos a função [`Cr2::read`] da crate `x86_64` para lê-lo e imprimi-lo. O tipo [`PageFaultErrorCode`] fornece mais informações sobre o tipo de acesso à memória que causou o page fault, por exemplo, se foi causado por uma operação de leitura ou escrita. Por esta razão, também o imprimimos. Não podemos continuar a execução sem resolver o page fault, então entramos em um [`hlt_loop`] no final.
+
+[`CR2`]: https://en.wikipedia.org/wiki/Control_register#CR2
+[`Cr2::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr2.html#method.read
+[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html
+[LLVM bug]: https://github.com/rust-lang/rust/issues/57270
+[`hlt_loop`]: @/edition-2/posts/07-hardware-interrupts/index.md#the-hlt-instruction
+
+Agora podemos tentar acessar alguma memória fora do nosso kernel:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Olá Mundo{}", "!");
+
+ blog_os::init();
+
+ // novo
+ let ptr = 0xdeadbeaf as *mut u8;
+ unsafe { *ptr = 42; }
+
+ // como antes
+ #[cfg(test)]
+ test_main();
+
+ println!("Não crashou!");
+ blog_os::hlt_loop();
+}
+```
+
+Quando o executamos, vemos que nosso manipulador de page fault é chamado:
+
+
+
+O registrador `CR2` de fato contém `0xdeadbeaf`, o endereço que tentamos acessar. O código de erro nos diz através do [`CAUSED_BY_WRITE`] que a falha ocorreu ao tentar realizar uma operação de escrita. Ele nos diz ainda mais através dos [bits que _não_ estão definidos][`PageFaultErrorCode`]. Por exemplo, o fato de que a flag `PROTECTION_VIOLATION` não está definida significa que o page fault ocorreu porque a página alvo não estava presente.
+
+[`CAUSED_BY_WRITE`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.CAUSED_BY_WRITE
+
+Vemos que o ponteiro de instrução atual é `0x2031b2`, então sabemos que este endereço aponta para uma página de código. Páginas de código são mapeadas como somente leitura pelo bootloader, então ler deste endereço funciona mas escrever causa um page fault. Você pode tentar isso mudando o ponteiro `0xdeadbeaf` para `0x2031b2`:
+
+```rust
+// Note: O endereço real pode ser diferente para você. Use o endereço que
+// seu manipulador de page fault reporta.
+let ptr = 0x2031b2 as *mut u8;
+
+// lê de uma página de código
+unsafe { let x = *ptr; }
+println!("leitura funcionou");
+
+// escreve em uma página de código
+unsafe { *ptr = 42; }
+println!("escrita funcionou");
+```
+
+Ao comentar a última linha, vemos que o acesso de leitura funciona, mas o acesso de escrita causa um page fault:
+
+
+
+Vemos que a mensagem _"leitura funcionou"_ é impressa, o que indica que a operação de leitura não causou nenhum erro. No entanto, em vez da mensagem _"escrita funcionou"_, ocorre um page fault. Desta vez a flag [`PROTECTION_VIOLATION`] está definida além da flag [`CAUSED_BY_WRITE`], o que indica que a página estava presente, mas a operação não era permitida nela. Neste caso, escritas na página não são permitidas já que páginas de código são mapeadas como somente leitura.
+
+[`PROTECTION_VIOLATION`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.PROTECTION_VIOLATION
+
+### Acessando as Tabelas de Página
+
+Vamos tentar dar uma olhada nas tabelas de página que definem como nosso kernel é mapeado:
+
+```rust
+// em src/main.rs
+
+#[unsafe(no_mangle)]
+pub extern "C" fn _start() -> ! {
+ println!("Olá Mundo{}", "!");
+
+ blog_os::init();
+
+ use x86_64::registers::control::Cr3;
+
+ let (level_4_page_table, _) = Cr3::read();
+ println!("Tabela de página de nível 4 em: {:?}", level_4_page_table.start_address());
+
+ […] // test_main(), println(…), e hlt_loop()
+}
+```
+
+A função [`Cr3::read`] da crate `x86_64` retorna a tabela de página de nível 4 atualmente ativa do registrador `CR3`. Ela retorna uma tupla de um tipo [`PhysFrame`] e um tipo [`Cr3Flags`]. Estamos interessados apenas no frame, então ignoramos o segundo elemento da tupla.
+
+[`Cr3::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3.html#method.read
+[`PhysFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/frame/struct.PhysFrame.html
+[`Cr3Flags`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3Flags.html
+
+Quando o executamos, vemos a seguinte saída:
+
+```
+Tabela de página de nível 4 em: PhysAddr(0x1000)
+```
+
+Então a tabela de página de nível 4 atualmente ativa está armazenada no endereço `0x1000` na memória _física_, como indicado pelo tipo wrapper [`PhysAddr`]. A questão agora é: como podemos acessar esta tabela do nosso kernel?
+
+[`PhysAddr`]: https://docs.rs/x86_64/0.14.2/x86_64/addr/struct.PhysAddr.html
+
+Acessar memória física diretamente não é possível quando paginação está ativa, já que programas poderiam facilmente contornar a proteção de memória e acessar a memória de outros programas caso contrário. Então a única forma de acessar a tabela é através de alguma página virtual que está mapeada para o frame físico no endereço `0x1000`. Este problema de criar mapeamentos para frames de tabela de página é um problema geral, já que o kernel precisa acessar as tabelas de página regularmente, por exemplo, ao alocar uma pilha para uma nova thread.
+
+Soluções para este problema são explicadas em detalhes na próxima postagem.
+
+## Resumo
+
+Esta postagem introduziu duas técnicas de proteção de memória: segmentação e paginação. Enquanto a primeira usa regiões de memória de tamanho variável e sofre de fragmentação externa, a última usa páginas de tamanho fixo e permite controle muito mais refinado sobre permissões de acesso.
+
+Paginação armazena a informação de mapeamento para páginas em tabelas de página com um ou mais níveis. A arquitetura x86_64 usa tabelas de página de 4 níveis e um tamanho de página de 4 KiB. O hardware automaticamente percorre as tabelas de página e armazena em cache as traduções resultantes no translation lookaside buffer (TLB). Este buffer não é atualizado transparentemente e precisa ser esvaziado manualmente em mudanças de tabela de página.
+
+Aprendemos que nosso kernel já executa em cima de paginação e que acessos ilegais à memória causam exceções de page fault. Tentamos acessar as tabelas de página atualmente ativas, mas não conseguimos fazê-lo porque o registrador CR3 armazena um endereço físico que não podemos acessar diretamente do nosso kernel.
+
+## O Que Vem a Seguir?
+
+A próxima postagem explica como implementar suporte para paginação em nosso kernel. Ela apresenta diferentes formas de acessar memória física do nosso kernel, o que torna possível acessar as tabelas de página nas quais nosso kernel executa. Neste ponto, seremos capazes de implementar funções para traduzir endereços virtuais para físicos e para criar novos mapeamentos nas tabelas de página.
\ No newline at end of file
From e75cbdb5a70c1cabd92ecf01796ccb79bcfe278c Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Mon, 10 Nov 2025 08:19:57 -0300
Subject: [PATCH 10/26] [Translation][Portuguese pt-BR] post-9 (edition-2)
---
.../09-paging-implementation/index.pt-BR.md | 1012 +++++++++++++++++
1 file changed, 1012 insertions(+)
create mode 100644 blog/content/edition-2/posts/09-paging-implementation/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/09-paging-implementation/index.pt-BR.md b/blog/content/edition-2/posts/09-paging-implementation/index.pt-BR.md
new file mode 100644
index 00000000..8334c7c8
--- /dev/null
+++ b/blog/content/edition-2/posts/09-paging-implementation/index.pt-BR.md
@@ -0,0 +1,1012 @@
++++
+title = "Implementação de Paginação"
+weight = 9
+path = "pt-BR/paging-implementation"
+date = 2019-03-14
+
+[extra]
+chapter = "Gerenciamento de Memória"
+# Please update this when updating the translation
+translation_based_on_commit = "32f629fb2dc193db0dc0657338bd0ddec5914f05"
+
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Esta postagem mostra como implementar suporte a paginação em nosso kernel. Ela primeiro explora diferentes técnicas para tornar os frames físicos da tabela de página acessíveis ao kernel e discute suas respectivas vantagens e desvantagens. Em seguida, implementa uma função de tradução de endereços e uma função para criar um novo mapeamento.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-09`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-09
+
+
+
+## Introdução
+
+A [postagem anterior] deu uma introdução ao conceito de paginação. Ela motivou paginação comparando-a com segmentação, explicou como paginação e tabelas de página funcionam, e então introduziu o design de tabela de página de 4 níveis do `x86_64`. Descobrimos que o bootloader já configurou uma hierarquia de tabela de página para nosso kernel, o que significa que nosso kernel já executa em endereços virtuais. Isso melhora a segurança, já que acessos ilegais à memória causam exceções de page fault em vez de modificar memória física arbitrária.
+
+[postagem anterior]: @/edition-2/posts/08-paging-introduction/index.md
+
+A postagem terminou com o problema de que [não podemos acessar as tabelas de página do nosso kernel][end of previous post] porque estão armazenadas na memória física e nosso kernel já executa em endereços virtuais. Esta postagem explora diferentes abordagens para tornar os frames da tabela de página acessíveis ao nosso kernel. Discutiremos as vantagens e desvantagens de cada abordagem e então decidiremos sobre uma abordagem para nosso kernel.
+
+[end of previous post]: @/edition-2/posts/08-paging-introduction/index.md#accessing-the-page-tables
+
+Para implementar a abordagem, precisaremos de suporte do bootloader, então o configuraremos primeiro. Depois, implementaremos uma função que percorre a hierarquia de tabela de página para traduzir endereços virtuais em físicos. Finalmente, aprenderemos como criar novos mapeamentos nas tabelas de página e como encontrar frames de memória não usados para criar novas tabelas de página.
+
+## Acessando Tabelas de Página
+
+Acessar as tabelas de página do nosso kernel não é tão fácil quanto pode parecer. Para entender o problema, vamos dar uma olhada na hierarquia de tabela de página de 4 níveis de exemplo da postagem anterior novamente:
+
+
+
+A coisa importante aqui é que cada entrada de página armazena o endereço _físico_ da próxima tabela. Isso evita a necessidade de executar uma tradução para esses endereços também, o que seria ruim para o desempenho e poderia facilmente causar loops de tradução infinitos.
+
+O problema para nós é que não podemos acessar diretamente endereços físicos do nosso kernel, já que nosso kernel também executa em cima de endereços virtuais. Por exemplo, quando acessamos o endereço `4 KiB`, acessamos o endereço _virtual_ `4 KiB`, não o endereço _físico_ `4 KiB` onde a tabela de página de nível 4 está armazenada. Quando queremos acessar o endereço físico `4 KiB`, só podemos fazê-lo através de algum endereço virtual que mapeia para ele.
+
+Então, para acessar frames de tabela de página, precisamos mapear algumas páginas virtuais para eles. Existem diferentes formas de criar esses mapeamentos que todos nos permitem acessar frames de tabela de página arbitrários.
+
+### Identity Mapping
+
+Uma solução simples é fazer **identity map de todas as tabelas de página**:
+
+
+
+Neste exemplo, vemos vários frames de tabela de página com identity mapping. Desta forma, os endereços físicos das tabelas de página também são endereços virtuais válidos, então podemos facilmente acessar as tabelas de página de todos os níveis começando do registrador CR3.
+
+No entanto, isso confunde o espaço de endereço virtual e torna mais difícil encontrar regiões contínuas de memória de tamanhos maiores. Por exemplo, imagine que queremos criar uma região de memória virtual de tamanho 1000 KiB no gráfico acima, por exemplo, para [mapear um arquivo na memória]. Não podemos iniciar a região em `28 KiB` porque colidia com a página já mapeada em `1004 KiB`. Então temos que procurar mais até encontrarmos uma área não mapeada grande o suficiente, por exemplo em `1008 KiB`. Este é um problema de fragmentação similar ao da [segmentação].
+
+[mapear um arquivo na memória]: https://en.wikipedia.org/wiki/Memory-mapped_file
+[segmentação]: @/edition-2/posts/08-paging-introduction/index.md#fragmentation
+
+Igualmente, torna muito mais difícil criar novas tabelas de página porque precisamos encontrar frames físicos cujas páginas correspondentes já não estão em uso. Por exemplo, vamos assumir que reservamos a região de memória _virtual_ de 1000 KiB começando em `1008 KiB` para nosso arquivo mapeado na memória. Agora não podemos mais usar nenhum frame com endereço _físico_ entre `1000 KiB` e `2008 KiB`, porque não podemos fazer identity mapping dele.
+
+### Mapear em um Deslocamento Fixo
+
+Para evitar o problema de confundir o espaço de endereço virtual, podemos **usar uma região de memória separada para mapeamentos de tabela de página**. Então, em vez de fazer identity mapping dos frames de tabela de página, os mapeamos em um deslocamento fixo no espaço de endereço virtual. Por exemplo, o deslocamento poderia ser 10 TiB:
+
+
+
+Ao usar a memória virtual no intervalo `10 TiB..(10 TiB + tamanho da memória física)` exclusivamente para mapeamentos de tabela de página, evitamos os problemas de colisão do identity mapping. Reservar uma região tão grande do espaço de endereço virtual só é possível se o espaço de endereço virtual for muito maior que o tamanho da memória física. Isso não é um problema no x86_64, já que o espaço de endereço de 48 bits tem 256 TiB de tamanho.
+
+Esta abordagem ainda tem a desvantagem de que precisamos criar um novo mapeamento sempre que criamos uma nova tabela de página. Além disso, não permite acessar tabelas de página de outros espaços de endereço, o que seria útil ao criar um novo processo.
+
+### Mapear a Memória Física Completa
+
+Podemos resolver esses problemas **mapeando a memória física completa** em vez de apenas frames de tabela de página:
+
+
+
+Esta abordagem permite que nosso kernel acesse memória física arbitrária, incluindo frames de tabela de página de outros espaços de endereço. O intervalo de memória virtual reservado tem o mesmo tamanho de antes, com a diferença de que não contém mais páginas não mapeadas.
+
+A desvantagem desta abordagem é que tabelas de página adicionais são necessárias para armazenar o mapeamento da memória física. Essas tabelas de página precisam ser armazenadas em algum lugar, então usam uma parte da memória física, o que pode ser um problema em dispositivos com uma pequena quantidade de memória.
+
+No x86_64, no entanto, podemos usar [huge pages] com tamanho de 2 MiB para o mapeamento, em vez das páginas padrão de 4 KiB. Desta forma, mapear 32 GiB de memória física requer apenas 132 KiB para tabelas de página, já que apenas uma tabela de nível 3 e 32 tabelas de nível 2 são necessárias. Huge pages também são mais eficientes em cache, já que usam menos entradas no translation lookaside buffer (TLB).
+
+[huge pages]: https://en.wikipedia.org/wiki/Page_%28computer_memory%29#Multiple_page_sizes
+
+### Mapeamento Temporário
+
+Para dispositivos com quantidades muito pequenas de memória física, poderíamos **mapear os frames de tabela de página apenas temporariamente** quando precisamos acessá-los. Para poder criar os mapeamentos temporários, precisamos apenas de uma única tabela de nível 1 com identity mapping:
+
+
+
+A tabela de nível 1 neste gráfico controla os primeiros 2 MiB do espaço de endereço virtual. Isso ocorre porque ela é alcançável começando no registrador CR3 e seguindo a 0ª entrada nas tabelas de página de nível 4, nível 3 e nível 2. A entrada com índice `8` mapeia a página virtual no endereço `32 KiB` para o frame físico no endereço `32 KiB`, fazendo assim identity mapping da própria tabela de nível 1. O gráfico mostra este identity mapping pela seta horizontal em `32 KiB`.
+
+Ao escrever na tabela de nível 1 com identity mapping, nosso kernel pode criar até 511 mapeamentos temporários (512 menos a entrada necessária para o identity mapping). No exemplo acima, o kernel criou dois mapeamentos temporários:
+
+- Ao mapear a 0ª entrada da tabela de nível 1 para o frame com endereço `24 KiB`, ele criou um mapeamento temporário da página virtual em `0 KiB` para o frame físico da tabela de página de nível 2, indicado pela seta tracejada.
+- Ao mapear a 9ª entrada da tabela de nível 1 para o frame com endereço `4 KiB`, ele criou um mapeamento temporário da página virtual em `36 KiB` para o frame físico da tabela de página de nível 4, indicado pela seta tracejada.
+
+Agora o kernel pode acessar a tabela de página de nível 2 escrevendo na página `0 KiB` e a tabela de página de nível 4 escrevendo na página `36 KiB`.
+
+O processo para acessar um frame de tabela de página arbitrário com mapeamentos temporários seria:
+
+- Procurar uma entrada livre na tabela de nível 1 com identity mapping.
+- Mapear essa entrada para o frame físico da tabela de página que queremos acessar.
+- Acessar o frame alvo através da página virtual que mapeia para a entrada.
+- Definir a entrada de volta para não usada, removendo assim o mapeamento temporário novamente.
+
+Esta abordagem reutiliza as mesmas 512 páginas virtuais para criar os mapeamentos e assim requer apenas 4 KiB de memória física. A desvantagem é que é um pouco trabalhosa, especialmente já que um novo mapeamento pode requerer modificações a múltiplos níveis de tabela, o que significa que precisaríamos repetir o processo acima múltiplas vezes.
+
+### Tabelas de Página Recursivas
+
+Outra abordagem interessante, que não requer nenhuma tabela de página adicional, é **mapear a tabela de página recursivamente**. A ideia por trás desta abordagem é mapear uma entrada da tabela de página de nível 4 para a própria tabela de nível 4. Ao fazer isso, efetivamente reservamos uma parte do espaço de endereço virtual e mapeamos todos os frames de tabela de página atuais e futuros para esse espaço.
+
+Vamos passar por um exemplo para entender como isso tudo funciona:
+
+
+
+A única diferença para o [exemplo no início desta postagem] é a entrada adicional no índice `511` na tabela de nível 4, que está mapeada para o frame físico `4 KiB`, o frame da própria tabela de nível 4.
+
+[exemplo no início desta postagem]: #acessando-tabelas-de-pagina
+
+Ao deixar a CPU seguir esta entrada em uma tradução, ela não alcança uma tabela de nível 3, mas a mesma tabela de nível 4 novamente. Isso é similar a uma função recursiva que se chama, portanto esta tabela é chamada de _tabela de página recursiva_. A coisa importante é que a CPU assume que cada entrada na tabela de nível 4 aponta para uma tabela de nível 3, então agora trata a tabela de nível 4 como uma tabela de nível 3. Isso funciona porque tabelas de todos os níveis têm exatamente o mesmo layout no x86_64.
+
+Ao seguir a entrada recursiva uma ou múltiplas vezes antes de começarmos a tradução real, podemos efetivamente encurtar o número de níveis que a CPU percorre. Por exemplo, se seguirmos a entrada recursiva uma vez e então prosseguirmos para a tabela de nível 3, a CPU pensa que a tabela de nível 3 é uma tabela de nível 2. Indo mais longe, ela trata a tabela de nível 2 como uma tabela de nível 1 e a tabela de nível 1 como o frame mapeado. Isso significa que agora podemos ler e escrever a tabela de página de nível 1 porque a CPU pensa que é o frame mapeado. O gráfico abaixo ilustra os cinco passos de tradução:
+
+
+
+Similarmente, podemos seguir a entrada recursiva duas vezes antes de iniciar a tradução para reduzir o número de níveis percorridos para dois:
+
+
+
+Vamos passar por isso passo a passo: Primeiro, a CPU segue a entrada recursiva na tabela de nível 4 e pensa que alcança uma tabela de nível 3. Então ela segue a entrada recursiva novamente e pensa que alcança uma tabela de nível 2. Mas na realidade, ela ainda está na tabela de nível 4. Quando a CPU agora segue uma entrada diferente, ela aterrissa em uma tabela de nível 3, mas pensa que já está em uma tabela de nível 1. Então, enquanto a próxima entrada aponta para uma tabela de nível 2, a CPU pensa que aponta para o frame mapeado, o que nos permite ler e escrever a tabela de nível 2.
+
+Acessar as tabelas de níveis 3 e 4 funciona da mesma forma. Para acessar a tabela de nível 3, seguimos a entrada recursiva três vezes, enganando a CPU a pensar que já está em uma tabela de nível 1. Então seguimos outra entrada e alcançamos uma tabela de nível 3, que a CPU trata como um frame mapeado. Para acessar a própria tabela de nível 4, apenas seguimos a entrada recursiva quatro vezes até a CPU tratar a própria tabela de nível 4 como o frame mapeado (em azul no gráfico abaixo).
+
+
+
+Pode levar algum tempo para entender o conceito, mas funciona muito bem na prática.
+
+Na seção abaixo, explicamos como construir endereços virtuais para seguir a entrada recursiva uma ou múltiplas vezes. Não usaremos paginação recursiva para nossa implementação, então você não precisa ler para continuar com a postagem. Se isso te interessa, apenas clique em _"Cálculo de Endereço"_ para expandir.
+
+---
+
+
+
Criar e manter este blog e as bibliotecas associadas dá muito trabalho, mas eu realmente gosto de fazê-lo. Ao me apoiar, você me permite investir mais tempo em novo conteúdo, novos recursos e manutenção contínua. A melhor forma de me apoiar é me patrocinar no GitHub. Obrigado!
+"""
+comment_note = """
+Teve algum problema, quer deixar um feedback ou discutir mais ideias? Fique à vontade para deixar um comentário aqui! Por favor, use o inglês e siga o código de conduta do Rust. Este tópico de comentários está diretamente vinculado a uma discussão no GitHub, então você também pode comentar lá se preferir.
+"""
From b7109e850c746fd0aa82992dbb7b0df0b001bbc8 Mon Sep 17 00:00:00 2001
From: Richard Alves <54530477+richarddalves@users.noreply.github.com>
Date: Mon, 10 Nov 2025 09:09:54 -0300
Subject: [PATCH 15/26] [Translation][Portuguese pt-BR] post-12 (edition-2)
---
.../posts/12-async-await/index.pt-BR.md | 1826 +++++++++++++++++
1 file changed, 1826 insertions(+)
create mode 100644 blog/content/edition-2/posts/12-async-await/index.pt-BR.md
diff --git a/blog/content/edition-2/posts/12-async-await/index.pt-BR.md b/blog/content/edition-2/posts/12-async-await/index.pt-BR.md
new file mode 100644
index 00000000..df6c9b83
--- /dev/null
+++ b/blog/content/edition-2/posts/12-async-await/index.pt-BR.md
@@ -0,0 +1,1826 @@
++++
+title = "Async/Await"
+weight = 12
+path = "pt-BR/async-await"
+date = 2020-03-27
+
+[extra]
+chapter = "Multitasking"
+# Please update this when updating the translation
+translation_based_on_commit = "1ba06fe61c39c1379bd768060c21040b62ff3f0b"
+# GitHub usernames of the people that translated this post
+translators = ["richarddalves"]
++++
+
+Neste post, exploramos _multitarefa cooperativa_ e a funcionalidade _async/await_ do Rust. Fazemos uma análise detalhada de como async/await funciona em Rust, incluindo o design da trait `Future`, a transformação em máquina de estados e _pinning_. Então adicionamos suporte básico para async/await ao nosso kernel criando uma tarefa assíncrona de teclado e um executor básico.
+
+
+
+Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ou dúvida, abra um issue lá. Você também pode deixar comentários [na parte inferior]. O código-fonte completo desta publicação pode ser encontrado na branch [`post-12`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[na parte inferior]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-12
+
+
+
+## Multitarefa
+
+Uma das funcionalidades fundamentais da maioria dos sistemas operacionais é [_multitarefa_], que é a capacidade de executar múltiplas tarefas concorrentemente. Por exemplo, você provavelmente tem outros programas abertos enquanto olha este post, como um editor de texto ou uma janela de terminal. Mesmo se você tiver apenas uma janela de navegador aberta, provavelmente existem várias tarefas em segundo plano gerenciando suas janelas da área de trabalho, verificando atualizações ou indexando arquivos.
+
+[_multitarefa_]: https://en.wikipedia.org/wiki/Computer_multitasking
+
+Embora pareça que todas as tarefas estão sendo executadas em paralelo, apenas uma única tarefa pode ser executada em um núcleo de CPU por vez. Para criar a ilusão de que as tarefas estão sendo executadas em paralelo, o sistema operacional alterna rapidamente entre as tarefas ativas para que cada uma possa fazer um pouco de progresso. Como os computadores são rápidos, não notamos essas alternâncias na maior parte do tempo.
+
+Enquanto CPUs de núcleo único podem executar apenas uma tarefa por vez, CPUs multi-core podem executar múltiplas tarefas de forma verdadeiramente paralela. Por exemplo, uma CPU com 8 núcleos pode executar 8 tarefas ao mesmo tempo. Explicaremos como configurar CPUs multi-core em um post futuro. Para este post, focaremos em CPUs de núcleo único por simplicidade. (Vale notar que todas as CPUs multi-core começam com apenas um único núcleo ativo, então podemos tratá-las como CPUs de núcleo único por enquanto.)
+
+Existem duas formas de multitarefa: Multitarefa _cooperativa_ requer que as tarefas regularmente cedam o controle da CPU para que outras tarefas possam progredir. Multitarefa _preemptiva_ usa funcionalidades do sistema operacional para alternar threads em pontos arbitrários no tempo, pausando-as forçadamente. A seguir, exploraremos as duas formas de multitarefa em mais detalhes e discutiremos suas respectivas vantagens e desvantagens.
+
+### Multitarefa Preemptiva
+
+A ideia por trás da multitarefa preemptiva é que o sistema operacional controla quando alternar tarefas. Para isso, ele utiliza o fato de que recupera o controle da CPU em cada interrupção. Isso torna possível alternar tarefas sempre que uma nova entrada está disponível para o sistema. Por exemplo, seria possível alternar tarefas quando o mouse é movido ou um pacote de rede chega. O sistema operacional também pode determinar o tempo exato que uma tarefa tem permissão para executar configurando um temporizador de hardware para enviar uma interrupção após esse tempo.
+
+O gráfico seguinte ilustra o processo de alternância de tarefas em uma interrupção de hardware:
+
+
+
+Na primeira linha, a CPU está executando a tarefa `A1` do programa `A`. Todas as outras tarefas estão pausadas. Na segunda linha, uma interrupção de hardware chega na CPU. Como descrito no post [_Interrupções de Hardware_], a CPU imediatamente para a execução da tarefa `A1` e salta para o manipulador de interrupção definido na tabela de descritores de interrupção (IDT). Através deste manipulador de interrupção, o sistema operacional agora tem controle da CPU novamente, o que permite alternar para a tarefa `B1` em vez de continuar a tarefa `A1`.
+
+[_Interrupções de Hardware_]: @/edition-2/posts/07-hardware-interrupts/index.md
+
+#### Salvando o Estado
+
+Como as tarefas são interrompidas em pontos arbitrários no tempo, elas podem estar no meio de alguns cálculos. Para poder retomá-las mais tarde, o sistema operacional deve fazer backup do estado completo da tarefa, incluindo sua [pilha de chamadas] e os valores de todos os registradores da CPU. Este processo é chamado de [_troca de contexto_].
+
+[pilha de chamadas]: https://en.wikipedia.org/wiki/Call_stack
+[_troca de contexto_]: https://en.wikipedia.org/wiki/Context_switch
+
+Como a pilha de chamadas pode ser muito grande, o sistema operacional normalmente configura uma pilha de chamadas separada para cada tarefa em vez de fazer backup do conteúdo da pilha de chamadas em cada alternância de tarefa. Tal tarefa com sua própria pilha é chamada de [_thread de execução_] ou _thread_ para abreviar. Ao usar uma pilha separada para cada tarefa, apenas o conteúdo dos registradores precisa ser salvo em uma troca de contexto (incluindo o contador de programa e o ponteiro de pilha). Esta abordagem minimiza a sobrecarga de desempenho de uma troca de contexto, o que é muito importante já que trocas de contexto geralmente ocorrem até 100 vezes por segundo.
+
+[_thread de execução_]: https://en.wikipedia.org/wiki/Thread_(computing)
+
+#### Discussão
+
+A principal vantagem da multitarefa preemptiva é que o sistema operacional pode controlar totalmente o tempo de execução permitido de uma tarefa. Desta forma, ele pode garantir que cada tarefa receba uma parcela justa do tempo de CPU, sem a necessidade de confiar que as tarefas cooperarão. Isto é especialmente importante ao executar tarefas de terceiros ou quando múltiplos usuários compartilham um sistema.
+
+A desvantagem da preempção é que cada tarefa requer sua própria pilha. Comparado a uma pilha compartilhada, isso resulta em maior uso de memória por tarefa e frequentemente limita o número de tarefas no sistema. Outra desvantagem é que o sistema operacional sempre tem que salvar o estado completo dos registradores da CPU em cada troca de tarefa, mesmo que a tarefa tenha usado apenas um pequeno subconjunto dos registradores.
+
+Multitarefa preemptiva e threads são componentes fundamentais de um sistema operacional porque tornam possível executar programas de espaço de usuário não confiáveis. Discutiremos esses conceitos em detalhes completos em posts futuros. Para este post, no entanto, focaremos na multitarefa cooperativa, que também fornece capacidades úteis para o nosso kernel.
+
+### Multitarefa Cooperativa
+
+Em vez de pausar forçadamente as tarefas em execução em pontos arbitrários no tempo, a multitarefa cooperativa permite que cada tarefa execute até que ela voluntariamente ceda o controle da CPU. Isso permite que as tarefas se pausem em pontos convenientes no tempo, por exemplo, quando precisam esperar por uma operação de E/S de qualquer forma.
+
+Multitarefa cooperativa é frequentemente usada no nível da linguagem, como na forma de [corrotinas] ou [async/await]. A ideia é que o programador ou o compilador insira operações de [_yield_] no programa, que cedem o controle da CPU e permitem que outras tarefas executem. Por exemplo, um yield poderia ser inserido após cada iteração de um loop complexo.
+
+[corrotinas]: https://en.wikipedia.org/wiki/Coroutine
+[async/await]: https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html
+[_yield_]: https://en.wikipedia.org/wiki/Yield_(multithreading)
+
+É comum combinar multitarefa cooperativa com [operações assíncronas]. Em vez de esperar até que uma operação seja finalizada e impedir outras tarefas de executar durante esse tempo, operações assíncronas retornam um status "não pronto" se a operação ainda não foi finalizada. Neste caso, a tarefa em espera pode executar uma operação de yield para permitir que outras tarefas executem.
+
+[operações assíncronas]: https://en.wikipedia.org/wiki/Asynchronous_I/O
+
+#### Salvando o Estado
+
+Como as tarefas definem seus próprios pontos de pausa, elas não precisam que o sistema operacional salve seu estado. Em vez disso, elas podem salvar exatamente o estado de que precisam para continuar antes de se pausarem, o que frequentemente resulta em melhor desempenho. Por exemplo, uma tarefa que acabou de finalizar um cálculo complexo pode precisar fazer backup apenas do resultado final do cálculo, já que não precisa mais dos resultados intermediários.
+
+Implementações de tarefas cooperativas com suporte da linguagem são frequentemente até capazes de fazer backup das partes necessárias da pilha de chamadas antes de pausar. Como exemplo, a implementação async/await do Rust armazena todas as variáveis locais que ainda são necessárias em uma struct gerada automaticamente (veja abaixo). Ao fazer backup das partes relevantes da pilha de chamadas antes de pausar, todas as tarefas podem compartilhar uma única pilha de chamadas, o que resulta em consumo de memória muito menor por tarefa. Isso torna possível criar um número quase arbitrário de tarefas cooperativas sem ficar sem memória.
+
+#### Discussão
+
+A desvantagem da multitarefa cooperativa é que uma tarefa não cooperativa pode potencialmente executar por um tempo ilimitado. Assim, uma tarefa maliciosa ou com bugs pode impedir outras tarefas de executar e desacelerar ou até bloquear todo o sistema. Por esta razão, multitarefa cooperativa deve ser usada apenas quando todas as tarefas são conhecidas por cooperar. Como contraexemplo, não é uma boa ideia fazer o sistema operacional depender da cooperação de programas arbitrários de nível de usuário.
+
+No entanto, os fortes benefícios de desempenho e memória da multitarefa cooperativa tornam-na uma boa abordagem para uso _dentro_ de um programa, especialmente em combinação com operações assíncronas. Como um kernel de sistema operacional é um programa crítico em termos de desempenho que interage com hardware assíncrono, multitarefa cooperativa parece uma boa abordagem para implementar concorrência.
+
+## Async/Await em Rust
+
+A linguagem Rust fornece suporte de primeira classe para multitarefa cooperativa na forma de async/await. Antes que possamos explorar o que é async/await e como funciona, precisamos entender como _futures_ e programação assíncrona funcionam em Rust.
+
+### Futures
+
+Uma _future_ representa um valor que pode ainda não estar disponível. Isso poderia ser, por exemplo, um inteiro que é computado por outra tarefa ou um arquivo que está sendo baixado da rede. Em vez de esperar até que o valor esteja disponível, futures tornam possível continuar a execução até que o valor seja necessário.
+
+#### Exemplo
+
+O conceito de futures é melhor ilustrado com um pequeno exemplo:
+
+
+
+Este diagrama de sequência mostra uma função `main` que lê um arquivo do sistema de arquivos e então chama uma função `foo`. Este processo é repetido duas vezes: uma vez com uma chamada `read_file` síncrona e uma vez com uma chamada `async_read_file` assíncrona.
+
+Com a chamada síncrona, a função `main` precisa esperar até que o arquivo seja carregado do sistema de arquivos. Somente então ela pode chamar a função `foo`, o que requer que ela espere novamente pelo resultado.
+
+Com a chamada `async_read_file` assíncrona, o sistema de arquivos retorna diretamente uma future e carrega o arquivo assincronamente em segundo plano. Isso permite que a função `main` chame `foo` muito mais cedo, que então executa em paralelo com o carregamento do arquivo. Neste exemplo, o carregamento do arquivo até termina antes que `foo` retorne, então `main` pode trabalhar diretamente com o arquivo sem mais espera após `foo` retornar.
+
+#### Futures em Rust
+
+Em Rust, futures são representadas pela trait [`Future`], que se parece com isto:
+
+[`Future`]: https://doc.rust-lang.org/nightly/core/future/trait.Future.html
+
+```rust
+pub trait Future {
+ type Output;
+ fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll;
+}
+```
+
+O [tipo associado] `Output` especifica o tipo do valor assíncrono. Por exemplo, a função `async_read_file` no diagrama acima retornaria uma instância `Future` com `Output` definido como `File`.
+
+[tipo associado]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types
+
+O método [`poll`] permite verificar se o valor já está disponível. Ele retorna um enum [`Poll`], que se parece com isto:
+
+[`poll`]: https://doc.rust-lang.org/nightly/core/future/trait.Future.html#tymethod.poll
+[`Poll`]: https://doc.rust-lang.org/nightly/core/task/enum.Poll.html
+
+```rust
+pub enum Poll {
+ Ready(T),
+ Pending,
+}
+```
+
+Quando o valor já está disponível (por exemplo, o arquivo foi totalmente lido do disco), ele é retornado encapsulado na variante `Ready`. Caso contrário, a variante `Pending` é retornada, que sinaliza ao chamador que o valor ainda não está disponível.
+
+O método `poll` recebe dois argumentos: `self: Pin<&mut Self>` e `cx: &mut Context`. O primeiro se comporta de forma similar a uma referência normal `&mut self`, exceto que o valor `Self` é [_fixado_] em sua localização na memória. Entender `Pin` e por que é necessário é difícil sem entender como async/await funciona primeiro. Portanto, explicaremos isso mais tarde neste post.
+
+[_fixado_]: https://doc.rust-lang.org/nightly/core/pin/index.html
+
+O propósito do parâmetro `cx: &mut Context` é passar uma instância [`Waker`] para a tarefa assíncrona, por exemplo, o carregamento do arquivo do sistema de arquivos. Este `Waker` permite que a tarefa assíncrona sinalize que ela (ou uma parte dela) foi finalizada, por exemplo, que o arquivo foi carregado do disco. Como a tarefa principal sabe que será notificada quando a `Future` estiver pronta, ela não precisa chamar `poll` repetidamente. Explicaremos este processo em mais detalhes mais tarde neste post quando implementarmos nosso próprio tipo waker.
+
+[`Waker`]: https://doc.rust-lang.org/nightly/core/task/struct.Waker.html
+
+### Trabalhando com Futures
+
+Agora sabemos como futures são definidas e entendemos a ideia básica por trás do método `poll`. No entanto, ainda não sabemos como trabalhar efetivamente com futures. O problema é que futures representam os resultados de tarefas assíncronas, que podem ainda não estar disponíveis. Na prática, no entanto, frequentemente precisamos desses valores diretamente para cálculos adicionais. Então a questão é: Como podemos recuperar eficientemente o valor de uma future quando precisamos dele?
+
+#### Esperando por Futures
+
+Uma resposta possível é esperar até que uma future se torne pronta. Isso poderia se parecer com algo assim:
+
+```rust
+let future = async_read_file("foo.txt");
+let file_content = loop {
+ match future.poll(…) {
+ Poll::Ready(value) => break value,
+ Poll::Pending => {}, // não faz nada
+ }
+}
+```
+
+Aqui nós _ativamente_ esperamos pela future chamando `poll` repetidamente em um loop. Os argumentos para `poll` não importam aqui, então os omitimos. Embora esta solução funcione, ela é muito ineficiente porque mantemos a CPU ocupada até que o valor se torne disponível.
+
+Uma abordagem mais eficiente poderia ser _bloquear_ a thread atual até que a future se torne disponível. Isso é, claro, possível apenas se você tiver threads, então esta solução não funciona para o nosso kernel, pelo menos ainda não. Mesmo em sistemas onde o bloqueio é suportado, frequentemente não é desejado porque transforma uma tarefa assíncrona em uma tarefa síncrona novamente, inibindo assim os benefícios de desempenho potenciais de tarefas paralelas.
+
+#### Combinadores de Future
+
+Uma alternativa a esperar é usar combinadores de future. Combinadores de future são métodos como `map` que permitem encadear e combinar futures juntas, similar aos métodos da trait [`Iterator`]. Em vez de esperar pela future, esses combinadores retornam uma future eles mesmos, que aplica a operação de mapeamento em `poll`.
+
+[`Iterator`]: https://doc.rust-lang.org/stable/core/iter/trait.Iterator.html
+
+Como exemplo, um simples combinador `string_len` para converter uma `Future