diff --git a/blog/config.toml b/blog/config.toml
index f65cebdb9..4a9b417b6 100644
--- a/blog/config.toml
+++ b/blog/config.toml
@@ -12,6 +12,7 @@ languages = [
{ code = "zh-TW" }, # Chinese (traditional)
{ code = "ja" }, # Japanese
{ code = "fa" }, # Persian
+ { code = "ru" }, # Russian
]
ignored_content = ["*/README.md"]
@@ -97,3 +98,17 @@ translated_content = "محتوای ترجمه شده:"
translated_content_notice = "این یک ترجمه از جامعه کاربران برای پست _original.title_ است. ممکن است ناقص، منسوخ شده یا دارای خطا باشد. لطفا هر گونه مشکل را در این ایشو گزارش دهید!"
translated_by = "ترجمه توسط"
word_separator = "و"
+
+
+[translations.ru]
+lang_name = "Russian"
+toc = "Содержание"
+all_posts = "« Все посты"
+comments = "Комментарии"
+comments_notice = "Пожалуйста, оставляйте комментарии на английском по возможности."
+readmore = "читать дальше »"
+not_translated = "(Этот пост еще не переведен.)"
+translated_content = "Переведенное содержание:"
+translated_content_notice = "Это перевод сообщества поста _original.title_. Он может быть неполным, устаревшим или содержать ошибки. Пожалуйста, сообщайте о любых проблемах!"
+translated_by = "Перевод сделан"
+word_separator = "и"
\ No newline at end of file
diff --git a/blog/content/_index.ru.md b/blog/content/_index.ru.md
new file mode 100644
index 000000000..a9bb07ecd
--- /dev/null
+++ b/blog/content/_index.ru.md
@@ -0,0 +1,13 @@
++++
+template = "edition-2/index.html"
++++
+
+
Собственная операционная система на Rust
+
+
+
+Этот блог посвящен написанию маленькой операционной системы на [языке программирования Rust](https://www.rust-lang.org/). Каждый пост — это маленькое руководство, включающее в себя весь необходимый код, — вы сможете следовать ему, если пожелаете. Исходный код также доступен в соотвестующем [репозитории на Github](https://github.com/phil-opp/blog_os).
+
+Последний пост:
+
+
diff --git a/blog/content/edition-2/extra/building-on-android/index.md b/blog/content/edition-2/extra/building-on-android/index.md
index 2e0bcb392..0ec2235a4 100644
--- a/blog/content/edition-2/extra/building-on-android/index.md
+++ b/blog/content/edition-2/extra/building-on-android/index.md
@@ -13,7 +13,7 @@ I finally managed to get `blog_os` building on my Android phone using [termux](h
### Install Termux and Nightly Rust
-First, install [termux](https://termux.com/) from the [Google Play Store](https://play.google.com/store/apps/details?id=com.termux) or from [F-Droid](https://f-droid.org/packages/com.termux/). After installing, open it and perform the following steps:
+First, install [termux](https://termux.com/) from the [Google Play Store](https://play.google.com/store/apps/details?id=com.termux) or from F-Droid. After installing, open it and perform the following steps:
- Install fish shell, set as default shell, and launch it:
```
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md
new file mode 100644
index 000000000..2677ac3f2
--- /dev/null
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md
@@ -0,0 +1,522 @@
++++
+title = "Независимый бинарный файл на Rust"
+weight = 1
+path = "ru/freestanding-rust-binary"
+date = 2018-02-10
+
+[extra]
+chapter = "С нуля"
+translators = ["MrZloHex"]
++++
+
+Первый шаг в создании собственного ядра операционной системы — это создание исполняемого файла на Rust, который не будет подключать стандартную библиотеку. Именно это дает возможность запускать Rust код на [голом железе][bare metal] без слоя операционной системы.
+
+[bare metal]: https://en.wikipedia.org/wiki/Bare_machine
+
+
+
+Этот блог открыто разрабатывается на [GitHub]. Если у вас возникли какие-либо проблемы или вопросы, пожалуйста, создайте _issue_. Также вы можете оставлять комментарии [в конце страницы][at the bottom]. Полный исходный код для этого поста вы можете найти в репозитории в ветке [`post-01`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-01
+
+
+
+## Введение
+Для того, чтобы написать ядро операционной системы, нужен код, который не зависит от операционной системы и ее возможностей. Это означает, что нельзя использовать потоки, файлы, [кучу][heap], сети, случайные числа, стандартный вывод или другие возможности, которые зависят от ОС или определённого железа.
+
+[heap]: https://en.wikipedia.org/wiki/Heap_(data_structure)
+
+Это значит, что нельзя использовать большую часть [стандартной библиотеки Rust][Rust Standard library], но остается множество других возможностей Rust, которые _можно использовать_. Например, [итераторы][iterators], [замыкания][closures], [сопоставление с образцом][pattern matching], [`Option`][option] и [`Result`][result], [форматирование строк][string formatting] и, конечно же, [систему владения][ownership system]. Эти функции дают возможность для написания ядра в очень выразительном и высоко-уровневом стиле, не беспокоясь о [неопределенном поведении][undefined behavior] или [сохранности памяти][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/
+[iterators]: 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
+[string formatting]: https://doc.rust-lang.org/core/macro.write.html
+[ownership system]: 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
+
+Чтобы создать ядро ОС на Rust, нужно создать исполняемый файл, который мог бы запускаться без ОС.
+
+Этот пост описывает необходимые шаги для создания независимого исполняемого файла на Rust и объясняет, почему эти шаги нужны. Если вам интересен только минимальный пример, можете сразу перейти к __[итогам](#Итоги)__.
+
+## Отключение стандартной библиотеки
+По умолчанию, все Rust-крейты подключают [стандартную библиотеку][standard library], которая зависит от возможностей операционной системы, таких как потоки, файлы, сети. Она также зависит от стандартной библиотки C `libc`, которая очень тесно взаимодействует с возможностями ОС. Так как мы хотим написать операционную систему, мы не можем использовать библиотеки, которые зависят от операционной системы. Поэтому необходимо отключить автоматические подключение стандартной библиотеки через [атрибут `no_std`][attribute].
+
+[standard library]: https://doc.rust-lang.org/std/
+[attribute]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
+
+Мы начнем с создания нового проекта cargo. Самый простой способ сделать это — через командную строку:
+
+```
+cargo new blog_os --bin -- edition 2018
+```
+
+Я назвал этот проект `blog_os`, но вы можете назвать как вам угодно. Флаг `--bin` указывает на то, что мы хотим создать исполняемый файл (а не библиотеку), а флаг `--edition 2018` указывает, что мы хотим использовать [редакцию Rust 2018][edition] для нашего крейта. После выполнения команды cargo создаст каталог со следующей структурой:
+
+[edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
+
+```
+blog_os
+├── Cargo.toml
+└── src
+ └── main.rs
+```
+
+`Cargo.toml` содержит данные и конфигурацию крейта, такие как _название, автор, [семантическую версию][semantic version]_ и _зависимости_ от других крейтов. Файл `src/main.rs` содержит корневой модуль нашего крейта и функцию `main`. Можно скомпилировать крейт с помощью `cargo build` и запустить скомпилированную программу `blog_os` в поддиректории `target/debug`.
+
+[semantic version]: https://semver.org/
+
+### Атрибут `no_std`
+
+В данный момент наш крейт неявно подключает стандартную библиотеку. Это можно исправить путем добавления [атрибута `no_std`][attribute]:
+
+```rust
+// main.rs
+
+#![no_std]
+
+fn main() {
+ println!("Hello, world!");
+}
+```
+
+Если сейчас попробовать скомпилировать программу (с помоцью команды `cargo build`), то появится следующая ошибка:
+
+```
+error: cannot find macro `println!` in this scope
+ --> src/main.rs:4:5
+ |
+4 | println!("Hello, world!");
+ | ^^^^^^^
+```
+
+Эта ошибка объясняется тем, что [макрос `println`][macro] — часть стандартной библиотеки, которая была отключена. Поэтому у нас больше нет возможность выводить что-либо на экран. Это логично, так как `println` печатает через [стандартный вывод][standard output], который, в свою очередь, является специальным файловым дескриптором, предоставляемым операционной системой.
+
+[macro]: https://doc.rust-lang.org/std/macro.println.html
+[standard output]: https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29
+
+Давайте уберем макрос `println` и попробуем скомпилировать еще раз:
+
+```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`
+```
+
+Сейчас компилятор не может найти функцию `#[panic_handler]` и «элемент языка».
+
+## Реализация _паники_
+
+Атрибут `pаnic_handler` определяет функцию, которая должна вызываться, когда происходит [паника (panic)][panic]. Стандартная библиотека предоставляет собственную функцию обработчика паники, но после отключения стандартной библиотеки мы должны написать собственный обработчик:
+
+[panic]: https://doc.rust-lang.org/stable/book/ch09-01-unrecoverable-errors-with-panic.html
+
+```rust
+// in main.rs
+
+use core::panic::PanicInfo;
+
+/// This function is called on panic.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+Параметр [`PanicInfo`][PanicInfo] содержит название файла и строку, где произошла паника, и дополнительное сообщение с пояснением. Эта функция никогда не должна возвратиться, и такая функция называется [расходящейся][diverging functions] и она возращает [пустой тип]["never" type] `!`. Пока что мы ничего не можем сделать в этой функции, поэтому мы просто войдем в бесконечный цикл.
+
+[PanicInfo]: https://doc.rust-lang.org/nightly/core/panic/struct.PanicInfo.html
+[diverging functions]: https://doc.rust-lang.org/1.30.0/book/first-edition/functions.html#diverging-functions
+["never" type]: https://doc.rust-lang.org/nightly/std/primitive.never.html
+
+## Элемент языка `eh_personality`
+
+Элементы языка — это специальные функции и типы, которые необходимы компилятору. Например, трейт [`Copy`] указывает компилятору, у каких типов есть [_семантика копирования_][`Copy`]. Если мы посмотрим на [реализацию][copy code] этого трейта, то увидим специальный атрибут `#[lang = "copy"]`, который говорит, что этот трейт является элементом языка.
+
+[`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
+
+Несмотря на то, что можно предоставить свою реализацию элементов языка, это следует делать только в крайних случаях. Причина в том, что элементы языка являются крайне нестабильными деталями реализации, и компилятор даже не проверяет в них согласованность типов (поэтому он даже не проверяет, имеет ли функция правильные типы аргументов). К счастью, существует более стабильный способ исправить вышеупомянутую ошибку.
+
+Элемент языка [`eh_personality`][language item] указывает на функцию, которая используется для реализации [раскрутки стека][stack unwinding]. По умолчанию, Rust использует раскрутку для запуска деструктуров для всех _живых_ переменных на стеке в случае [паники][panic]. Это гарантирует, что вся использованная память будет освобождена, и позволяет родительскому потоку перехватить панику и продолжить выполнение. Раскрутка — очень сложный процесс и требует некоторых специльных библиотек ОС (например, [libunwind] для Linux или [structured exception handling] для Windows), так что мы не должны использовать её для нашей операционной системы.
+
+[language item]: 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/
+[structured exception handling]: https://docs.microsoft.com/de-de/windows/win32/debug/structured-exception-handling
+
+### Отключение раскрутки
+
+Существуют и другие случаи использования, для которых раскрутка нежелательна, поэтому Rust предоставляет опцию [прерывания выполнения при панике][abort on panic]. Это отключает генерацию информации о символах раскрутки и, таким образом, значительно уменьшает размер бинарного файла. Есть несколько мест, где мы можем отключить раскрутку. Самый простой способ — добавить следующие строки в наш `Cargo.toml`:
+
+```toml
+[profile.dev]
+panic = "abort"
+
+[profile.release]
+panic = "abort"
+```
+
+Это устанавливает стратегию паники на `abort` (прерывание) как для профиля `dev` (используемого для `cargo build`), так и для профиля `release` (используемого для `cargo build --release`). Теперь элемент языка `eh_personality` больше не должен требоваться.
+
+[abort on panic]: https://github.com/rust-lang/rust/pull/32900
+
+Теперь мы исправили обе вышеуказанные ошибки. Однако, если мы сейчас попытаемся скомпилировать программу, возникнет другая ошибка:
+
+```
+> cargo build
+error: requires `start` lang_item
+```
+
+В нашей программе отсутствует элемент языка `start`, который определяет начальную точку входа программы.
+
+## Аттрибут `start`
+
+Можно подумать, что функция `main` — это первая функция, вызываемая при запуске программы. Однако в большинстве языков есть [среда выполнения][runtime system], которая отвечает за такие вещи, как сборка мусора (например, в Java) или программные потоки (например, goroutines в Go). Эта система выполнения должна быть вызвана до `main`, поскольку ей необходимо инициализировать себя.
+
+[runtime system]: https://en.wikipedia.org/wiki/Runtime_system
+
+В типичном исполнимом файле Rust, который использует стандартную библиотеку, выполнение начинается в runtime-библиотеке C под названием `crt0` ("C runtime zero"), которая создает окружение для C-приложения. Это включает создание стека и размещение аргументов в нужных регистрах. Затем C runtime вызывает [точку входа для Rust-приложения][rt::lang_start], которая обозначается элементом языка `start`. Rust имеет очень маленький runtime, который заботится о некоторых мелочах, таких как установка защиты от переполнения стека или вывод сообщения при панике. Затем рантайм вызывает функцию `main`.
+
+[rt::lang_start]: https://github.com/rust-lang/rust/blob/bb4d1491466d8239a7a5fd68bd605e3276e97afb/src/libstd/rt.rs#L32-L73
+
+Наш независимый исполняемый файл не имеет доступа к runtime Rust и `crt0`, поэтому нам нужно определить собственную точку входа. Реализация языкового элемента `start` не поможет, поскольку он все равно потребует `crt0`. Вместо этого нам нужно напрямую переопределить точку входа `crt0`.
+
+### Переопределение точки входа
+
+Чтобы сообщить компилятору Rust, что мы не хотим использовать стандартную цепочку точек входа, мы добавляем атрибут `#![no_main]`.
+
+```rust
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+/// This function is called on panic.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+Можно заметить, что мы удалили функцию `main`. Причина в том, что `main` не имеет смысла без стандартного runtime, которая ее вызывает. Вместо этого мы переопределим точку входа операционной системы с помощью нашей собственной функции `_start`:
+
+```rust
+#[no_mangle]
+pub extern "C" fn _start() -> ! {
+ loop {}
+}
+```
+
+Используя атрибут `#[no_mangle]`, мы отключаем [искажение имен][name mangling], чтобы гарантировать, что компилятор Rust сгенерирует функцию с именем `_start`. Без этого атрибута компилятор генерировал бы какой-нибудь загадочный символ `_ZN3blog_os4_start7hb173fedf945531caE`, чтобы дать каждой функции уникальное имя. Атрибут необходим, потому что на следующем этапе нам нужно сообщить имя функции точки входа компоновщику.
+
+Мы также должны пометить функцию как `extern "C"`, чтобы указать компилятору, что он должен использовать [соглашение о вызове C][C calling convention] для этой функции (вместо неопределенного соглашения о вызове Rust). Причина именования функции `_start` в том, что это имя точки входа по умолчанию для большинства систем.
+
+[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
+[C calling convention]: https://en.wikipedia.org/wiki/Calling_convention
+
+Возвращаемый `!` означает, что функция является расходящейся, т.е. не имеет права возвращаться. Это необходимо, поскольку точка входа не вызывается никакой функцией, а вызывается непосредственно операционной системой или загрузчиком. Поэтому вместо возврата точка входа должна, например, вызвать [системный вызов `exit`][`exit` system call] операционной системы. В нашем случае разумным действием может быть выключение машины, поскольку ничего не останется делать, если независимый исполнимый файл завершит исполнение. Пока что мы выполняем это требование путем бесконечного цикла.
+
+[`exit` system call]: https://en.wikipedia.org/wiki/Exit_(system_call)
+
+Если мы выполним `cargo build` сейчас, мы получим ошибку компоновщика (_linker_ error).
+
+## Ошибки компоновщика
+
+Компоновщик — это программа, которая объединяет сгенерированный код в исполняемый файл. Поскольку формат исполняемого файла отличается в Linux, Windows и macOS, в каждой системе есть свой компоновщик, и каждый покажет свою ошибку. Основная причина ошибок одна и та же: конфигурация компоновщика по умолчанию предполагает, что наша программа зависит от C runtime, а это не так.
+
+Чтобы устранить ошибки, нам нужно сообщить компоновщику, что он не должен включать C runtime. Мы можем сделать это, передав компоновщику определенный набор аргументов или выполнив компиляцию для голого железа.
+
+### Компиляция для голого железа
+
+По умолчанию Rust пытается создать исполняемый файл, который может быть запущен в окружении вашей текущей системы. Например, если вы используете Windows на `x86_64`, Rust пытается создать исполняемый файл Windows `.exe`, который использует инструкции `x86_64`. Это окружение называется вашей "хост-системой".
+
+Для описания различных окружений Rust использует строку [_target triple_]. Вы можете узнать тройку вашей хост-системы, выполнив команду `rustc --version --verbose`:
+
+[_target triple_]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+
+```
+rustc 1.35.0-nightly (474e7a648 2019-04-07)
+binary: rustc
+commit-hash: 474e7a6486758ea6fc761893b1a49cd9076fb0ab
+commit-date: 2019-04-07
+host: x86_64-unknown-linux-gnu
+release: 1.35.0-nightly
+LLVM version: 8.0
+```
+
+Приведенный выше результат получен от системы `x86_64` Linux. Мы видим, что тройка `host` — это `x86_64-unknown-linux-gnu`, которая включает архитектуру процессора (`x86_64`), производителя (`unknown`), операционную систему (`linux`) и [ABI] (`gnu`).
+
+[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
+
+Компилируя для тройки нашего хоста, компилятор Rust и компоновщик предполагают наличие базовой операционной системы, такой как Linux или Windows, которая по умолчанию использует C runtime, что вызывает ошибки компоновщика. Поэтому, чтобы избежать ошибок компоновщика, мы можем настроить компиляцию для другого окружения без базовой операционной системы.
+
+Примером такого "голого" окружения является тройка `thumbv7em-none-eabihf`, которая описывает [ARM] архитектуру. Детали не важны, важно лишь то, что тройка не имеет базовой операционной системы, на что указывает `none` в тройке. Чтобы иметь возможность компилировать для этой системы, нам нужно добавить ее в rustup:
+
+[ARM]: https://en.wikipedia.org/wiki/ARM_architecture
+
+```
+rustup target add thumbv7em-none-eabihf
+```
+
+Это загружает копию стандартной библиотеки (и `core`) для системы. Теперь мы можем собрать наш независимый исполняемый файл для этой системы:
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+Передавая аргумент `--target`, мы [кросс-компилируем][cross compile] наш исполняемый файл для голого железа. Поскольку система, под которую мы компилируем, не имеет операционной системы, компоновщик не пытается компоновать C runtime, и наша компиляция проходит успешно без каких-либо ошибок компоновщика.
+
+[cross compile]: https://en.wikipedia.org/wiki/Cross_compiler
+
+Именно этот подход мы будем использовать для сборки ядра нашей ОС. Вместо `thumbv7em-none-eabihf` мы будем использовать [custom target], который описывает окружение для архитектуры `x86_64`. Подробности будут описаны в следующем посте.
+
+[custom target]: https://doc.rust-lang.org/rustc/targets/custom.html
+
+### Аргументы компоновщика
+
+Вместо компиляции под голое железо, ошибки компоновщика можно исправить, передав ему определенный набор аргументов. Мы не будем использовать этот подход для нашего ядра, поэтому данный раздел является необязательным и приводится только для полноты картины. Щелкните на _"Аргументы компоновщика"_ ниже, чтобы показать необязательное содержание.
+
+
+
+Аргументы компоновщика
+
+В этом разделе мы рассмотрим ошибки компоновщика, возникающие в Linux, Windows и macOS, и объясним, как их решить, передав компоновщику дополнительные аргументы. Обратите внимание, что формат исполняемого файла и компоновщик отличаются в разных операционных системах, поэтому для каждой системы требуется свой набор аргументов.
+
+#### Linux
+
+На Linux возникает следующая ошибка компоновщика (сокращенно):
+
+```
+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
+```
+
+Проблема заключается в том, что компоновщик по умолчанию включает процедуру запуска C runtime, которая также называется `_start`. Она требует некоторых символов стандартной библиотеки C `libc`, которые мы не включаем из-за атрибута `no_std`, поэтому компоновщик не может подключить эти библиотеки, поэтому появляются ошибки. Чтобы решить эту проблему, мы можем сказать компоновщику, что он не должен компоновать процедуру запуска C, передав флаг `-nostartfiles`.
+
+Одним из способов передачи атрибутов компоновщика через cargo является команда `cargo rustc`. Команда ведет себя точно так же, как `cargo build`, но позволяет передавать опции `rustc`, базовому компилятору Rust. У `rustc` есть флаг `-C link-arg`, который передает аргумент компоновщику. В совокупности наша новая команда сборки выглядит следующим образом:
+
+```
+cargo rustc -- -C link-arg=-nostartfiles
+```
+
+Теперь наш крейт собирается как независимый исполняемый файл в Linux!
+
+Нам не нужно было явно указывать имя нашей функции точки входа, поскольку компоновщик по умолчанию ищет функцию с именем `_start`.
+
+#### Windows
+
+В Windows возникает другая ошибка компоновщика (сокращенно):
+
+```
+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
+```
+
+Ошибка "точка входа должна быть определена" (_"entry point must be defined"_) означает, что компоновщик не может найти точку входа. В Windows имя точки входа по умолчанию [зависит от используемой подсистемы][windows-subsystems]. Для подсистемы `CONSOLE` компоновщик ищет функцию с именем `mainCRTStartup`, а для подсистемы `WINDOWS` - функцию с именем `WinMainCRTStartup`. Чтобы переопределить названия точки входа на `_start`, мы можем передать компоновщику аргумент `/ENTRY`:
+
+[windows-subsystems]: https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol
+
+```
+cargo rustc -- -C link-arg=/ENTRY:_start
+```
+
+Из разного формата аргументов мы ясно видим, что компоновщик Windows - это совершенно другая программа, чем компоновщик Linux.
+
+Теперь возникает другая ошибка компоновщика:
+
+```
+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
+```
+
+Эта ошибка возникает из-за того, что исполняемые файлы Windows могут использовать различные [подсистемы][windows-subsystems]. Для обычных программ они определяются в зависимости от имени точки входа: если точка входа называется `main`, то используется подсистема `CONSOLE`, а если точка входа называется `WinMain`, то используется подсистема `WINDOWS`. Поскольку наша функция `_start` имеет другое имя, нам нужно явно указать подсистему:
+
+```
+cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
+```
+
+Здесь мы используем подсистему `CONSOLE`, но подойдет и подсистема `WINDOWS`. Вместо того, чтобы передавать `-C link-arg` несколько раз, мы используем `-C link-args`, который принимает список аргументов, разделенных пробелами.
+
+С помощью этой команды наш исполняемый файл должен успешно скомпилироваться под Windows.
+
+#### macOS
+
+На macOS возникает следующая ошибка компоновщика (сокращенно):
+
+```
+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 […]
+```
+
+Это сообщение об ошибке говорит нам, что компоновщик не может найти функцию точки входа с именем по умолчанию `main` (по какой-то причине в macOS все функции имеют префикс `_`). Чтобы установить точку входа в нашу функцию `_start`, мы передаем аргумент компоновщика `-e`:
+
+```
+cargo rustc -- -C link-args="-e __start"
+```
+
+Флаг `-e` задает имя функции точки входа. Поскольку в macOS все функции имеют дополнительный префикс `_`, нам нужно установить точку входа на `__start` вместо `_start`.
+
+Теперь возникает следующая ошибка компоновщика:
+
+```
+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 [официально не поддерживает статически скомпонованные исполняемые файлы][static binary] и по умолчанию требует от программ компоновки библиотеки `libSystem`. Чтобы переопределить это поведение и скомпоновать статический исполняемый файл, передадим компоновщику флаг `-static`:
+
+[static binary]: https://developer.apple.com/library/archive/qa/qa1118/_index.html
+
+```
+cargo rustc -- -C link-args="-e __start -static"
+```
+
+Этого все равно недостаточно, так как возникает третья ошибка компоновщика:
+
+```
+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 […]
+```
+
+Эта ошибка возникает из-за того, что программы на macOS по умолчанию ссылаются на `crt0` ("C runtime zero"). Она похожа на ошибку под Linux и тоже может быть решена добавлением аргумента компоновщика `-nostartfiles`:
+
+```
+cargo rustc -- -C link-args="-e __start -static -nostartfiles"
+```
+
+Теперь наша программа должна успешно скомпилироваться на macOS.
+
+#### Объединение команд сборки
+
+Сейчас у нас разные команды сборки в зависимости от платформы хоста, что не идеально. Чтобы избежать этого, мы можем создать файл с именем `.cargo/config.toml`, который будет содержать аргументы для конкретной платформы:
+
+```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"]
+```
+
+Ключ `rustflags` содержит аргументы, которые автоматически добавляются к каждому вызову `rustc`. Более подробную информацию о файле `.cargo/config.toml` можно найти в [официальной документации](https://doc.rust-lang.org/cargo/reference/config.html).
+
+Теперь наша программа должна собираться на всех трех платформах с помощью простой `cargo build`.
+
+#### Должны ли вы это делать?
+
+Хотя можно создать независимый исполняемый файл для Linux, Windows и macOS, это, вероятно, не очень хорошая идея. Причина в том, что наш исполняемый файл все еще ожидает различных вещей, например, инициализации стека при вызове функции `_start`. Без C runtime некоторые из этих требований могут быть не выполнены, что может привести к сбою нашей программы, например, из-за ошибки сегментации.
+
+Если вы хотите создать минимальный исполняемый файл, запускаемый поверх существующей операционной системы, то включение `libc` и установка атрибута `#[start]`, как описано [здесь] (https://doc.rust-lang.org/1.16.0/book/no-stdlib.html), вероятно, будет идеей получше.
+
+
+
+## Итоги
+
+Минимальный независимый исполняемый бинарный файл Rust выглядит примерно так:
+
+`src/main.rs`:
+
+```rust
+#![no_std] // don't link the Rust standard library
+#![no_main] // disable all Rust-level entry points
+
+use core::panic::PanicInfo;
+
+#[no_mangle] // don't mangle the name of this function
+pub extern "C" fn _start() -> ! {
+ // this function is the entry point, since the linker looks for a function
+ // named `_start` by default
+ loop {}
+}
+
+/// This function is called on panic.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+`Cargo.toml`:
+
+```toml
+[package]
+name = "crate_name"
+version = "0.1.0"
+authors = ["Author Name "]
+
+# the profile used for `cargo build`
+[profile.dev]
+panic = "abort" # disable stack unwinding on panic
+
+# the profile used for `cargo build --release`
+[profile.release]
+panic = "abort" # disable stack unwinding on panic
+```
+
+Чтобы собрать этот исполняемый файл, его надо скомпилировать для голого железа, например, `thumbv7em-none-eabihf`:
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+В качестве альтернативы, мы можем скомпилировать его для хост-системы, передав дополнительные аргументы компоновщика:
+
+```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"
+```
+
+Обратите внимание, что это лишь минимальный пример независимого бинарного файла Rust. Этот бинарник ожидает различных вещей, например, инициализацию стека при вызове функции `_start`. **Поэтому для любого реального использования такого бинарного файла потребуется совершить еще больше действий**.
+
+## Что дальше?
+
+В [следующем посте][next post] описаны шаги, необходимые для превращения нашего независимого бинарного файла в минимальное ядро операционной системы. Сюда входит создание custom target, объединение нашего исполняемого файла с загрузчиком и изучение, как вывести что-то на экран.
+
+[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.ru.md
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ru.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ru.md
new file mode 100644
index 000000000..05fa97e14
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ru.md
@@ -0,0 +1,29 @@
++++
+title = "Отключение красной зоны"
+weight = 1
+path = "ru/red-zone"
+template = "edition-2/extra.html"
++++
+
+[Красная зона][red zone] — это оптимизация [System V ABI], которая позволяет функциям временно использовать 128 байт ниже своего стекового кадра без корректировки указателя стека:
+
+[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
+
+
+
+![stack frame with red zone](red-zone.svg)
+
+На рисунке показан стековый фрейм функции с `n` локальных переменных. При входе в функцию указатель стека корректируется, чтобы освободить место в стеке для адреса возврата и локальных переменных.
+
+Красная зона определяется как 128 байт ниже скорректированного указателя стека. Функция может использовать эту зону для временных данных, которые не нужны при всех вызовах функции. Таким образом, в некоторых случаях (например, в небольших листовых функциях) можно обойтись без двух инструкций для корректировки указателя стека.
+
+Однако такая оптимизация приводит к огромным проблемам при работе с исключениями или аппаратными прерываниями. Предположим, что во время использования функцией красной зоны происходит исключение:
+
+![red zone overwritten by exception handler](red-zone-overwrite.svg)
+
+Процессор и обработчик исключений перезаписывают данные в красной зоне. Но эти данные все еще нужны прерванной функции. Поэтому функция не будет работать правильно, когда мы вернемся из обработчика исключений. Это может привести к странным ошибкам, на отладку которых [уйдут недели][take weeks to debug].
+
+[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
+
+Чтобы избежать подобных ошибок при реализации обработки исключений в будущем, мы отключим красную зону с самого начала. Это достигается путем добавления строки `"disable-redzone": true` в наш целевой конфигурационный файл.
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ru.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ru.md
new file mode 100644
index 000000000..8a1feaa1c
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ru.md
@@ -0,0 +1,44 @@
++++
+title = "Отключение SIMD"
+weight = 2
+path = "ru/disable-simd"
+template = "edition-2/extra.html"
++++
+
+Инструкции [Single Instruction Multiple Data (SIMD)] способны выполнять операцию (например, сложение) одновременно над несколькими словами данных, что может значительно ускорить работу программ. Архитектура `x86_64` поддерживает различные стандарты SIMD:
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+
+
+- [MMX]: Набор инструкций _Multi Media Extension_ был представлен в 1997 году и определяет восемь 64-битных регистров, называемых `mm0` - `mm7`. Эти регистры являются псевдонимами регистров [x87 блока с плавающей запятой][x87 floating point unit].
+- [SSE]: Набор инструкций _Streaming SIMD Extensions_ был представлен в 1999 году. Вместо повторного использования регистров с плавающей запятой он добавляет совершенно новый набор регистров. Шестнадцать новых регистров называются `xmm0` - `xmm15` и имеют размер 128 бит каждый.
+- [AVX]: _Advanced Vector Extensions_ - это расширения, которые еще больше увеличивают размер мультимедийных регистров. Новые регистры называются `ymm0` - `ymm15` и имеют размер 256 бит каждый. Они расширяют регистры `xmm`, поэтому, например, `xmm0` - это нижняя половина `ymm0`.
+
+[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
+[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
+[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
+[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
+
+Используя такие стандарты SIMD, программы часто могут значительно ускориться. Хорошие компиляторы способны автоматически преобразовывать обычные циклы в такой SIMD-код с помощью процесса, называемого [автовекторизацией][auto-vectorization].
+
+[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
+
+Однако большие регистры SIMD приводят к проблемам в ядрах ОС. Причина в том, что ядро должно создавать резервные копии всех регистров, которые оно использует, в память при каждом аппаратном прерывании, потому что они должны иметь свои первоначальные значения, когда прерванная программа продолжает работу. Поэтому, если ядро использует SIMD-регистры, ему приходится резервировать гораздо больше данных (512-1600 байт), что заметно снижает производительность. Чтобы избежать этого снижения производительности, мы хотим отключить функции `sse` и `mmx` (функция `avx` отключена по умолчанию).
+
+Мы можем сделать это через поле `features` в нашей целевой спецификации. Чтобы отключить функции `mmx` и `sse`, мы добавим их с минусом:
+
+```json
+"features": "-mmx,-sse"
+```
+
+## Числа с плавающей точкой
+К сожалению для нас, архитектура `x86_64` использует регистры SSE для операций с числами с плавающей точкой. Таким образом, каждое использование чисел с плавающей точкой с отключенным SSE вызовёт ошибку в LLVM. Проблема в том, что библиотека `core` уже использует числа с плавающей точкой (например, в ней реализованы трейты для `f32` и `f64`), поэтому недостаточно избегать чисел с плавающей точкой в нашем ядре.
+
+К счастью, LLVM поддерживает функцию `soft-float`, эмулирующую все операции с числавами с плавающей точкой через программные функции, основанные на обычных целых числах. Это позволяет использовать плавающие числа в нашем ядре без SSE, просто это будет немного медленнее.
+
+Чтобы включить функцию `soft-float` для нашего ядра, мы добавим ее в строку `features` в спецификации цели с префиксом плюс:
+
+```json
+"features": "-mmx,-sse,+soft-float"
+```
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md
new file mode 100644
index 000000000..dc27b1834
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md
@@ -0,0 +1,501 @@
++++
+title = "Минимально возможное ядро на Rust"
+weight = 2
+path = "ru/minimal-rust-kernel"
+date = 2018-02-10
+
+[extra]
+chapter = "С нуля"
+translators = ["MrZloHex"]
++++
+
+В этом посте мы создадим минимальное 64-битное ядро на Rust для архитектуры x86_64. Мы будем отталкиваться от [независимого бинарного файла][freestanding Rust binary] из предыдущего поста для создания загрузочного образа диска, который может что-то выводить на экран.
+
+[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.ru.md
+
+
+Этот блог открыто разрабатывается на [GitHub]. Если у вас возникли какие-либо проблемы или вопросы, пожалуйста, создайте _issue_. Также вы можете оставлять комментарии [в конце страницы][at the bottom]. Полный исходный код для этого поста вы можете найти в репозитории в ветке [`post-02`][post branch].
+
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-02
+
+
+
+## Последовательность процессов запуска
+
+Когда вы включаете компьютер, он начинает выполнять код микропрограммы, который хранится в [ПЗУ][ROM] материнской платы. Этот код выполняет [самотестирование при включении][power-on self-test], определяет доступную оперативную память и выполняет предварительную инициализацию процессора и аппаратного обеспечения. После этого он ищет загрузочный диск и начинает загрузку ядра операционной системы.
+
+[ROM]: https://en.wikipedia.org/wiki/Read-only_memory
+[power-on self-test]: https://en.wikipedia.org/wiki/Power-on_self-test
+
+Для архитектуры x86 существует два стандарта прошивки: “Basic Input/Output System“ ("Базовая система ввода/вывода" **[BIOS]**) и более новый “Unified Extensible Firmware Interface” ("Унифицированный расширяемый интерфейс прошивки" **[UEFI]**). Стандарт BIOS - старый, но простой и хорошо поддерживаемый на любой машине x86 с 1980-х годов. UEFI, напротив, более современный и имеет гораздо больше возможностей, но более сложен в настройке (по крайней мере, на мой взгляд).
+
+[BIOS]: https://en.wikipedia.org/wiki/BIOS
+[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
+
+В данный момент, мы обеспечиваем поддержку только BIOS, но планируется поддержка и UEFI. Если вы хотите помочь нам в этом, обратитесь к [Github issue](https://github.com/phil-opp/blog_os/issues/349).
+
+## Запуск BIOS
+
+Почти все системы x86 имеют поддержку загрузки BIOS, включая более новые машины на базе UEFI, которые используют эмулированный BIOS. Это замечательно, потому что вы можете использовать одну и ту же логику загрузки на всех машинах из прошлых веков. Но такая широкая совместимость одновременно является и самым большим недостатком загрузки BIOS, поскольку это означает, что перед загрузкой процессор переводится в 16-битный режим совместимости под названием [реальный режим], чтобы архаичные загрузчики 1980-х годов все еще работали.
+
+Но давайте начнем с самого начала:
+
+Когда вы включаете компьютер, он загружает BIOS из специальной флэш-памяти, расположенной на материнской плате. BIOS запускает процедуры самодиагностики и инициализации оборудования, затем ищет загрузочные диски. Если он находит такой, управление передается _загрузчику_, который представляет собой 512-байтовую порцию исполняемого кода, хранящуюся в начале диска. Большинство загрузчиков имеют размер более 512 байт, поэтому загрузчики обычно разделяются на небольшой первый этап, который помещается в 512 байт, и второй этап, который впоследствии загружается первым этапом.
+
+Загрузчик должен определить расположение образа ядра на диске и загрузить его в память. Он также должен переключить процессор из 16-битного [реального режима][real mode] сначала в 32-битный [защищенный режим][protected mode], а затем в 64-битный [длинный режим][long mode], где доступны 64-битные регистры и вся основная память. Третья задача - запросить определенную информацию (например, карту памяти) у BIOS и передать ее ядру ОС.
+
+[real mode]: https://en.wikipedia.org/wiki/Real_mode
+[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
+[long mode]: https://en.wikipedia.org/wiki/Long_mode
+[memory segmentation]: https://en.wikipedia.org/wiki/X86_memory_segmentation
+
+Написание загрузчика немного громоздко, поскольку требует использования языка ассемблера и множества неинтересных действий, таких как "запишите это магическое значение в этот регистр процессора". Поэтому мы не рассматриваем создание загрузчика в этом посте и вместо этого предоставляем инструмент под названием [bootimage], который автоматически добавляет загрузчик к вашему ядру.
+
+[bootimage]: https://github.com/rust-osdev/bootimage
+
+Если вы заинтересованы в создании собственного загрузчика: Оставайтесь с нами, набор постов на эту тему уже запланирован!
+
+#### Стандарт Multiboot
+
+Чтобы избежать того, что каждая операционная система реализует свой собственный загрузчик, который совместим только с одной ОС, [Free Software Foundation] в 1995 году создал открытый стандарт загрузчика под названием [Multiboot]. Стандарт определяет интерфейс между загрузчиком и операционной системой, так что любой совместимый с Multiboot загрузчик может загружать любую совместимую с Multiboot операционную систему. Эталонной реализацией является [GNU GRUB], который является самым популярным загрузчиком для систем 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
+
+Чтобы сделать ядро совместимым с Multiboot, нужно просто вставить так называемый [Multiboot заголовок][Multiboot header] в начало файла ядра. Это делает загрузку ОС в GRUB очень простой. Однако у GRUB и стандарта Multiboot есть и некоторые проблемы:
+
+[Multiboot header]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#OS-image-format
+
+- Они поддерживают только 32-битный защищенный режим. Это означает, что для перехода на 64-битный длинный режим необходимо выполнить конфигурацию процессора.
+- Они предназначены для того, чтобы упростить загрузчик вместо ядра. Например, ядро должно быть связано с [скорректированным размером страницы по умолчанию][adjusted default page size], потому что иначе GRUB не сможет найти заголовок Multiboot. Другой пример - [информация запуска][boot information], которая передается ядру, содержит множество структур, зависящих от архитектуры, вместо того, чтобы предоставлять чистые абстракции.
+- И GRUB, и стандарт Multiboot документированы очень скудно.
+- GRUB должен быть установлен на хост-системе, чтобы создать загрузочный образ диска из файла ядра. Это усложняет разработку под Windows или Mac.
+
+[adjusted default page size]: https://wiki.osdev.org/Multiboot#Multiboot_2
+[boot information]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+
+Из-за этих недостатков мы решили не использовать GRUB или стандарт Multiboot. Однако мы планируем добавить поддержку Multiboot в наш инструмент [bootimage], чтобы можно было загружать ваше ядро и на системе GRUB. Если вы заинтересованы в написании ядра, совместимого с Multiboot, ознакомьтесь с [первым выпуском][first edition] этой серии блогов.
+
+[first edition]: @/edition-1/_index.md
+
+### UEFI
+
+(На данный момент мы не предоставляем поддержку UEFI, но мы бы хотели! Если вы хотите помочь, пожалуйста, сообщите нам об этом в [Github issue](https://github.com/phil-opp/blog_os/issues/349).)
+
+## Минимально возможное ядро
+
+Теперь, когда мы примерно знаем, как запускается компьютер, пришло время создать собственное минимально возможное ядро. Наша цель - создать образ диска, который при загрузке выводит на экран "Hello World!". Для этого мы будем используем [Независимый бинарный файл на Rust][freestanding Rust binary] из предыдущего поста.
+
+Как вы помните, мы собирали независимый бинарный файл с помощью `cargo`, но в зависимости от операционной системы нам требовались разные имена точек входа и флаги компиляции. Это потому, что `cargo` по умолчанию компилирует для _хостовой системы_, то есть системы, на которой вы работаете. Это не то, что мы хотим для нашего ядра, потому что ядро, работающее поверх, например, Windows, не имеет особого смысла. Вместо этого мы хотим компилировать для четко определенной _целевой системы_.
+
+### Установка Rust Nightly
+
+Rust имеет три релизных канала: _stable_, _beta_ и _nightly_. В книге Rust Book очень хорошо объясняется разница между этими каналами, поэтому уделите минуту и [ознакомьтесь с ней](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains). Для создания операционной системы нам понадобятся некоторые экспериментальные возможности, которые доступны только на канале nightly, поэтому нам нужно установить nightly версию Rust.
+
+Для управления установками Rust я настоятельно рекомендую [rustup]. Он позволяет устанавливать nightly, beta и stable компиляторы рядом друг с другом и облегчает их обновление. С помощью rustup вы можете использовать nightly компилятор для текущего каталога, выполнив команду `rustup override set nightly`. В качестве альтернативы вы можете добавить файл `rust-toolchain` с содержимым `nightly` в корневой каталог проекта. Вы можете проверить, установлена ли у вас версия nightly, выполнив команду `rustc --version`: Номер версии должен содержать `-nightly` в конце.
+
+[rustup]: https://www.rustup.rs/
+
+Nightly версия компилятора позволяет нам подключать различные экспериментальные возможности с помощью так называемых _флагов_ в верхней части нашего файла. Например, мы можем включить экспериментальный [макрос `asm!``asm!` macro] для встроенного ассемблера, добавив `#![feature(asm)]` в начало нашего `main.rs`. Обратите внимание, что такие экспериментальные возможности совершенно нестабильны, что означает, что будущие версии Rust могут изменить или удалить их без предварительного предупреждения. По этой причине мы будем использовать их только в случае крайней необходимости.
+
+[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+
+### Спецификация целевой платформы
+
+Cargo поддерживает различные целевые системы через параметр `--target`. Цель описывается так называемой тройкой _[target triple]_, которая описывает архитектуру процессора, производителя, операционную систему и [ABI]. Например, тройка целей `x86_64-unknown-linux-gnu` описывает систему с процессором `x86_64`, неизвестным поставщиком и операционной системой Linux с GNU ABI. Rust поддерживает [множество различных целевых троек][many different target triples] [поддержка платформы][platform-support], включая `arm-linux-androideabi` для Android или [`wasm32-unknown-unknown` для 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
+
+Однако для нашей целевой системы нам требуются некоторые специальные параметры конфигурации (например, отсутствие базовой ОС), поэтому ни одна из [существующих целевых троек][platform-support] не подходит. К счастью, Rust позволяет нам определить [custom target][custom-targets] через JSON-файл. Например, JSON-файл, описывающий цель `x86_64-unknown-linux-gnu`, выглядит следующим образом:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-linux-gnu",
+ "data-layout": "e-m:e-i64:64-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
+}
+```
+
+Большинство полей требуется LLVM для генерации кода для данной платформы. Например, поле [`data-layout`] определяет размер различных типов целых чисел, чисел с плавающей точкой и указателей. Затем есть поля, которые Rust использует для условной компиляции, такие как `target-pointer-width`. Третий вид полей определяет, как должен быть собран крейт. Например, поле `pre-link-args` определяет аргументы, передаваемые [компоновщику][linker].
+
+[`data-layout`]: https://llvm.org/docs/LangRef.html#data-layout
+[linker]: https://en.wikipedia.org/wiki/Linker_(computing)
+
+Для нашего ядра тоже нужна архитектура `x86_64`, поэтому наша спецификация цели будет очень похожа на приведенную выше. Начнем с создания файла `x86_64-blog_os.json` (выберите любое имя, которое вам нравится) с общим содержанием:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-i64:64-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
+}
+```
+
+Обратите внимание, что мы изменили ОС в поле `llvm-target` и `os` на `none`, потому что мы будем работать на голом железе.
+
+Добавляем дополнительные параметры для сборки ядра:
+
+```json
+"linker-flavor": "ld.lld",
+"linker": "rust-lld",
+```
+
+Вместо того чтобы использовать компоновщик по умолчанию платформы (который может не поддерживать цели Linux), мы используем кроссплатформенный компоновщик [LLD], поставляемый вместе с Rust, для компоновки нашего ядра.
+
+[LLD]: https://lld.llvm.org/
+
+```json
+"panic-strategy": "abort",
+```
+
+Этот параметр указывает, что цель не поддерживает [раскрутку стека][stack unwinding] при панике, поэтому вместо этого программа должна прерваться напрямую. Это имеет тот же эффект, что и опция `panic = "abort"` в нашем Cargo.toml, поэтому мы можем удалить ее оттуда. (Обратите внимание, что в отличие от опции Cargo.toml, эта опция также будет применяться, когда мы перекомпилируем библиотеку `core` позже в этом посте. Поэтому не забудьте добавить эту опцию, даже если вы предпочтете оставить опцию в Cargo.toml).
+
+[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
+
+```json
+"disable-redzone": true,
+```
+
+Мы пишем ядро, поэтому в какой-то момент нам понадобится обрабатывать прерывания. Чтобы сделать это безопасно, мы должны отключить определенную оптимизацию указателя стека, называемую _"красной зоной"_, поскольку в противном случае она приведет к повреждениям стека. Для получения дополнительной информации см. нашу отдельную статью об [отключении красной зоны][disabling the red zone].
+
+[disabling the red zone]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ru.md
+
+```json
+"features": "-mmx,-sse,+soft-float",
+```
+
+Поле `features` включает/выключает функции целевой платформы. Мы отключаем функции `mmx` и `sse`, добавляя к ним минус, и включаем функцию `soft-float`, добавляя к ней плюс. Обратите внимание, что между разными флагами не должно быть пробелов, иначе LLVM не сможет интерпретировать строку features.
+
+Функции `mmx` и `sse` определяют поддержку инструкций [Single Instruction Multiple Data (SIMD)], которые часто могут значительно ускорить работу программ. Однако использование больших регистров SIMD в ядрах ОС приводит к проблемам с производительностью. Причина в том, что ядру необходимо восстановить все регистры в исходное состояние перед продолжением прерванной программы. Это означает, что ядро должно сохранять полное состояние SIMD в основной памяти при каждом системном вызове или аппаратном прерывании. Поскольку состояние SIMD очень велико (512-1600 байт), а прерывания могут происходить очень часто, эти дополнительные операции сохранения/восстановления значительно снижают производительность. Чтобы избежать этого, мы отключили SIMD для нашего ядра (не для приложений, работающих поверх него!).
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+Проблема с отключением SIMD заключается в том, что операции с числами с плавающей точкой на `x86_64` по умолчанию требуют регистров SIMD. Чтобы решить эту проблему, мы добавили функцию `soft-float`, которая эмулирует все операции с числами с плавающей точкой через программные функции, основанные на обычных целых числах.
+
+Для получения дополнительной информации см. наш пост об [отключении SIMD](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ru.md).
+
+#### Соединяем все вместе
+
+Наша спецификация целовой платформы выглядит следующим образом:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-i64:64-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"
+}
+```
+
+### Компиляция ядра
+
+Компиляция для нашей новой целевой платформы будет использовать соглашения Linux (я не совсем уверен почему — предполагаю, что это просто поведение LLVM по умолчанию). Это означает, что нам нужна точка входа с именем `_start`, как описано в [предыдущем посте][previous post]:
+
+[previous post]: @/edition-2/posts/01-freestanding-rust-binary/index.ru.md
+
+```rust
+// src/main.rs
+
+#![no_std] // don't link the Rust standard library
+#![no_main] // disable all Rust-level entry points
+
+use core::panic::PanicInfo;
+
+/// This function is called on panic.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+
+#[no_mangle] // don't mangle the name of this function
+pub extern "C" fn _start() -> ! {
+ // this function is the entry point, since the linker looks for a function
+ // named `_start` by default
+ loop {}
+}
+```
+
+Обратите внимание, что точка входа должна называться `_start` независимо от используемой вами ОС.
+
+Теперь мы можем собрать ядро для нашей новой цели, передав имя файла JSON в качестве `--target`:
+
+```
+> cargo build --target x86_64-blog_os.json
+
+error[E0463]: can't find crate for `core`
+```
+
+Не получается! Ошибка сообщает нам, что компилятор Rust больше не может найти [библиотеку `core`][`core` library]. Эта библиотека содержит основные типы Rust, такие как `Result`, `Option` и итераторы, и неявно связана со всеми `no_std` модулями.
+
+[`core` library]: https://doc.rust-lang.org/nightly/core/index.html
+
+Проблема в том, что корневая (`core`) библиотека распространяется вместе с компилятором Rust как _прекомпилированная_ библиотека. Поэтому она действительна только для поддерживаемых тройных хостов (например, `x86_64-unknown-linux-gnu`), но не для нашей пользовательской целевой платформы. Если мы хотим скомпилировать код для других целевых платформ, нам нужно сначала перекомпилировать `core` для этих целей.
+
+### Функция `build-std`
+
+Вот тут-то и приходит на помощь функция [`build-std`][`build-std` feature] в cargo. Она позволяет перекомпилировать `core` и другие стандартные библиотеки по требованию, вместо того, чтобы использовать предварительно скомпилированные версии, поставляемые вместе с установкой Rust. Эта функция очень новая и еще не закончена, поэтому она помечена как "нестабильная" и доступна только на [nightly Rust].
+
+[`build-std` feature]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
+[nightly Rust]: #установка-rust-nightly
+
+Чтобы использовать эту функцию, нам нужно создать файл [конфигурации cargo][cargo configuration] по пути `.cargo/config.toml` со следующим содержимым:
+
+[cargo configuration]: https://doc.rust-lang.org/cargo/reference/config.html
+
+
+```toml
+# in .cargo/config.toml
+
+[unstable]
+build-std = ["core", "compiler_builtins"]
+```
+
+Это говорит cargo, что он должен перекомпилировать библиотеки `core` и `compiler_builtins`. Последняя необходима, поскольку `core` зависит от неё. Чтобы перекомпилировать эти библиотеки, cargo нужен доступ к исходному коду rust, который мы можем установить с помощью команды `rustup component add rust-src`.
+
+
+
+**Note:** Ключ конфигурации `unstable.build-std` требует как минимум Rust nightly от 2020-07-15.
+
+
+
+```
+> 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
+```
+
+Мы видим, что `cargo build` теперь перекомпилирует библиотеки `core`, `rustc-std-workspace-core` (зависимость от `compiler_builtins`) и `compiler_builtins` для нашей пользовательской целевой платформы.
+
+#### Внутренние функции, работающие с памятью
+
+Компилятор Rust предполагает, что определенный набор встроенных функций доступен для всех систем. Большинство этих функций обеспечивается модулем `compiler_builtins`, который мы только что перекомпилировали. Однако в этом модуле есть некоторые функции, связанные с памятью, которые не включены по умолчанию, потому что они обычно предоставляются библиотекой C в системе. Эти функции включают `memset`, которая устанавливает все байты в блоке памяти в заданное значение, `memcpy`, которая копирует один блок памяти в другой, и `memcmp`, которая сравнивает два блока памяти. Хотя ни одна из этих функций нам сейчас не понадобилась для компиляции нашего ядра, они потребуются, как только мы добавим в него дополнительный код (например, при копировании структур).
+
+Поскольку мы не можем ссылаться на С библиотеку хостовой операционной системы, нам нужен альтернативный способ предоставления этих функций компилятору. Одним из возможных подходов для этого может быть реализация наших собственных функций `memset` и т.д. и применение к ним атрибута `#[no_mangle]` (чтобы избежать автоматического переименования во время компиляции). Однако это опасно, поскольку малейшая ошибка в реализации этих функций может привести к неопределенному поведению. Например, при реализации `memcpy` с помощью цикла `for` вы можете получить бесконечную рекурсию, поскольку циклы `for` неявно вызывают метод трейта [`IntoIterator::into_iter`], который может снова вызвать `memcpy`. Поэтому хорошей идеей будет повторное использование существующих, хорошо протестированных реализаций.
+
+[`IntoIterator::into_iter`]: https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html#tymethod.into_iter
+
+К счастью, модуль `compiler_builtins` уже содержит реализации всех необходимых функций, они просто отключены по умолчанию, чтобы не столкнуться с реализациями из С библиотеки. Мы можем включить их, установив флаг cargo [`build-std-features`] на `["compiler-builtins-mem"]`. Как и флаг `build-std`, этот флаг может быть передан в командной строке как флаг `-Z` или настроен в таблице `unstable` в файле `.cargo/config.toml`. Поскольку мы всегда хотим собирать с этим флагом, вариант с конфигурационным файлом имеет для нас больше смысла:
+
+[`build-std-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std-features
+
+```toml
+# in .cargo/config.toml
+
+[unstable]
+build-std-features = ["compiler-builtins-mem"]
+build-std = ["core", "compiler_builtins"]
+```
+
+(Поддержка функции `compiler-builtins-mem` была [добавлена совсем недавно](https://github.com/rust-lang/rust/pull/77284), поэтому для нее вам нужен как минимум Rust nightly `2020-09-30`).
+
+За кулисами этот флаг включает функцию [`mem`][`mem` feature] крейта `compiler_builtins`. Это приводит к тому, что атрибут `#[no_mangle]` применяется к [реализациям `memcpy` и т.п.][`memcpy` etc. implementations] из этого крейта, что делает их доступными для компоновщика.
+
+[`mem` feature]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L54-L55
+[`memcpy` etc. implementations]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69
+
+Благодаря этому изменению наше ядро имеет валидные реализации для всех функций, требуемых компилятором, поэтому оно будет продолжать компилироваться, даже если наш код станет сложнее.
+
+#### Переопределение цели по умолчанию
+
+Чтобы избежать передачи параметра `--target` при каждом вызове `cargo build`, мы можем переопределить цель по умолчанию. Для этого мы добавим следующее в наш файл [конфигураций cargo][cargo configuration] по пути `.cargo/config.toml`:
+
+```toml
+# in .cargo/config.toml
+
+[build]
+target = "x86_64-blog_os.json"
+```
+
+С этой конфигурацией `cargo` будет использовать нашу цель `x86_64-blog_os.json`, если не передан явный аргумент `--target`. Это означает, что теперь мы можем собрать наше ядро с помощью простой `cargo build`. Чтобы узнать больше о параметрах конфигурации cargo, ознакомьтесь с [официальной документацией][cargo configuration].
+
+Теперь мы можем скомпилировать наше ядро под голое железо с помощью простой `cargo build`. Однако наша точка входа `_start`, которая будет вызываться загрузчиком, все еще пуста. Пришло время вывести что-нибудь на экран.
+
+### Вывод на экран
+
+Самым простым способом печати текста на экран на данном этапе является [текстовый буфер VGA][VGA text buffer]. Это специальная область памяти, сопоставленная с аппаратным обеспечением VGA, которая содержит содержимое, отображаемое на экране. Обычно он состоит из 25 строк, каждая из которых содержит 80 символьных ячеек. Каждая символьная ячейка отображает символ ASCII с некоторыми цветами переднего и заднего плана. Вывод на экран выглядит следующим образом:
+
+[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
+
+![screen output for common ASCII characters](https://upload.wikimedia.org/wikipedia/commons/f/f8/Codepage-437.png)
+
+Точную разметку буфера VGA мы обсудим в следующем посте, где мы напишем первый небольшой драйвер для него. Для печати "Hello World!" нам достаточно знать, что буфер расположен по адресу `0xb8000` и что каждая символьная ячейка состоит из байта ASCII и байта цвета.
+
+Реализация выглядит следующим образом:
+
+```rust
+static HELLO: &[u8] = b"Hello World!";
+
+#[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 {}
+}
+```
+
+Сначала мы приводим целое число `0xb8000` к [сырому указателю][raw pointer]. Затем мы [итерируем][iterate] по байтам [статической][static] [байтовой строки][byte string] `HELLO`. Мы используем метод [`enumerate`], чтобы дополнительно получить бегущую переменную `i`. В теле цикла for мы используем метод [`offset`] для записи байта строки и соответствующего байта цвета (`0xb` - светло-голубой).
+
+[iterate]: 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
+[raw pointer]: 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
+
+Обратите внимание, что вокруг всех записей в память стоит блок [`unsafe`]. Причина в том, что компилятор Rust не может доказать, что сырые указатели, которые мы создаем, действительны. Они могут указывать куда угодно и привести к повреждению данных. Помещая их в блок `unsafe`, мы, по сути, говорим компилятору, что абсолютно уверены в правильности операций. Обратите внимание, что блок `unsafe` не отключает проверки безопасности Rust. Он только позволяет вам делать [пять дополнительных вещей][five additional things].
+
+[`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html
+[five additional things]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers
+
+Я хочу подчеркнуть, что **это не тот способ, которым стоит что-либо делать в Rust!** Очень легко ошибиться при работе с сырыми указателями внутри блоков `unsafe`: например, мы можем легко записать за конец буфера, если не будем осторожны.
+
+Поэтому мы хотим минимизировать использование `unsafe` настолько, насколько это возможно. Rust дает нам возможность сделать это путем создания безопасных абстракций. Например, мы можем создать тип буфера VGA, который инкапсулирует всю небезопасность и гарантирует, что извне _невозможно_ сделать что-либо неправильно. Таким образом, нам понадобится лишь минимальное количество блоков `unsafe` и мы можем быть уверены, что не нарушаем [безопасность памяти][memory safety]. Мы создадим такую безопасную абстракцию буфера VGA в следующем посте.
+
+[memory safety]: https://en.wikipedia.org/wiki/Memory_safety
+
+## Запуск ядра
+
+Теперь, когда у нас есть исполняемый файл, который делает что-то ощутимое, пришло время запустить его. Сначала нам нужно превратить наше скомпилированное ядро в загрузочный образ диска, связав его с загрузчиком. Затем мы можем запустить образ диска в виртуальной машине [QEMU] или загрузить его на реальном оборудовании с помощью USB-носителя.
+
+### Создание загрузочного образа
+
+Чтобы превратить наше скомпилированное ядро в загрузочный образ диска, нам нужно связать его с загрузчиком. Как мы узнали в [разделе о загрузке], загрузчик отвечает за инициализацию процессора и загрузку нашего ядра.
+
+[разделе о загрузке]: #последовательность-процессов-запуска
+
+Вместо того чтобы писать собственный загрузчик, который является самостоятельным проектом, мы используем модуль [`bootloader`]. Этот модуль реализует базовый BIOS-загрузчик без каких-либо C-зависимостей, только Rust и встроенный ассемблер. Чтобы использовать его для загрузки нашего ядра, нам нужно добавить зависимость от него:
+
+[`bootloader`]: https://crates.io/crates/bootloader
+
+```toml
+# in Cargo.toml
+
+[dependencies]
+bootloader = "0.9.8"
+```
+
+Добавление загрузчика в качестве зависимости недостаточно для создания загрузочного образа диска. Проблема в том, что нам нужно связать наше ядро с загрузчиком после компиляции, но в cargo нет поддержки [скриптов после сборки][post-build scripts].
+
+[post-build scripts]: https://github.com/rust-lang/cargo/issues/545
+
+Для решения этой проблемы мы создали инструмент `bootimage`, который сначала компилирует ядро и загрузчик, а затем соединяет их вместе для создания загрузочного образа диска. Чтобы установить инструмент, выполните следующую команду в терминале:
+
+```
+cargo install bootimage
+```
+
+Для запуска `bootimage` и сборки загрузчика вам необходимо установить компонент rustup `llvm-tools-preview`. Это можно сделать, выполнив команду `rustup component add llvm-tools-preview`.
+
+После установки `bootimage` и добавления компонента `llvm-tools-preview` мы можем создать образ загрузочного диска, выполнив команду:
+
+```
+> cargo bootimage
+```
+
+Мы видим, что инструмент перекомпилирует наше ядро с помощью `cargo build`, поэтому он автоматически подхватит все внесенные вами изменения. После этого он компилирует загрузчик, что может занять некоторое время. Как и все зависимости модулей, он собирается только один раз, а затем кэшируется, поэтому последующие сборки будут происходить гораздо быстрее. Наконец, `bootimage` объединяет загрузчик и ваше ядро в загрузочный образ диска.
+
+После выполнения команды вы должны увидеть загрузочный образ диска с именем `bootimage-blog_os.bin` в каталоге `target/x86_64-blog_os/debug`. Вы можете загрузить его в виртуальной машине или скопировать на USB-накопитель, чтобы загрузить его на реальном оборудовании. (Обратите внимание, что это не образ CD, который имеет другой формат, поэтому запись на CD не работает).
+
+#### Как этот работает?
+
+Инструмент `bootimage` выполняет следующие действия за кулисами:
+
+- Компилирует наше ядро в файл [ELF].
+- Компилирует зависимость загрузчика как отдельный исполняемый файл.
+- Он связывает байты ELF-файла ядра с загрузчиком.
+
+[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+[rust-osdev/bootloader]: https://github.com/rust-osdev/bootloader
+
+При запуске загрузчик читает и разбирает приложенный файл ELF. Затем он сопоставляет сегменты программы с виртуальными адресами в таблицах страниц, обнуляет секцию `.bss` и устанавливает стек. Наконец, он считывает адрес точки входа (наша функция `_start`) и переходит к ней.
+
+### Запуск через QEMU
+
+Теперь мы можем загрузить образ диска в виртуальной машине. Чтобы загрузить его в [QEMU], выполните следующую команду:
+
+[QEMU]: https://www.qemu.org/
+
+```
+> qemu-system-x86_64 -drive format=raw,file=target/x86_64-blog_os/debug/bootimage-blog_os.bin
+warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
+```
+
+Откроется отдельное окно, которое выглядит следующим образом:
+
+![QEMU showing "Hello World!"](qemu.png)
+
+Мы видим, что наш "Hello World!" отображается на экране.
+
+### Настоящая машина
+
+Также можно записать его на USB-накопитель и загрузить на реальной машине:
+
+```
+> dd if=target/x86_64-blog_os/debug/bootimage-blog_os.bin of=/dev/sdX && sync
+```
+
+Где `sdX` - имя устройства вашего USB-накопителя. **Внимательно проверьте**, что вы выбрали правильное имя устройства, потому что все, что находится на этом устройстве, будет перезаписано.
+
+После записи образа на USB-накопитель его можно запустить на реальном оборудовании, загрузившись с него. Для загрузки с USB-накопителя вам, вероятно, потребуется использовать специальное меню загрузки или изменить порядок загрузки в конфигурации BIOS. Обратите внимание, что в настоящее время это не работает на машинах с UEFI, так как модуль `bootloader` пока не имеет поддержки UEFI.
+
+### Использование `cargo run`
+
+Чтобы облегчить запуск нашего ядра в QEMU, мы можем установить ключ конфигурации `runner` для cargo:
+
+```toml
+# in .cargo/config.toml
+
+[target.'cfg(target_os = "none")']
+runner = "bootimage runner"
+```
+
+Таблица `target.'cfg(target_os = "none")'` применяется ко всем целям, которые установили поле `"os"` своего конфигурационного файла цели на `"none"`. Это включает нашу цель `x86_64-blog_os.json`. Ключ `runner` указывает команду, которая должна быть вызвана для `cargo run`. Команда запускается после успешной сборки с путем к исполняемому файлу, переданному в качестве первого аргумента. Более подробную информацию смотрите в [документации по cargo][cargo configuration].
+
+Команда `bootimage runner` специально разработана для использования в качестве исполняемого файла `runner`. Она связывает заданный исполняемый файл с зависимостью загрузчика проекта, а затем запускает QEMU. Более подробную информацию и возможные варианты конфигурации смотрите в [Readme of `bootimage`].
+
+[Readme of `bootimage`]: https://github.com/rust-osdev/bootimage
+
+Теперь мы можем использовать `cargo run` для компиляции нашего ядра и его загрузки в QEMU.
+
+## Что дальше?
+
+В следующем посте мы более подробно рассмотрим текстовый буфер VGA и напишем безопасный интерфейс для него. Мы также добавим поддержку макроса `println`.
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.ru.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.ru.md
new file mode 100644
index 000000000..ce1f65921
--- /dev/null
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.ru.md
@@ -0,0 +1,64 @@
++++
+title = "Текстовый режим VGA"
+weight = 2
+path = "ru/vga-text-mode"
+date = 2018-02-26
+
+[extra]
+chapter = "С нуля"
+translators = ["MrZloHex"]
++++
+
+[Текстовый режим VGA][VGA text mode] — это простой способ вывода текста на экран. В этом посте мы создадим интерфейс, который делает его использование безопасным и простым, инкапсулируя все уязвимости в отдельный модуль. Мы также реализуем поддержку [макросов форматирования][formatting macros] языка Rust.
+
+[VGA text mode]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
+[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
+
+
+
+Этот блог открыто разрабатывается на [GitHub]. Если у вас возникли какие-либо проблемы или вопросы, пожалуйста, создайте _issue_. Также вы можете оставлять комментарии [в конце страницы][at the bottom]. Полный исходный код для этого поста вы можете найти в репозитории в ветке [`post-03`][post branch].
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-03
+
+
+
+## Текстовый буфер VGA
+
+Чтобы вывести символ на экран в текстовом режиме VGA, необходимо записать его в текстовый буфер аппаратного обеспечения VGA. Текстовый буфер VGA представляет собой двумерный массив с типичными 25 строками и 80 столбцами, который непосредственно выводится на экран. Каждая запись массива описывает один экранный символ в следующем формате:
+
+ Биты | Значения
+------ | --------------------
+0-7 | ASCII Кодовая точка
+8-11 | Цвет переднего плана
+12-14 | Цвет фона
+15 | Мигание
+
+Первый байт представляет символ, который должен быть напечатан в [кодировке ASCII][ASCII encoding]. Точнее говоря, это не совсем ASCII, а набор символов [_кодовая страница 437_][_code page 437_] с некоторыми дополнительными символами и небольшими модификациями. Для простоты в этой заметке мы будем называть его символом ASCII.
+
+[ASCII encoding]: https://en.wikipedia.org/wiki/ASCII
+[_code page 437_]: https://en.wikipedia.org/wiki/Code_page_437
+
+Второй байт определяет способ отображения символа. Первые четыре бита определяют цвет переднего плана, следующие три бита — цвет фона, а последний бит — должен ли символ мигать. Доступны следующие цвета:
+
+Номер | Цвет | Номер + Бит Яркости | Яркий Цвет
+------ | ------------ | ------------------- | -------------
+0x0 | Черный | 0x8 | Темно-серый
+0x1 | Синий | 0x9 | Светло-синий
+0x2 | Зеленый | 0xa | Светло-зеленый
+0x3 | Голубой | 0xb | Светло-голубой
+0x4 | Красный | 0xc | Светло-красный
+0x5 | Пурпурный | 0xd | Розовый
+0x6 | Коричневый | 0xe | Желтый
+0x7 | Светло-серый | 0xf | Белый
+
+Бит 4 — это _бит яркости_, который превращает, например, синий цвет в светло-синий. Для цвета фона этот бит перепрофилируется в бит мигания.
+
+Текстовый буфер VGA доступен через [отображение ввода-вывода на памяти][memory-mapped I/O] по адресу `0xb8000`. Это означает, что при чтении и записи по этому адресу доступ осуществляется не к оперативной памяти, а непосредственно к текстовому буферу VGA. Это означает, что мы можем читать и записывать его через обычные операции с памятью по этому адресу.
+
+[memory-mapped I/O]: https://en.wikipedia.org/wiki/Memory-mapped_I/O
+
+Обратите внимание, что оборудование с отображением на память может не поддерживать все обычные операции с оперативной памятью. Например, устройство может поддерживать только побайтовое чтение и возвращать мусор при чтении `u64`. К счастью, текстовый буфер [поддерживает обычное чтение и запись][supports normal reads and writes], так что нам не придется обращаться с ним особым образом.
+
+[supports normal reads and writes]: https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vgamem.htm#manip
\ No newline at end of file
diff --git a/blog/content/edition-2/posts/_index.ru.md b/blog/content/edition-2/posts/_index.ru.md
new file mode 100644
index 000000000..c7079c408
--- /dev/null
+++ b/blog/content/edition-2/posts/_index.ru.md
@@ -0,0 +1,7 @@
++++
+title = "Posts"
+sort_by = "weight"
+insert_anchor_links = "left"
+render = false
+page_template = "edition-2/page.html"
++++