# SOME DESCRIPTIVE TITLE # Copyright (C) YEAR The FreeBSD Project # This file is distributed under the same license as the FreeBSD Documentation package. # Vladlen Popolitov , 2025, 2026. msgid "" msgstr "" "Project-Id-Version: FreeBSD Documentation VERSION\n" "POT-Creation-Date: 2025-11-08 16:17+0000\n" "PO-Revision-Date: 2026-03-05 04:45+0000\n" "Last-Translator: Vladlen Popolitov \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.17\n" #. type: Title = #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:14 #, no-wrap msgid "Bootstrapping and Kernel Initialization" msgstr "Начальная загрузка и инициализация ядра" #. type: YAML Front Matter: title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1 #, no-wrap msgid "Chapter 1. Bootstrapping and Kernel Initialization" msgstr "Глава 1. Начальная загрузка и инициализация ядра" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:52 #, no-wrap msgid "Synopsis" msgstr "Обзор" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:57 msgid "" "This chapter is an overview of the boot and system initialization processes, " "starting from the BIOS (firmware) POST, to the first user process creation. " "Since the initial steps of system startup are very architecture dependent, " "the IA-32 architecture is used as an example. But the AMD64 and ARM64 " "architectures are much more important and compelling examples and should be " "explained in the near future according to the topic of this document." msgstr "" "Эта глава представляет собой обзор процессов загрузки и инициализации " "системы, начиная с POST в BIOS (микропрограмме) и заканчивая созданием " "первого пользовательского процесса. Поскольку начальные этапы загрузки " "системы сильно зависят от архитектуры, в качестве примера используется " "архитектура IA-32. Однако архитектуры AMD64 и ARM64 гораздо важнее и " "интереснее, и их следует рассмотреть в ближайшем будущем в соответствии с " "темой этого документа." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:61 msgid "" "The FreeBSD boot process can be surprisingly complex. After control is " "passed from the BIOS, a considerable amount of low-level configuration must " "be done before the kernel can be loaded and executed. This setup must be " "done in a simple and flexible manner, allowing the user a great deal of " "customization possibilities." msgstr "" "Процесс загрузки FreeBSD может быть удивительно сложным. После передачи " "управления от BIOS необходимо выполнить значительный объём низкоуровневой " "настройки перед загрузкой и выполнением ядра. Эта настройка должна быть " "выполнена простым и гибким способом, предоставляя пользователю широкие " "возможности для настройки и адаптации." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:63 #, no-wrap msgid "Overview" msgstr "Обзор" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:76 msgid "" "The boot process is an extremely machine-dependent activity. Not only must " "code be written for every computer architecture, but there may also be " "multiple types of booting on the same architecture. For example, a " "directory listing of [.filename]#stand# reveals a great amount of " "architecture-dependent code. There is a directory for each of the various " "supported architectures. FreeBSD supports the CSM boot standard " "(Compatibility Support Module). So CSM is supported (with both GPT and MBR " "partitioning support) and UEFI booting (GPT is totally supported, MBR is " "mostly supported). It also supports loading files from ext2fs, MSDOS, UFS " "and ZFS. FreeBSD also supports the boot environment feature of ZFS which " "allows the HOST OS to communicate details about what to boot that go beyond " "a simple partition as was possible in the past. But UEFI is more relevant " "than the CSM these days. The example that follows shows booting an x86 " "computer from an MBR-partitioned hard drive with the FreeBSD [." "filename]#boot0# multi-boot loader stored in the very first sector. That " "boot code starts the FreeBSD three-stage boot process." msgstr "" "Процесс загрузки — это операция, крайне зависимая от оборудования. Не только " "для каждой архитектуры компьютера должен быть написан код, но также могут " "существовать различные типы загрузки в рамках одной архитектуры. Например, " "список файлов в каталоге [.filename]#stand# показывает большое количество " "кода, зависящего от архитектуры. Для каждой из поддерживаемых архитектур " "существует отдельный каталог. FreeBSD поддерживает стандарт загрузки CSM " "(Compatibility Support Module). Таким образом, CSM поддерживается (как с " "GPT, так и с MBR разметкой), а также загрузка через UEFI (GPT полностью " "поддерживается, MBR — в основном). Также поддерживается загрузка файлов с " "ext2fs, MSDOS, UFS и ZFS. FreeBSD поддерживает функцию загрузочного " "окружения ZFS, которая позволяет основной ОС передавать детали о том, что " "загружать, выходящие за рамки простого раздела, как это было возможно ранее. " "Однако в наши дни UEFI более актуален, чем CSM. В следующем примере показана " "загрузка компьютера x86 с жёсткого диска с MBR-разметкой, где используется " "мультизагрузчик FreeBSD [.filename]#boot0#, сохранённый в самом первом " "секторе. Этот загрузочный код запускает трёхэтапный процесс загрузки FreeBSD." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:82 msgid "" "The key to understanding this process is that it is a series of stages of " "increasing complexity. These stages are [.filename]#boot1#, [." "filename]#boot2#, and [.filename]#loader# (see man:boot[8] for more " "detail). The boot system executes each stage in sequence. The last stage, " "[.filename]#loader#, is responsible for loading the FreeBSD kernel. Each " "stage is examined in the following sections." msgstr "" "Ключ к пониманию этого процесса заключается в том, что он состоит из " "последовательных стадий возрастающей сложности. Эти стадии — [." "filename]#boot1#, [.filename]#boot2# и [.filename]#loader# (подробнее см. " "man:boot[8]). Система загрузки выполняет каждую стадию последовательно. " "Последняя стадия, [.filename]#loader#, отвечает за загрузку ядра FreeBSD. " "Каждая стадия рассматривается в следующих разделах." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:85 msgid "" "Here is an example of the output generated by the different boot stages. " "Actual output may differ from machine to machine:" msgstr "" "Вот пример вывода, сгенерированного на различных этапах загрузки. " "Фактический вывод может отличаться в зависимости от машины:" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:91 #, no-wrap msgid "*FreeBSD Component*" msgstr "*Компонент FreeBSD*" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:93 #, no-wrap msgid "*Output (may vary)*" msgstr "*Вывод (может отличаться)*" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:94 #, no-wrap msgid "`boot0`" msgstr "`boot0`" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:103 #, no-wrap msgid "" "[source,bash]\n" "....\n" "F1 FreeBSD\n" "F2 BSD\n" "F5 Disk 2\n" "...." msgstr "" "[source,bash]\n" "....\n" "F1 FreeBSD\n" "F2 BSD\n" "F5 Disk 2\n" "...." #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:104 #, no-wrap msgid "`boot2` footnote:[This prompt will appear if the user presses a key just after selecting an OS to boot at the boot0 stage.]" msgstr "`boot2` footnote:[Это приглашение появится, если пользователь нажмет клавишу сразу после выбора ОС для загрузки на этапе boot0.]" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:113 #, no-wrap msgid "" "[source,bash]\n" "....\n" ">>FreeBSD/x86 BOOT\n" "Default: 0:ad(0p4)/boot/loader\n" "boot:\n" "...." msgstr "" "[source,bash]\n" "....\n" ">>FreeBSD/x86 BOOT\n" "Default: 0:ad(0p4)/boot/loader\n" "boot:\n" "...." #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:114 #, no-wrap msgid "[.filename]#loader#" msgstr "[.filename]#loader#" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:130 #, no-wrap msgid "" "[source,bash]\n" "....\n" "BTX loader 1.00 BTX version is 1.02\n" "Consoles: internal video/keyboard\n" "BIOS drive C: is disk0\n" "BIOS 639kB/2096064kB available memory\n" "\n" "FreeBSD/x86 bootstrap loader, Revision 1.1\n" "Console internal video/keyboard\n" "(root@releng1.nyi.freebsd.org, Fri Apr 9 04:04:45 UTC 2021)\n" "Loading /boot/defaults/loader.conf\n" "/boot/kernel/kernel text=0xed9008 data=0x117d28+0x176650 syms=[0x8+0x137988+0x8+0x1515f8]\n" "...." msgstr "" "[source,bash]\n" "....\n" "BTX loader 1.00 BTX version is 1.02\n" "Consoles: internal video/keyboard\n" "BIOS drive C: is disk0\n" "BIOS 639kB/2096064kB available memory\n" "\n" "FreeBSD/x86 bootstrap loader, Revision 1.1\n" "Console internal video/keyboard\n" "(root@releng1.nyi.freebsd.org, Fri Apr 9 04:04:45 UTC 2021)\n" "Loading /boot/defaults/loader.conf\n" "/boot/kernel/kernel text=0xed9008 data=0x117d28+0x176650 syms=[0x8+0x137988+0x8+0x1515f8]\n" "...." #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:131 #, no-wrap msgid "kernel" msgstr "ядро системы" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:144 #, no-wrap msgid "" "[source,bash]\n" "....\n" "Copyright (c) 1992-2021 The FreeBSD Project.\n" "Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994\n" " The Regents of the University of California. All rights reserved.\n" "FreeBSD is a registered trademark of The FreeBSD Foundation.\n" "FreeBSD 13.0-RELEASE 0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:04:45 UTC 2021\n" " root@releng1.nyi.freebsd.org:/usr/obj/usr/src/i386.i386/sys/GENERIC i386\n" "FreeBSD clang version 11.0.1 (git@github.com:llvm/llvm-project.git llvmorg-11.0.1-0-g43ff75f2c3fe)\n" "...." msgstr "" "[source,bash]\n" "....\n" "Copyright (c) 1992-2021 The FreeBSD Project.\n" "Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994\n" " The Regents of the University of California. All rights reserved.\n" "FreeBSD is a registered trademark of The FreeBSD Foundation.\n" "FreeBSD 13.0-RELEASE 0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:04:45 UTC 2021\n" " root@releng1.nyi.freebsd.org:/usr/obj/usr/src/i386.i386/sys/GENERIC i386\n" "FreeBSD clang version 11.0.1 (git@github.com:llvm/llvm-project.git llvmorg-11.0.1-0-g43ff75f2c3fe)\n" "...." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:147 #, no-wrap msgid "The BIOS" msgstr "BIOS" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:158 msgid "" "When the computer powers on, the processor's registers are set to some " "predefined values. One of the registers is the _instruction pointer_ " "register, and its value after a power on is well defined: it is a 32-bit " "value of `0xfffffff0`. The instruction pointer register (also known as the " "Program Counter) points to code to be executed by the processor. Another " "important register is the `cr0` 32-bit control register, and its value just " "after a reboot is `0`. One of ``cr0``'s bits, the PE (Protection Enabled) " "bit, indicates whether the processor is running in 32-bit protected mode or " "16-bit real mode. Since this bit is cleared at boot time, the processor " "boots in 16-bit real mode. Real mode means, among other things, that linear " "and physical addresses are identical. The reason for the processor not to " "start immediately in 32-bit protected mode is backwards compatibility. In " "particular, the boot process relies on the services provided by the BIOS, " "and the BIOS itself works in legacy, 16-bit code." msgstr "" "При включении компьютера регистры процессора устанавливаются в некоторые " "предопределённые значения. Один из регистров — это регистр _указателя " "команд_, и его значение после включения питания чётко определено: это 32-" "битное значение `0xfffffff0`. Регистр указателя команд (также известный как " "Счётчик Команд) указывает на код, который должен быть выполнен процессором. " "Ещё один важный регистр — это 32-битный управляющий регистр `cr0`, и его " "значение сразу после перезагрузки равно `0`. Один из битов ``cr0``, бит PE " "(Protection Enabled, Защита Включена), указывает, работает ли процессор в 32-" "битном защищённом режиме или 16-битном реальном режиме. Поскольку этот бит " "сброшен при загрузке, процессор запускается в 16-битном реальном режиме. " "Реальный режим означает, среди прочего, что линейные и физические адреса " "идентичны. Причина, по которой процессор не запускается сразу в 32-битном " "защищённом режиме, — это обратная совместимость. В частности, процесс " "загрузки зависит от услуг, предоставляемых BIOS, а сам BIOS работает в " "устаревшем 16-битном коде." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:161 msgid "" "The value of `0xfffffff0` is slightly less than 4 GB, so unless the machine " "has 4 GB of physical memory, it cannot point to a valid memory address. The " "computer's hardware translates this address so that it points to a BIOS " "memory block." msgstr "" "Значение `0xfffffff0` немного меньше 4 ГБ, поэтому, если в машине нет 4 ГБ " "физической памяти, оно не может указывать на действительный адрес памяти. " "Аппаратное обеспечение компьютера преобразует этот адрес так, чтобы он " "указывал на блок памяти BIOS." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:166 msgid "" "The BIOS (Basic Input Output System) is a chip on the motherboard that has a " "relatively small amount of read-only memory (ROM). This memory contains " "various low-level routines that are specific to the hardware supplied with " "the motherboard. The processor will first jump to the address 0xfffffff0, " "which really resides in the BIOS's memory. Usually this address contains a " "jump instruction to the BIOS's POST routines." msgstr "" "BIOS (Basic Input Output System) — это микросхема на материнской плате, " "которая содержит относительно небольшой объём памяти только для чтения (ROM)" ". Эта память включает различные низкоуровневые процедуры, специфичные для " "оборудования, поставляемого с материнской платой. Процессор сначала " "переходит по адресу 0xfffffff0, который фактически находится в памяти BIOS. " "Обычно по этому адресу содержится инструкция перехода к процедурам POST BIOS." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:170 msgid "" "The POST (Power On Self Test) is a set of routines including the memory " "check, system bus check, and other low-level initialization so the CPU can " "set up the computer properly. The important step of this stage is " "determining the boot device. Modern BIOS implementations permit the " "selection of a boot device, allowing booting from a floppy, CD-ROM, hard " "disk, or other devices." msgstr "" "POST (Power On Self Test) — это набор процедур, включающих проверку памяти, " "проверку системной шины и другую низкоуровневую инициализацию, чтобы " "процессор мог правильно настроить компьютер. Важным этапом на этой стадии " "является определение загрузочного устройства. Современные реализации BIOS " "позволяют выбирать загрузочное устройство, обеспечивая загрузку с дискеты, " "CD-ROM, жёсткого диска или других устройств." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:179 msgid "" "The very last thing in the POST is the `INT 0x19` instruction. The `INT " "0x19` handler reads 512 bytes from the first sector of boot device into the " "memory at address `0x7c00`. The term _first sector_ originates from hard " "drive architecture, where the magnetic plate is divided into a number of " "cylindrical tracks. Tracks are numbered, and every track is divided into a " "number (usually 64) of sectors. Track numbers start at 0, but sector " "numbers start from 1. Track 0 is the outermost on the magnetic plate, and " "sector 1, the first sector, has a special purpose. It is also called the " "MBR, or Master Boot Record. The remaining sectors on the first track are " "never used." msgstr "" "Самым последним действием в POST является инструкция `INT 0x19`. Обработчик " "`INT 0x19` считывает 512 байт из первого сектора загрузочного устройства в " "память по адресу `0x7c00`. Термин _первый сектор_ происходит из архитектуры " "жёстких дисков, где магнитная пластина разделена на множество цилиндрических " "дорожек. Дорожки нумеруются, и каждая дорожка разделена на несколько (обычно " "64) секторов. Нумерация дорожек начинается с 0, но нумерация секторов " "начинается с 1. Дорожка 0 находится на внешней стороне магнитной пластины, а " "сектор 1, первый сектор, имеет особое назначение. Он также называется MBR " "(Master Boot Record) или Главная Загрузочная Запись. Остальные секторы на " "первой дорожке не используются." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:183 msgid "" "This sector is our boot-sequence starting point. As we will see, this " "sector contains a copy of our [.filename]#boot0# program. A jump is made by " "the BIOS to address `0x7c00` so it starts executing." msgstr "" "Этот сектор является нашей точкой входа в последовательность загрузки. Как " "мы увидим, этот сектор содержит копию нашей программы [.filename]#boot0#. " "BIOS выполняет переход по адресу `0x7c00`, и она начинает выполняться." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:185 #, no-wrap msgid "The Master Boot Record (`boot0`)" msgstr "Главная загрузочная запись (`boot0`)" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:194 msgid "" "After control is received from the BIOS at memory address `0x7c00`, [." "filename]#boot0# starts executing. It is the first piece of code under " "FreeBSD control. The task of [.filename]#boot0# is quite simple: scan the " "partition table and let the user choose which partition to boot from. The " "Partition Table is a special, standard data structure embedded in the MBR " "(hence embedded in [.filename]#boot0#) describing the four standard PC " "\"partitions\". [.filename]#boot0# resides in the filesystem as [." "filename]#/boot/boot0#. It is a small 512-byte file, and it is exactly what " "FreeBSD's installation procedure wrote to the hard disk's MBR if you chose " "the \"bootmanager\" option at installation time. Indeed, [.filename]#boot0# " "_is_ the MBR." msgstr "" "После получения управления от BIOS по адресу памяти `0x7c00` начинает " "выполняться [.filename]#boot0#. Это первый код, который управляется FreeBSD. " "Задача [.filename]#boot0# довольно проста: просканировать таблицу разделов и " "позволить пользователю выбрать, с какого раздела загружаться. Таблица " "разделов — это специальная стандартная структура данных, встроенная в MBR (а " "значит, и в [.filename]#boot0#), которая описывает четыре стандартных PC-" "раздела. [.filename]#boot0# находится в файловой системе как [.filename]#/" "boot/boot0#. Это небольшой файл размером 512 байт, и именно его процедура " "установки FreeBSD записывает в MBR жёсткого диска, если во время установки " "была выбрана опция \"bootmanager\". Действительно, [.filename]#boot0# _и " "есть_ MBR." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:197 msgid "" "As mentioned previously, we're calling the BIOS `INT 0x19` to load the MBR " "([.filename]#boot0#) into memory at address `0x7c00`. The source file for [." "filename]#boot0# can be found in [.filename]#stand/i386/boot0/boot0.S# - " "which is an awesome piece of code written by Robert Nordier." msgstr "" "Как упоминалось ранее, мы вызываем прерывание BIOS `INT 0x19` для загрузки " "MBR ([.filename]#boot0#) в память по адресу `0x7c00`. Исходный файл для [." "filename]#boot0# можно найти в [.filename]#stand/i386/boot0/boot0.S# — это " "впечатляющий фрагмент кода, написанный Робертом Нордье." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:202 msgid "" "A special structure starting from offset `0x1be` in the MBR is called the " "_partition table_. It has four records of 16 bytes each, called _partition " "records_, which represent how the hard disk is partitioned, or, in FreeBSD's " "terminology, sliced. One byte of those 16 says whether a partition (slice) " "is bootable or not. Exactly one record must have that flag set, otherwise [." "filename]#boot0#'s code will refuse to proceed." msgstr "" "Особая структура, начинающаяся со смещения `0x1be` в MBR, называется " "_таблицей разделов_. Она содержит четыре записи по 16 байт каждая, " "называемые _записями разделов_, которые определяют, как разделён жёсткий " "диск, или, в терминологии FreeBSD, нарезан. Один из этих 16 байт указывает, " "является ли раздел (срез) загрузочным или нет. Ровно одна запись должна быть " "с этом установленным флагом, иначе код [.filename]#boot0# откажется " "продолжать работу." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:204 msgid "A partition record has the following fields:" msgstr "Запись о разделе содержит следующие поля:" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:206 msgid "the 1-byte filesystem type" msgstr "1-байтовый тип файловой системы" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:207 msgid "the 1-byte bootable flag" msgstr "1-байтовый флаг загрузки (`bootable`)" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:208 msgid "the 6 byte descriptor in CHS format" msgstr "6-байтовый дескриптор в формате CHS" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:209 msgid "the 8 byte descriptor in LBA format" msgstr "8-байтовый дескриптор в формате LBA" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:213 msgid "" "A partition record descriptor contains information about where exactly the " "partition resides on the drive. Both descriptors, LBA and CHS, describe the " "same information, but in different ways: LBA (Logical Block Addressing) has " "the starting sector for the partition and the partition's length, while CHS " "(Cylinder Head Sector) has coordinates for the first and last sectors of the " "partition. The partition table ends with the special signature `0xaa55`." msgstr "" "Дескриптор записи раздела содержит информацию о том, где именно раздел " "расположен на диске. Оба дескриптора, LBA и CHS, описывают одну и ту же " "информацию, но разными способами: LBA (Logical Block Addressing) содержит " "начальный сектор раздела и его длину, тогда как CHS (Cylinder Head Sector) " "содержит координаты первого и последнего секторов раздела. Таблица разделов " "завершается специальной сигнатурой `0xaa55`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:218 msgid "" "The MBR must fit into 512 bytes, a single disk sector. This program uses " "low-level \"tricks\" like taking advantage of the side effects of certain " "instructions and reusing register values from previous operations to make " "the most out of the fewest possible instructions. Care must also be taken " "when handling the partition table, which is embedded in the MBR itself. For " "these reasons, be very careful when modifying [.filename]#boot0.S#." msgstr "" "MBR должен помещаться в 512 байт, один сектор диска. Эта программа " "использует низкоуровневые «трюки», такие как использование побочных эффектов " "определённых инструкций и повторное использование значений регистров из " "предыдущих операций, чтобы максимально эффективно использовать минимально " "возможное количество инструкций. Также необходимо соблюдать осторожность при " "работе с таблицей разделов, которая встроена в сам MBR. По этим причинам " "будьте очень внимательны при изменении [.filename]#boot0.S#." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:223 msgid "" "Note that the [.filename]#boot0.S# source file is assembled \"as is\": " "instructions are translated one by one to binary, with no additional " "information (no ELF file format, for example). This kind of low-level " "control is achieved at link time through special control flags passed to the " "linker. For example, the text section of the program is set to be located " "at address `0x600`. In practice this means that [.filename]#boot0# must be " "loaded to memory address `0x600` in order to function properly." msgstr "" "Обратите внимание, что исходный файл [.filename]#boot0.S# ассемблируется " "\"как есть\": инструкции переводятся одна за одной в бинарный код без " "дополнительной информации (например, без формата файла ELF). Такой " "низкоуровневый контроль достигается на этапе компоновки с помощью " "специальных флагов, передаваемых компоновщику. Например, текстовая секция " "программы располагается по адресу `0x600`. На практике это означает, что [." "filename]#boot0# должен быть загружен в память по адресу `0x600` для " "корректной работы." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:230 msgid "" "It is worth looking at the [.filename]#Makefile# for [.filename]#boot0# ([." "filename]#stand/i386/boot0/Makefile#), as it defines some of the run-time " "behavior of [.filename]#boot0#. For instance, if a terminal connected to " "the serial port (COM1) is used for I/O, the macro `SIO` must be defined (`-" "DSIO`). `-DPXE` enables boot through PXE by pressing kbd:[F6]. " "Additionally, the program defines a set of _flags_ that allow further " "modification of its behavior. All of this is illustrated in the [." "filename]#Makefile#. For example, look at the linker directives which " "command the linker to start the text section at address `0x600`, and to " "build the output file \"as is\" (strip out any file formatting):" msgstr "" "Стоит взглянуть на [.filename]#Makefile# для [.filename]#boot0# ([." "filename]#stand/i386/boot0/Makefile#), так как он определяет некоторые " "аспекты поведения [.filename]#boot0# во время выполнения. Например, если для " "ввода-вывода используется терминал, подключённый к последовательному порту " "(COM1), необходимо определить макрос `SIO` (`-DSIO`). `-DPXE` включает " "загрузку через PXE при нажатии kbd:[F6]. Кроме того, программа определяет " "набор _флагов_, которые позволяют дополнительно настроить её поведение. Всё " "это проиллюстрировано в [.filename]#Makefile#. Например, обратите внимание " "на директивы компоновщика, которые предписывают ему начинать секцию текста с " "адреса `0x600` и создавать выходной файл \"как есть\" (удаляя любое " "форматирование файла):" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:235 #, no-wrap msgid "" " BOOT_BOOT0_ORG?=0x600\n" " ORG=${BOOT_BOOT0_ORG}\n" msgstr "" " BOOT_BOOT0_ORG?=0x600\n" " ORG=${BOOT_BOOT0_ORG}\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:237 #, no-wrap msgid "[.filename]#stand/i386/boot0/Makefile# [[boot-boot0-makefile-as-is]]" msgstr "[.filename]#stand/i386/boot0/Makefile# [[boot-boot0-makefile-as-is]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:239 msgid "" "Let us now start our study of the MBR, or [.filename]#boot0#, starting where " "execution begins." msgstr "" "Приступим к изучению MBR, или [.filename]#boot0#, начиная с точки входа." #. type: delimited block = 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:245 msgid "" "Some modifications have been made to some instructions in favor of better " "exposition. For example, some macros are expanded, and some macro tests are " "omitted when the result of the test is known. This applies to all of the " "code examples shown." msgstr "" "В некоторые инструкции были внесены изменения для лучшего изложения. " "Например, некоторые макросы раскрыты, а некоторые проверки макросов опущены, " "когда результат проверки известен. Это относится ко всем приведённым " "примерам кода." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:256 #, no-wrap msgid "" "start:\n" " cld\t\t\t# String ops inc\n" " xorw %ax,%ax\t\t# Zero\n" " movw %ax,%es\t\t# Address\n" " movw %ax,%ds\t\t# data\n" " movw %ax,%ss\t\t# Set up\n" " movw $LOAD,%sp\t\t# stack\n" msgstr "" "start:\n" " cld\t\t\t# String ops inc\n" " xorw %ax,%ax\t\t# Zero\n" " movw %ax,%es\t\t# Address\n" " movw %ax,%ds\t\t# data\n" " movw %ax,%ss\t\t# Set up\n" " movw $LOAD,%sp\t\t# stack\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:258 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-entrypoint]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-entrypoint]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:264 msgid "" "This first block of code is the entry point of the program. It is where the " "BIOS transfers control. First, it makes sure that the string operations " "autoincrement its pointer operands (the `cld` instruction) footnote:[When in " "doubt, we refer the reader to the official Intel manuals, which describe the " "exact semantics for each instruction.]. Then, as it makes no assumption " "about the state of the segment registers, it initializes them. Finally, it " "sets the stack pointer register (`%sp`) to ($LOAD = address `0x7c00`), so we " "have a working stack." msgstr "" "Этот первый блок кода является точкой входа программы. Именно сюда BIOS " "передаёт управление. Сначала он гарантирует, что строковые операции " "автоматически увеличивают указатели операндов (инструкция `cld`) footnote:[В " "случае сомнений мы отсылаем читателя к официальным руководствам Intel, где " "описана точная семантика каждой инструкции.]. Затем, не делая предположений " "о состоянии сегментных регистров, он их инициализирует. Наконец, он " "устанавливает регистр указателя стека (`%sp`) в ($LOAD = адрес `0x7c00`), " "чтобы обеспечить работоспособный стек." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:266 msgid "" "The next block is responsible for the relocation and subsequent jump to the " "relocated code." msgstr "" "Следующий блок отвечает за перемещение и последующий переход к перемещенному " "коду." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:280 #, no-wrap msgid "" " movw %sp,%si # Source\n" " movw $start,%di\t\t# Destination\n" " movw $0x100,%cx\t\t# Word count\n" " rep\t\t\t# Relocate\n" " movsw\t\t\t# code\n" " movw %di,%bp\t\t# Address variables\n" " movb $0x8,%cl\t\t# Words to clear\n" " rep\t\t\t# Zero\n" " stosw\t\t\t# them\n" " incb -0xe(%di)\t\t# Set the S field to 1\n" " jmp main-LOAD+ORIGIN\t# Jump to relocated code\n" msgstr "" " movw %sp,%si # Source\n" " movw $start,%di\t\t# Destination\n" " movw $0x100,%cx\t\t# Word count\n" " rep\t\t\t# Relocate\n" " movsw\t\t\t# code\n" " movw %di,%bp\t\t# Address variables\n" " movb $0x8,%cl\t\t# Words to clear\n" " rep\t\t\t# Zero\n" " stosw\t\t\t# them\n" " incb -0xe(%di)\t\t# Set the S field to 1\n" " jmp main-LOAD+ORIGIN\t# Jump to relocated code\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:282 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-relocation]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-relocation]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:293 msgid "" "As [.filename]#boot0# is loaded by the BIOS to address `0x7C00`, it copies " "itself to address `0x600` and then transfers control there (recall that it " "was linked to execute at address `0x600`). The source address, `0x7c00`, is " "copied to register `%si`. The destination address, `0x600`, to register `" "%di`. The number of words to copy, `256` (the program's size = 512 bytes), " "is copied to register `%cx`. Next, the `rep` instruction repeats the " "instruction that follows, that is, `movsw`, the number of times dictated by " "the `%cx` register. The `movsw` instruction copies the word pointed to by `" "%si` to the address pointed to by `%di`. This is repeated another 255 " "times. On each repetition, both the source and destination registers, `%si` " "and `%di`, are incremented by one. Thus, upon completion of the 256-word " "(512-byte) copy, `%di` has the value `0x600`+`512`= `0x800`, and `%si` has " "the value `0x7c00`+`512`= `0x7e00`; we have thus completed the code " "_relocation_. Since the last update of this document, the copy instructions " "have changed in the code, so instead of the movsb and stosb, movsw and stosw " "have been introduced, which copy 2 bytes(1 word) in one iteration." msgstr "" "Так как [.filename]#boot0# загружается BIOS по адресу `0x7C00`, он копирует " "себя по адресу `0x600` и передаёт управление туда (напомним, что он был " "слинкован для выполнения по адресу `0x600`). Исходный адрес, `0x7c00`, " "копируется в регистр `%si`. Конечный адрес, `0x600`, — в регистр `%di`. " "Количество слов для копирования, `256` (размер программы = 512 байт), " "копируется в регистр `%cx`. Далее инструкция `rep` повторяет следующую за " "ней инструкцию, то есть `movsw`, количество раз, указанное в регистре `%cx`. " "Инструкция `movsw` копирует слово, на которое указывает `%si`, по адресу, на " "который указывает `%di`. Это повторяется ещё 255 раз. При каждом повторении " "оба регистра, исходный и конечный, `%si` и `%di`, увеличиваются на единицу. " "Таким образом, по завершении копирования 256 слов (512 байт), `%di` имеет " "значение `0x600`+`512`= `0x800`, а `%si` — значение `0x7c00`+`512`= " "`0x7e00`; таким образом, мы завершили _перемещение_ кода. С момента " "последнего обновления этого документа инструкции копирования в коде " "изменились, поэтому вместо movsb и stosb были введены movsw и stosw, которые " "копируют 2 байта (1 слово) за одну итерацию." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:304 msgid "" "Next, the destination register `%di` is copied to `%bp`. `%bp` gets the " "value `0x800`. The value `8` is copied to `%cl` in preparation for a new " "string operation (like our previous `movsw`). Now, `stosw` is executed 8 " "times. This instruction copies a `0` value to the address pointed to by the " "destination register (`%di`, which is `0x800`), and increments it. This is " "repeated another 7 times, so `%di` ends up with value `0x810`. Effectively, " "this clears the address range `0x800`-`0x80f`. This range is used as a " "(fake) partition table for writing the MBR back to disk. Finally, the " "sector field for the CHS addressing of this fake partition is given the " "value 1 and a jump is made to the main function from the relocated code. " "Note that until this jump to the relocated code, any reference to an " "absolute address was avoided." msgstr "" "Затем регистр назначения `%di` копируется в `%bp`. `%bp` получает значение " "`0x800`. Значение `8` копируется в `%cl` для подготовки новой строковой " "операции (как в предыдущей `movsw`). Теперь `stosw` выполняется 8 раз. Эта " "инструкция копирует значение `0` по адресу, на который указывает регистр " "назначения (`%di`, то есть `0x800`), и увеличивает его. Это повторяется ещё " "7 раз, так что `%di` в итоге получает значение `0x810`. Фактически это " "очищает диапазон адресов `0x800`-`0x80f`. Этот диапазон используется как " "(фиктивная) таблица разделов для записи MBR обратно на диск. Наконец, полю " "сектора для CHS-адресации этого фиктивного раздела присваивается значение 1, " "и выполняется переход к основной функции из перемещённого кода. Обратите " "внимание, что до этого перехода к перемещённому коду любые ссылки на " "абсолютные адреса избегались." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:306 msgid "" "The following code block tests whether the drive number provided by the BIOS " "should be used, or the one stored in [.filename]#boot0#." msgstr "" "Следующий блок кода проверяет, следует ли использовать номер диска, " "предоставленный BIOS, или тот, что хранится в [.filename]#boot0#." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:318 #, no-wrap msgid "" "main:\n" " testb $SETDRV,_FLAGS(%bp)\t# Set drive number?\n" "#ifndef CHECK_DRIVE\t/* disable drive checks */\n" " jz save_curdrive\t\t# no, use the default\n" "#else\n" " jnz disable_update\t# Yes\n" " testb %dl,%dl\t\t# Drive number valid?\n" " js save_curdrive\t\t# Possibly (0x80 set)\n" "#endif\n" msgstr "" "main:\n" " testb $SETDRV,_FLAGS(%bp)\t# Set drive number?\n" "#ifndef CHECK_DRIVE\t/* disable drive checks */\n" " jz save_curdrive\t\t# no, use the default\n" "#else\n" " jnz disable_update\t# Yes\n" " testb %dl,%dl\t\t# Drive number valid?\n" " js save_curdrive\t\t# Possibly (0x80 set)\n" "#endif\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:320 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-drivenumber]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-drivenumber]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:327 msgid "" "This code tests the `SETDRV` bit (`0x20`) in the _flags_ variable. Recall " "that register `%bp` points to address location `0x800`, so the test is done " "to the _flags_ variable at address `0x800`-`69`= `0x7bb`. This is an " "example of the type of modifications that can be done to [." "filename]#boot0#. The `SETDRV` flag is not set by default, but it can be " "set in the [.filename]#Makefile#. When set, the drive number stored in the " "MBR is used instead of the one provided by the BIOS. We assume the " "defaults, and that the BIOS provided a valid drive number, so we jump to " "`save_curdrive`." msgstr "" "Этот код проверяет бит `SETDRV` (`0x20`) в переменной _flags_. Напомним, что " "регистр `%bp` указывает на адрес `0x800`, поэтому проверка выполняется для " "переменной _flags_ по адресу `0x800`-`69`= `0x7bb`. Это пример типа " "изменений, которые можно внести в [.filename]#boot0#. Флаг `SETDRV` не " "установлен по умолчанию, но его можно задать в [.filename]#Makefile#. Если " "он установлен, используется номер диска, сохранённый в MBR, вместо " "предоставленного BIOS. Мы предполагаем значения по умолчанию и то, что BIOS " "предоставил корректный номер диска, поэтому переходим к `save_curdrive`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:329 msgid "" "The next block saves the drive number provided by the BIOS, and calls `putn` " "to print a new line on the screen." msgstr "" "Следующий блок сохраняет номер диска, предоставленный BIOS, и вызывает " "`putn` для вывода новой строки на экран." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:341 #, no-wrap msgid "" "save_curdrive:\n" " movb %dl, (%bp)\t\t# Save drive number\n" " pushw %dx\t\t\t# Also in the stack\n" "#ifdef\tTEST\t/* test code, print internal bios drive */\n" " rolb $1, %dl\n" " movw $drive, %si\n" " call putkey\n" "#endif\n" " callw putn\t\t# Print a newline\n" msgstr "" "save_curdrive:\n" " movb %dl, (%bp)\t\t# Save drive number\n" " pushw %dx\t\t\t# Also in the stack\n" "#ifdef\tTEST\t/* test code, print internal bios drive */\n" " rolb $1, %dl\n" " movw $drive, %si\n" " call putkey\n" "#endif\n" " callw putn\t\t# Print a newline\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:343 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-savedrivenumber]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-savedrivenumber]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:345 msgid "" "Note that we assume `TEST` is not defined, so the conditional code in it is " "not assembled and will not appear in our executable [.filename]#boot0#." msgstr "" "Обратите внимание, что мы предполагаем, что `TEST` не определён, поэтому " "условный код в нём не собирается и не появится в нашем исполняемом файле [." "filename]#boot0#." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:351 msgid "" "Our next block implements the actual scanning of the partition table. It " "prints to the screen the partition type for each of the four entries in the " "partition table. It compares each type with a list of well-known operating " "system file systems. Examples of recognized partition types are NTFS " "(Windows(R), ID 0x7), `ext2fs` (Linux(R), ID 0x83), and, of course, `ffs`/" "`ufs2` (FreeBSD, ID 0xa5). The implementation is fairly simple." msgstr "" "Следующий блок реализует фактическое сканирование таблицы разделов. Он " "выводит на экран тип раздела для каждой из четырёх записей в таблице " "разделов. Каждый тип сравнивается со списком известных файловых систем " "операционных систем. Примерами распознаваемых типов разделов являются NTFS " "(Windows(R), ID 0x7), `ext2fs` (Linux(R), ID 0x83) и, конечно же, `ffs`/" "`ufs2` (FreeBSD, ID 0xa5). Реализация довольно проста." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:356 #, no-wrap msgid "" " movw $(partbl+0x4),%bx\t# Partition table (+4)\n" " xorw %dx,%dx\t\t# Item number\n" msgstr "" " movw $(partbl+0x4),%bx\t# Partition table (+4)\n" " xorw %dx,%dx\t\t# Item number\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:372 #, no-wrap msgid "" "read_entry:\n" " movb %ch,-0x4(%bx)\t# Zero active flag (ch == 0)\n" " btw %dx,_FLAGS(%bp)\t# Entry enabled?\n" " jnc next_entry\t\t# No\n" " movb (%bx),%al\t\t# Load type\n" " test %al, %al\t\t# skip empty partition\n" " jz next_entry\n" " movw $bootable_ids,%di\t# Lookup tables\n" " movb $(TLEN+1),%cl\t# Number of entries\n" " repne\t\t\t# Locate\n" " scasb\t\t\t# type\n" " addw $(TLEN-1), %di\t# Adjust\n" " movb (%di),%cl\t\t# Partition\n" " addw %cx,%di\t\t# description\n" " callw putx\t\t# Display it\n" msgstr "" "read_entry:\n" " movb %ch,-0x4(%bx)\t# Zero active flag (ch == 0)\n" " btw %dx,_FLAGS(%bp)\t# Entry enabled?\n" " jnc next_entry\t\t# No\n" " movb (%bx),%al\t\t# Load type\n" " test %al, %al\t\t# skip empty partition\n" " jz next_entry\n" " movw $bootable_ids,%di\t# Lookup tables\n" " movb $(TLEN+1),%cl\t# Number of entries\n" " repne\t\t\t# Locate\n" " scasb\t\t\t# type\n" " addw $(TLEN-1), %di\t# Adjust\n" " movb (%di),%cl\t\t# Partition\n" " addw %cx,%di\t\t# description\n" " callw putx\t\t# Display it\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:377 #, no-wrap msgid "" "next_entry:\n" " incw %dx\t\t\t# Next item\n" " addb $0x10,%bl\t\t# Next entry\n" " jnc read_entry\t\t# Till done\n" msgstr "" "next_entry:\n" " incw %dx\t\t\t# Next item\n" " addb $0x10,%bl\t\t# Next entry\n" " jnc read_entry\t\t# Till done\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:379 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-partition-scan]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-partition-scan]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:383 msgid "" "It is important to note that the active flag for each entry is cleared, so " "after the scanning, _no_ partition entry is active in our memory copy of [." "filename]#boot0#. Later, the active flag will be set for the selected " "partition. This ensures that only one active partition exists if the user " "chooses to write the changes back to disk." msgstr "" "Важно отметить, что флаг активности для каждой записи сбрасывается, поэтому " "после сканирования _ни одна_ запись о разделе не активна в нашей копии [." "filename]#boot0# в памяти. Позже флаг активности будет установлен для " "выбранного раздела. Это гарантирует, что только один активный раздел " "существует, если пользователь решит записать изменения обратно на диск." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:388 msgid "" "The next block tests for other drives. At startup, the BIOS writes the " "number of drives present in the computer to address `0x475`. If there are " "any other drives present, [.filename]#boot0# prints the current drive to " "screen. The user may command [.filename]#boot0# to scan partitions on " "another drive later." msgstr "" "Следующий блок проверяет наличие других дисков. При запуске BIOS записывает " "количество дисков, присутствующих в компьютере, по адресу `0x475`. Если есть " "другие диски, [.filename]#boot0# выводит текущий диск на экран. Пользователь " "может позже дать команду [.filename]#boot0# просканировать разделы на другом " "диске." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:397 #, no-wrap msgid "" " popw %ax\t\t\t# Drive number\n" " subb $0x80-0x1,%al\t\t# Does next\n" " cmpb NHRDRV,%al\t\t# drive exist? (from BIOS?)\n" " jb print_drive\t\t# Yes\n" " decw %ax\t\t\t# Already drive 0?\n" " jz print_prompt\t\t# Yes\n" msgstr "" " popw %ax\t\t\t# Drive number\n" " subb $0x80-0x1,%al\t\t# Does next\n" " cmpb NHRDRV,%al\t\t# drive exist? (from BIOS?)\n" " jb print_drive\t\t# Yes\n" " decw %ax\t\t\t# Already drive 0?\n" " jz print_prompt\t\t# Yes\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:399 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-test-drives]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-test-drives]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:402 msgid "" "We make the assumption that a single drive is present, so the jump to " "`print_drive` is not performed. We also assume nothing strange happened, so " "we jump to `print_prompt`." msgstr "" "Мы предполагаем, что присутствует только один диск, поэтому переход к " "`print_drive` не выполняется. Также мы предполагаем, что ничего необычного " "не произошло, поэтому переходим к `print_prompt`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:404 msgid "" "This next block just prints out a prompt followed by the default option:" msgstr "" "Следующий блок просто выводит приглашение с последующим вариантом по " "умолчанию:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:414 #, no-wrap msgid "" "print_prompt:\n" " movw $prompt,%si\t\t# Display\n" " callw putstr\t\t# prompt\n" " movb _OPT(%bp),%dl\t# Display\n" " decw %si\t\t\t# default\n" " callw putkey\t\t# key\n" " jmp start_input\t\t# Skip beep\n" msgstr "" "print_prompt:\n" " movw $prompt,%si\t\t# Display\n" " callw putstr\t\t# prompt\n" " movb _OPT(%bp),%dl\t# Display\n" " decw %si\t\t\t# default\n" " callw putkey\t\t# key\n" " jmp start_input\t\t# Skip beep\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:416 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-prompt]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-prompt]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:418 msgid "" "Finally, a jump is performed to `start_input`, where the BIOS services are " "used to start a timer and for reading user input from the keyboard; if the " "timer expires, the default option will be selected:" msgstr "" "Наконец, выполняется переход к `start_input`, где используются сервисы BIOS " "для запуска таймера и чтения пользовательского ввода с клавиатуры; если " "таймер истекает, будет выбран вариант по умолчанию:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:434 #, no-wrap msgid "" "start_input:\n" " xorb %ah,%ah\t\t# BIOS: Get\n" " int $0x1a\t\t\t# system time\n" " movw %dx,%di\t\t# Ticks when\n" " addw _TICKS(%bp),%di\t# timeout\n" "read_key:\n" " movb $0x1,%ah\t\t# BIOS: Check\n" " int $0x16\t\t\t# for keypress\n" " jnz got_key\t\t# Have input\n" " xorb %ah,%ah\t\t# BIOS: int 0x1a, 00\n" " int $0x1a\t\t\t# get system time\n" " cmpw %di,%dx\t\t# Timeout?\n" " jb read_key\t\t# No\n" msgstr "" "start_input:\n" " xorb %ah,%ah\t\t# BIOS: Get\n" " int $0x1a\t\t\t# system time\n" " movw %dx,%di\t\t# Ticks when\n" " addw _TICKS(%bp),%di\t# timeout\n" "read_key:\n" " movb $0x1,%ah\t\t# BIOS: Check\n" " int $0x16\t\t\t# for keypress\n" " jnz got_key\t\t# Have input\n" " xorb %ah,%ah\t\t# BIOS: int 0x1a, 00\n" " int $0x1a\t\t\t# get system time\n" " cmpw %di,%dx\t\t# Timeout?\n" " jb read_key\t\t# No\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:436 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-start-input]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-start-input]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:449 msgid "" "An interrupt is requested with number `0x1a` and argument `0` in register `" "%ah`. The BIOS has a predefined set of services, requested by applications " "as software-generated interrupts through the `int` instruction and receiving " "arguments in registers (in this case, `%ah`). Here, particularly, we are " "requesting the number of clock ticks since last midnight; this value is " "computed by the BIOS through the RTC (Real Time Clock). This clock can be " "programmed to work at frequencies ranging from 2 Hz to 8192 Hz. The BIOS " "sets it to 18.2 Hz at startup. When the request is satisfied, a 32-bit " "result is returned by the BIOS in registers `%cx` and `%dx` (lower bytes in `" "%dx`). This result (the `%dx` part) is copied to register `%di`, and the " "value of the `TICKS` variable is added to `%di`. This variable resides in [." "filename]#boot0# at offset `_TICKS` (a negative value) from register `%bp` " "(which, recall, points to `0x800`). The default value of this variable is " "`0xb6` (182 in decimal). Now, the idea is that [.filename]#boot0# " "constantly requests the time from the BIOS, and when the value returned in " "register `%dx` is greater than the value stored in `%di`, the time is up and " "the default selection will be made. Since the RTC ticks 18.2 times per " "second, this condition will be met after 10 seconds (this default behavior " "can be changed in the [.filename]#Makefile#). Until this time has passed, [." "filename]#boot0# continually asks the BIOS for any user input; this is done " "through `int 0x16`, argument `1` in `%ah`." msgstr "" "Прерывание запрашивается с номером `0x1a` и аргументом `0` в регистре `%ah`. " "BIOS имеет предопределённый набор сервисов, запрашиваемых приложениями как " "программно-генерируемые прерывания через инструкцию `int`, с получением " "аргументов в регистрах (в данном случае, `%ah`). Здесь, в частности, " "запрашивается количество тиков часов с момента последней полуночи; это " "значение вычисляется BIOS через RTC (Real Time Clock). Эти часы могут быть " "настроены на работу с частотой от 2 Гц до 8192 Гц. BIOS устанавливает их на " "18,2 Гц при запуске. Когда запрос выполнен, 32-битный результат возвращается " "BIOS в регистрах `%cx` и `%dx` (младшие байты в `%dx`). Этот результат " "(часть `%dx`) копируется в регистр `%di`, и к `%di` добавляется значение " "переменной `TICKS`. Эта переменная находится в [.filename]#boot0# по " "смещению `_TICKS` (отрицательное значение) от регистра `%bp` (который, " "напомним, указывает на `0x800`). Значение этой переменной по умолчанию — " "`0xb6` (182 в десятичной системе). Идея заключается в том, что [." "filename]#boot0# постоянно запрашивает время у BIOS, и когда значение, " "возвращённое в регистре `%dx`, становится больше значения, хранящегося в `" "%di`, время истекает и будет сделан выбор по умолчанию. Поскольку RTC тикает " "18,2 раза в секунду, это условие выполнится через 10 секунд (это поведение " "по умолчанию можно изменить в [.filename]#Makefile#). До истечения этого " "времени [.filename]#boot0# непрерывно опрашивает BIOS на предмет ввода " "пользователя; это делается через `int 0x16`, аргумент `1` в `%ah`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:457 msgid "" "Whether a key was pressed or the time expired, subsequent code validates the " "selection. Based on the selection, the register `%si` is set to point to " "the appropriate partition entry in the partition table. This new selection " "overrides the previous default one. Indeed, it becomes the new default. " "Finally, the ACTIVE flag of the selected partition is set. If it was " "enabled at compile time, the in-memory version of [.filename]#boot0# with " "these modified values is written back to the MBR on disk. We leave the " "details of this implementation to the reader." msgstr "" "Была нажата клавиша или истекло время, последующий код проверяет выбор. В " "зависимости от выбора, регистр `%si` устанавливается так, чтобы указывать на " "соответствующую запись раздела в таблице разделов. Этот новый выбор " "переопределяет предыдущий выбор по умолчанию. Действительно, он становится " "новым значением по умолчанию. Наконец, устанавливается флаг ACTIVE " "выбранного раздела. Если это было разрешено при компиляции, версия [." "filename]#boot0# в памяти с этими изменёнными значениями записывается " "обратно в MBR на диске. Мы оставляем детали этой реализации читателю." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:459 msgid "" "We now end our study with the last code block from the [.filename]#boot0# " "program:" msgstr "" "Мы завершаем наше изучение последним блоком кода из программы [." "filename]#boot0#:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:472 #, no-wrap msgid "" " movw $LOAD,%bx\t\t# Address for read\n" " movb $0x2,%ah\t\t# Read sector\n" " callw intx13\t\t# from disk\n" " jc beep\t\t\t# If error\n" " cmpw $MAGIC,0x1fe(%bx)\t# Bootable?\n" " jne beep\t\t\t# No\n" " pushw %si\t\t\t# Save ptr to selected part.\n" " callw putn\t\t# Leave some space\n" " popw %si\t\t\t# Restore, next stage uses it\n" " jmp *%bx\t\t\t# Invoke bootstrap\n" msgstr "" " movw $LOAD,%bx\t\t# Address for read\n" " movb $0x2,%ah\t\t# Read sector\n" " callw intx13\t\t# from disk\n" " jc beep\t\t\t# If error\n" " cmpw $MAGIC,0x1fe(%bx)\t# Bootable?\n" " jne beep\t\t\t# No\n" " pushw %si\t\t\t# Save ptr to selected part.\n" " callw putn\t\t# Leave some space\n" " popw %si\t\t\t# Restore, next stage uses it\n" " jmp *%bx\t\t\t# Invoke bootstrap\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:474 #, no-wrap msgid "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-check-bootable]]" msgstr "[.filename]#stand/i386/boot0/boot0.S# [[boot-boot0-check-bootable]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:478 msgid "" "Recall that `%si` points to the selected partition entry. This entry tells " "us where the partition begins on disk. We assume, of course, that the " "partition selected is actually a FreeBSD slice." msgstr "" "Вспомним, что `%si` указывает на выбранную запись раздела. Эта запись " "сообщает нам, где начинается раздел на диске. Мы предполагаем, конечно, что " "выбранный раздел действительно является срезом FreeBSD." #. type: delimited block = 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:482 msgid "" "From now on, we will favor the use of the technically more accurate term " "\"slice\" rather than \"partition\"." msgstr "" "Отныне мы будем отдавать предпочтение использованию технически более точного " "термина \"слайс\" вместо \"раздел\"." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:488 msgid "" "The transfer buffer is set to `0x7c00` (register `%bx`), and a read for the " "first sector of the FreeBSD slice is requested by calling `intx13`. We " "assume that everything went okay, so a jump to `beep` is not performed. In " "particular, the new sector read must end with the magic sequence `0xaa55`. " "Finally, the value at `%si` (the pointer to the selected partition table) is " "preserved for use by the next stage, and a jump is performed to address " "`0x7c00`, where execution of our next stage (the just-read block) is started." msgstr "" "Буфер передачи установлен в `0x7c00` (регистр `%bx`), и запрос на чтение " "первого сектора слайса FreeBSD выполняется вызовом `intx13`. Мы " "предполагаем, что всё прошло успешно, поэтому переход к `beep` не " "выполняется. В частности, новый прочитанный сектор должен заканчиваться " "магической последовательностью `0xaa55`. Наконец, значение в `%si` " "(указатель на выбранную таблицу разделов) сохраняется для использования на " "следующем этапе, и выполняется переход по адресу `0x7c00`, где начинается " "выполнение нашего следующего этапа (только что прочитанного блока)." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:490 #, no-wrap msgid "`boot1` Stage" msgstr "Этап `boot1`" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:493 msgid "So far we have gone through the following sequence:" msgstr "До сих пор мы прошли следующую последовательность:" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:497 msgid "" "The BIOS did some early hardware initialization, including the POST. The " "MBR ([.filename]#boot0#) was loaded from absolute disk sector one to address " "`0x7c00`. Execution control was passed to that location." msgstr "" "BIOS выполнил первоначальную инициализацию оборудования, включая POST. MBR " "([.filename]#boot0#) был загружен по адресу `0x7c00` из абсолютного сектора " "один с диска. Управление выполнением было передано по этому адресу." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:500 msgid "" "[.filename]#boot0# relocated itself to the location it was linked to execute " "(`0x600`), followed by a jump to continue execution at the appropriate " "place. Finally, [.filename]#boot0# loaded the first disk sector from the " "FreeBSD slice to address `0x7c00`. Execution control was passed to that " "location." msgstr "" "[.filename]#boot0# переместил себя по адресу, по которому он был скомпонован " "для выполнения (`0x600`), после чего выполнил переход для продолжения " "выполнения в соответствующем месте. В завершение, [.filename]#boot0# " "загрузил первый сектор диска из раздела FreeBSD по адресу `0x7c00`. " "Управление выполнением было передано по этому адресу." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:513 msgid "" "[.filename]#boot1# is the next step in the boot-loading sequence. It is the " "first of three boot stages. Note that we have been dealing exclusively with " "disk sectors. Indeed, the BIOS loads the absolute first sector, while [." "filename]#boot0# loads the first sector of the FreeBSD slice. Both loads " "are to address `0x7c00`. We can conceptually think of these disk sectors as " "containing the files [.filename]#boot0# and [.filename]#boot1#, " "respectively, but in reality this is not entirely true for [." "filename]#boot1#. Strictly speaking, unlike [.filename]#boot0#, [." "filename]#boot1# is not part of the boot blocks footnote:[There is a file /" "boot/boot1, but it is not the written to the beginning of the FreeBSD " "slice. Instead, it is concatenated with boot2 to form boot, which is " "written to the beginning of the FreeBSD slice and read at boot time.]. " "Instead, a single, full-blown file, [.filename]#boot# ([.filename]#/boot/" "boot#), is what ultimately is written to disk. This file is a combination " "of [.filename]#boot1#, [.filename]#boot2# and the `Boot Extender` (or BTX). " "This single file is greater in size than a single sector (greater than 512 " "bytes). Fortunately, [.filename]#boot1# occupies _exactly_ the first 512 " "bytes of this single file, so when [.filename]#boot0# loads the first sector " "of the FreeBSD slice (512 bytes), it is actually loading [.filename]#boot1# " "and transferring control to it." msgstr "" "[.filename]#boot1# — это следующий шаг в последовательности загрузки. Это " "первая из трёх стадий загрузки. Обратите внимание, что до сих пор мы " "работали исключительно с секторами диска. Действительно, BIOS загружает " "самый первый сектор, а [.filename]#boot0# загружает первый сектор раздела " "FreeBSD. Обе загрузки происходят по адресу `0x7c00`. Мы можем концептуально " "представлять эти секторы диска как содержащие файлы [.filename]#boot0# и [." "filename]#boot1#, соответственно, но на самом деле это не совсем верно для [." "filename]#boot1#. Строго говоря, в отличие от [.filename]#boot0#, [." "filename]#boot1# не является частью загрузочных блоков footnote:[Файл /boot/" "boot1 существует, но он не записывается в начало раздела FreeBSD. Вместо " "этого он объединяется с boot2, формируя файл boot, который записывается в " "начало раздела FreeBSD и считывается во время загрузки.]. Вместо этого, " "единый полноценный файл [.filename]#boot# ([.filename]#/boot/boot#) в итоге " "записывается на диск. Этот файл представляет собой комбинацию [." "filename]#boot1#, [.filename]#boot2# и `Boot Extender` (или BTX). Этот " "единый файл превышает размер одного сектора (больше 512 байт). К счастью, [." "filename]#boot1# занимает _ровно_ первые 512 байт этого файла, поэтому, " "когда [.filename]#boot0# загружает первый сектор раздела FreeBSD (512 байт), " "он фактически загружает [.filename]#boot1# и передаёт ему управление." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:518 msgid "" "The main task of [.filename]#boot1# is to load the next boot stage. This " "next stage is somewhat more complex. It is composed of a server called the " "\"Boot Extender\", or BTX, and a client, called [.filename]#boot2#. As we " "will see, the last boot stage, [.filename]#loader#, is also a client of the " "BTX server." msgstr "" "Основная задача [.filename]#boot1# — загрузить следующий этап загрузки. Этот " "следующий этап несколько сложнее. Он состоит из сервера под названием \"Boot " "Extender\" (BTX) и клиента под названием [.filename]#boot2#. Как мы увидим, " "последний этап загрузки, [.filename]#loader#, также является клиентом " "сервера BTX." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:520 msgid "" "Let us now look in detail at what exactly is done by [.filename]#boot1#, " "starting like we did for [.filename]#boot0#, at its entry point:" msgstr "" "Давайте теперь подробно рассмотрим, что именно делает [.filename]#boot1#, " "начиная, как мы это делали для [.filename]#boot0#, с точки входа:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:525 #, no-wrap msgid "" "start:\n" "\tjmp main\n" msgstr "" "start:\n" "\tjmp main\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:527 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-entry]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-entry]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:529 msgid "" "The entry point at `start` simply jumps past a special data area to the " "label `main`, which in turn looks like this:" msgstr "" "Точка входа `start` просто переходит через специальную область данных к " "метке `main`, которая, в свою очередь, выглядит следующим образом:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:544 #, no-wrap msgid "" "main:\n" " cld\t\t\t# String ops inc\n" " xor %cx,%cx\t\t# Zero\n" " mov %cx,%es\t\t# Address\n" " mov %cx,%ds\t\t# data\n" " mov %cx,%ss\t\t# Set up\n" " mov $start,%sp\t\t# stack\n" " mov %sp,%si\t\t# Source\n" " mov $MEM_REL,%di\t\t# Destination\n" " incb %ch\t\t\t# Word count\n" " rep\t\t\t# Copy\n" " movsw\t\t\t# code\n" msgstr "" "main:\n" " cld\t\t\t# String ops inc\n" " xor %cx,%cx\t\t# Zero\n" " mov %cx,%es\t\t# Address\n" " mov %cx,%ds\t\t# data\n" " mov %cx,%ss\t\t# Set up\n" " mov $start,%sp\t\t# stack\n" " mov %sp,%si\t\t# Source\n" " mov $MEM_REL,%di\t\t# Destination\n" " incb %ch\t\t\t# Word count\n" " rep\t\t\t# Copy\n" " movsw\t\t\t# code\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:546 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:551 msgid "" "Just like [.filename]#boot0#, this code relocates [.filename]#boot1#, this " "time to memory address `0x700`. However, unlike [.filename]#boot0#, it does " "not jump there. [.filename]#boot1# is linked to execute at address " "`0x7c00`, effectively where it was loaded in the first place. The reason " "for this relocation will be discussed shortly." msgstr "" "Как и [.filename]#boot0#, этот код перемещает [.filename]#boot1#, на этот " "раз по адресу `0x700`. Однако, в отличие от [.filename]#boot0#, он не " "переходит туда. [.filename]#boot1# скомпонован для выполнения по адресу " "`0x7c00`, фактически там, куда он был изначально загружен. Причина этого " "перемещения будет рассмотрена далее." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:556 msgid "" "Next comes a loop that looks for the FreeBSD slice. Although [." "filename]#boot0# loaded [.filename]#boot1# from the FreeBSD slice, no " "information was passed to it about this footnote:[Actually we did pass a " "pointer to the slice entry in register %si. However, boot1 does not assume " "that it was loaded by boot0 (perhaps some other MBR loaded it, and did not " "pass this information), so it assumes nothing.], so [.filename]#boot1# must " "rescan the partition table to find where the FreeBSD slice starts. " "Therefore it rereads the MBR:" msgstr "" "Далее идёт цикл, который ищет слайс FreeBSD. Хотя [.filename]#boot0# " "загрузил [.filename]#boot1# из слайса FreeBSD, ему не была передана " "информация об этом footnote:[На самом деле мы передали указатель на адрес " "слайса в регистре %si. Однако boot1 не предполагает, что он был загружен " "boot0 (возможно, его загрузил другой MBR и не передал эту информацию), " "поэтому он ничего не предполагает.], поэтому [.filename]#boot1# должен " "повторно просканировать таблицу разделов, чтобы найти начало слайса FreeBSD. " "Для этого он перечитывает MBR:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:564 #, no-wrap msgid "" " mov $part4,%si\t\t# Partition\n" " cmpb $0x80,%dl\t\t# Hard drive?\n" " jb main.4\t\t\t# No\n" " movb $0x1,%dh\t\t# Block count\n" " callw nread\t\t# Read MBR\n" msgstr "" " mov $part4,%si\t\t# Partition\n" " cmpb $0x80,%dl\t\t# Hard drive?\n" " jb main.4\t\t\t# No\n" " movb $0x1,%dh\t\t# Block count\n" " callw nread\t\t# Read MBR\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:566 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-freebsd]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-freebsd]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:574 msgid "" "In the code above, register `%dl` maintains information about the boot " "device. This is passed on by the BIOS and preserved by the MBR. Numbers " "`0x80` and greater tells us that we are dealing with a hard drive, so a call " "is made to `nread`, where the MBR is read. Arguments to `nread` are passed " "through `%si` and `%dh`. The memory address at label `part4` is copied to `" "%si`. This memory address holds a \"fake partition\" to be used by " "`nread`. The following is the data in the fake partition:" msgstr "" "В приведённом выше коде регистр `%dl` содержит информацию о загрузочном " "устройстве. Эти данные передаются BIOS и сохраняются MBR. Числа `0x80` и " "выше указывают на то, что мы имеем дело с жёстким диском, поэтому вызывается " "`nread`, где считывается MBR. Аргументы для `nread` передаются через `%si` " "и `%dh`. Адрес памяти по метке `part4` копируется в `%si`. Этот адрес " "памяти содержит \"фальшивый раздел\", который будет использован `nread`. " "Ниже приведены данные фальшивого раздела:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:582 #, no-wrap msgid "" " part4:\n" "\t.byte 0x80, 0x00, 0x01, 0x00\n" "\t.byte 0xa5, 0xfe, 0xff, 0xff\n" "\t.byte 0x00, 0x00, 0x00, 0x00\n" "\t.byte 0x50, 0xc3, 0x00, 0x00\n" msgstr "" " part4:\n" "\t.byte 0x80, 0x00, 0x01, 0x00\n" "\t.byte 0xa5, 0xfe, 0xff, 0xff\n" "\t.byte 0x00, 0x00, 0x00, 0x00\n" "\t.byte 0x50, 0xc3, 0x00, 0x00\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:584 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot2-make-fake-partition]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot2-make-fake-partition]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:589 msgid "" "In particular, the LBA for this fake partition is hardcoded to zero. This " "is used as an argument to the BIOS for reading absolute sector one from the " "hard drive. Alternatively, CHS addressing could be used. In this case, the " "fake partition holds cylinder 0, head 0 and sector 1, which is equivalent to " "absolute sector one." msgstr "" "В частности, LBA для этой фиктивной раздела жёстко закодирован как ноль. Это " "используется как аргумент для BIOS при чтении абсолютного сектора один с " "жёсткого диска. Или же может использоваться адресация CHS. В этом случае " "фиктивный раздел содержит цилиндр 0, головку 0 и сектор 1, что эквивалентно " "абсолютному сектору один." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:591 msgid "Let us now proceed to take a look at `nread`:" msgstr "Продолжим, рассмотрев `nread`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:601 #, no-wrap msgid "" "nread:\n" " mov $MEM_BUF,%bx\t\t# Transfer buffer\n" " mov 0x8(%si),%ax\t\t# Get\n" " mov 0xa(%si),%cx\t\t# LBA\n" " push %cs\t\t\t# Read from\n" " callw xread.1\t\t# disk\n" " jnc return\t\t# If success, return\n" msgstr "" "nread:\n" " mov $MEM_BUF,%bx\t\t# Transfer buffer\n" " mov 0x8(%si),%ax\t\t# Get\n" " mov 0xa(%si),%cx\t\t# LBA\n" " push %cs\t\t\t# Read from\n" " callw xread.1\t\t# disk\n" " jnc return\t\t# If success, return\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:603 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-nread]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-nread]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:612 msgid "" "Recall that `%si` points to the fake partition. The word footnote:[In the " "context of 16-bit real mode, a word is 2 bytes.] at offset `0x8` is copied " "to register `%ax` and word at offset `0xa` to `%cx`. They are interpreted " "by the BIOS as the lower 4-byte value denoting the LBA to be read (the upper " "four bytes are assumed to be zero). Register `%bx` holds the memory address " "where the MBR will be loaded. The instruction pushing `%cs` onto the stack " "is very interesting. In this context, it accomplishes nothing. However, as " "we will see shortly, [.filename]#boot2#, in conjunction with the BTX server, " "also uses `xread.1`. This mechanism will be discussed in the next section." msgstr "" "Напомним, что `%si` указывает на поддельный раздел. Слово footnote:[В " "контексте 16-битного реального режима слово — это 2 байта.] по смещению " "`0x8` копируется в регистр `%ax`, а слово по смещению `0xa` — в `%cx`. BIOS " "интерпретирует их как младшее 4-байтовое значение, обозначающее LBA для " "чтения (старшие четыре байта предполагаются нулевыми). Регистр `%bx` " "содержит адрес памяти, куда будет загружен MBR. Инструкция, помещающая `%cs` " "в стек, очень интересна. В данном контексте она ничего не делает. Однако, " "как мы скоро увидим, [.filename]#boot2# в сочетании с сервером BTX также " "использует `xread.1`. Этот механизм будет рассмотрен в следующем разделе." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:614 msgid "" "The code at `xread.1` further calls the `read` function, which actually " "calls the BIOS asking for the disk sector:" msgstr "" "Код в `xread.1` далее вызывает функцию `read`, которая фактически обращается " "к BIOS с запросом на чтение сектора диска:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:631 #, no-wrap msgid "" "xread.1:\n" "\tpushl $0x0\t\t# absolute\n" "\tpush %cx\t\t# block\n" "\tpush %ax\t\t# number\n" "\tpush %es\t\t# Address of\n" "\tpush %bx\t\t# transfer buffer\n" "\txor %ax,%ax\t\t# Number of\n" "\tmovb %dh,%al\t\t# blocks to\n" "\tpush %ax\t\t# transfer\n" "\tpush $0x10\t\t# Size of packet\n" "\tmov %sp,%bp\t\t# Packet pointer\n" "\tcallw read\t\t# Read from disk\n" "\tlea 0x10(%bp),%sp\t# Clear stack\n" "\tlret\t\t\t# To far caller\n" msgstr "" "xread.1:\n" "\tpushl $0x0\t\t# absolute\n" "\tpush %cx\t\t# block\n" "\tpush %ax\t\t# number\n" "\tpush %es\t\t# Address of\n" "\tpush %bx\t\t# transfer buffer\n" "\txor %ax,%ax\t\t# Number of\n" "\tmovb %dh,%al\t\t# blocks to\n" "\tpush %ax\t\t# transfer\n" "\tpush $0x10\t\t# Size of packet\n" "\tmov %sp,%bp\t\t# Packet pointer\n" "\tcallw read\t\t# Read from disk\n" "\tlea 0x10(%bp),%sp\t# Clear stack\n" "\tlret\t\t\t# To far caller\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:633 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-xread1]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-xread1]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:637 msgid "" "Note the long return instruction at the end of this block. This instruction " "pops out the `%cs` register pushed by `nread`, and returns. Finally, " "`nread` also returns." msgstr "" "Обратите внимание на длинную инструкцию возврата в конце этого блока. Эта " "инструкция извлекает регистр `%cs`, помещённый в стек `nread`, и возвращает " "управление. В конце `nread` также возвращает управление." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:639 msgid "" "With the MBR loaded to memory, the actual loop for searching the FreeBSD " "slice begins:" msgstr "" "С загрузкой MBR в память начинается фактический цикл поиска слайса FreeBSD:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:659 #, no-wrap msgid "" "\tmov $0x1,%cx\t\t # Two passes\n" "main.1:\n" "\tmov $MEM_BUF+PRT_OFF,%si # Partition table\n" "\tmovb $0x1,%dh\t\t # Partition\n" "main.2:\n" "\tcmpb $PRT_BSD,0x4(%si)\t # Our partition type?\n" "\tjne main.3\t\t # No\n" "\tjcxz main.5\t\t # If second pass\n" "\ttestb $0x80,(%si)\t # Active?\n" "\tjnz main.5\t\t # Yes\n" "main.3:\n" "\tadd $0x10,%si\t\t # Next entry\n" "\tincb %dh\t\t # Partition\n" "\tcmpb $0x1+PRT_NUM,%dh\t\t # In table?\n" "\tjb main.2\t\t # Yes\n" "\tdec %cx\t\t\t # Do two\n" "\tjcxz main.1\t\t # passes\n" msgstr "" "\tmov $0x1,%cx\t\t # Two passes\n" "main.1:\n" "\tmov $MEM_BUF+PRT_OFF,%si # Partition table\n" "\tmovb $0x1,%dh\t\t # Partition\n" "main.2:\n" "\tcmpb $PRT_BSD,0x4(%si)\t # Our partition type?\n" "\tjne main.3\t\t # No\n" "\tjcxz main.5\t\t # If second pass\n" "\ttestb $0x80,(%si)\t # Active?\n" "\tjnz main.5\t\t # Yes\n" "main.3:\n" "\tadd $0x10,%si\t\t # Next entry\n" "\tincb %dh\t\t # Partition\n" "\tcmpb $0x1+PRT_NUM,%dh\t\t # In table?\n" "\tjb main.2\t\t # Yes\n" "\tdec %cx\t\t\t # Do two\n" "\tjcxz main.1\t\t # passes\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:661 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-part]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-find-part]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:665 msgid "" "If a FreeBSD slice is identified, execution continues at `main.5`. Note " "that when a FreeBSD slice is found `%si` points to the appropriate entry in " "the partition table, and `%dh` holds the partition number. We assume that a " "FreeBSD slice is found, so we continue execution at `main.5`:" msgstr "" "Если обнаружен слайс FreeBSD, выполнение продолжается на метке `main.5`. " "Обратите внимание, что при обнаружении слайса FreeBSD `%si` указывает на " "соответствующую запись в таблице разделов, а `%dh` содержит номер раздела. " "Мы предполагаем, что слайс FreeBSD найден, поэтому продолжаем выполнение на " "метке `main.5`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:680 #, no-wrap msgid "" "main.5:\n" "\tmov %dx,MEM_ARG\t\t\t # Save args\n" "\tmovb $NSECT,%dh\t\t\t # Sector count\n" "\tcallw nread\t\t\t # Read disk\n" "\tmov $MEM_BTX,%bx\t\t\t # BTX\n" "\tmov 0xa(%bx),%si\t\t # Get BTX length and set\n" "\tadd %bx,%si\t\t\t # %si to start of boot2.bin\n" "\tmov $MEM_USR+SIZ_PAG*2,%di\t\t\t # Client page 2\n" "\tmov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx\t\t\t # Byte\n" "\tsub %si,%cx\t\t\t # count\n" "\trep\t\t\t\t # Relocate\n" "\tmovsb\t\t\t\t # client\n" msgstr "" "main.5:\n" "\tmov %dx,MEM_ARG\t\t\t # Save args\n" "\tmovb $NSECT,%dh\t\t\t # Sector count\n" "\tcallw nread\t\t\t # Read disk\n" "\tmov $MEM_BTX,%bx\t\t\t # BTX\n" "\tmov 0xa(%bx),%si\t\t # Get BTX length and set\n" "\tadd %bx,%si\t\t\t # %si to start of boot2.bin\n" "\tmov $MEM_USR+SIZ_PAG*2,%di\t\t\t # Client page 2\n" "\tmov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx\t\t\t # Byte\n" "\tsub %si,%cx\t\t\t # count\n" "\trep\t\t\t\t # Relocate\n" "\tmovsb\t\t\t\t # client\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:682 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main5]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-main5]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:688 msgid "" "Recall that at this point, register `%si` points to the FreeBSD slice entry " "in the MBR partition table, so a call to `nread` will effectively read " "sectors at the beginning of this partition. The argument passed on register " "`%dh` tells `nread` to read 16 disk sectors. Recall that the first 512 " "bytes, or the first sector of the FreeBSD slice, coincides with the [." "filename]#boot1# program. Also recall that the file written to the " "beginning of the FreeBSD slice is not [.filename]#/boot/boot1#, but [." "filename]#/boot/boot#. Let us look at the size of these files in the " "filesystem:" msgstr "" "Напомним, что в данный момент регистр `%si` указывает на запись среза " "FreeBSD в таблице разделов MBR, поэтому вызов `nread` фактически прочитает " "секторы в начале этого раздела. Аргумент, переданный в регистре `%dh`, " "указывает `nread` прочитать 16 секторов диска. Напомним, что первые 512 " "байт, или первый сектор слайса FreeBSD, совпадает с программой [." "filename]#boot1#. Также напомним, что файл, записанный в начало слайса " "FreeBSD, это не [.filename]#/boot/boot1#, а [.filename]#/boot/boot#. Давайте " "посмотрим на размер этих файлов в файловой системе:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:695 #, no-wrap msgid "" "-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot0\n" "-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot1\n" "-r--r--r-- 1 root wheel 7.5K Jan 8 00:15 /boot/boot2\n" "-r--r--r-- 1 root wheel 8.0K Jan 8 00:15 /boot/boot\n" msgstr "" "-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot0\n" "-r--r--r-- 1 root wheel 512B Jan 8 00:15 /boot/boot1\n" "-r--r--r-- 1 root wheel 7.5K Jan 8 00:15 /boot/boot2\n" "-r--r--r-- 1 root wheel 8.0K Jan 8 00:15 /boot/boot\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:703 msgid "" "Both [.filename]#boot0# and [.filename]#boot1# are 512 bytes each, so they " "fit _exactly_ in one disk sector. [.filename]#boot2# is much bigger, " "holding both the BTX server and the [.filename]#boot2# client. Finally, a " "file called simply [.filename]#boot# is 512 bytes larger than [." "filename]#boot2#. This file is a concatenation of [.filename]#boot1# and [." "filename]#boot2#. As already noted, [.filename]#boot0# is the file written " "to the absolute first disk sector (the MBR), and [.filename]#boot# is the " "file written to the first sector of the FreeBSD slice; [.filename]#boot1# " "and [.filename]#boot2# are _not_ written to disk. The command used to " "concatenate [.filename]#boot1# and [.filename]#boot2# into a single [." "filename]#boot# is merely `cat boot1 boot2 > boot`." msgstr "" "Оба файла [.filename]#boot0# и [.filename]#boot1# имеют размер 512 байт " "каждый, поэтому они занимают _ровно_ один сектор диска. [.filename]#boot2# " "значительно больше, так как содержит как сервер BTX, так и клиент [." "filename]#boot2#. Наконец, файл под названием просто [.filename]#boot# на " "512 байт больше, чем [.filename]#boot2#. Этот файл представляет собой " "объединение [.filename]#boot1# и [.filename]#boot2#. Как уже отмечалось, [." "filename]#boot0# записывается в самый первый сектор диска (MBR), а [." "filename]#boot# записывается в первый сектор раздела FreeBSD; [." "filename]#boot1# и [.filename]#boot2# _не_ записываются на диск. Команда, " "используемая для объединения [.filename]#boot1# и [.filename]#boot2# в " "единый файл [.filename]#boot#, выглядит просто как `cat boot1 boot2 > boot`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:707 msgid "" "So [.filename]#boot1# occupies exactly the first 512 bytes of [." "filename]#boot# and, because [.filename]#boot# is written to the first " "sector of the FreeBSD slice, [.filename]#boot1# fits exactly in this first " "sector. When `nread` reads the first 16 sectors of the FreeBSD slice, it " "effectively reads the entire [.filename]#boot# file footnote:[512*16=8192 " "bytes, exactly the size of boot]. We will see more details about how [." "filename]#boot# is formed from [.filename]#boot1# and [.filename]#boot2# in " "the next section." msgstr "" "Итак, [.filename]#boot1# занимает ровно первые 512 байт [.filename]#boot#, " "и, поскольку [.filename]#boot# записывается в первый сектор слайса FreeBSD, " "[.filename]#boot1# полностью помещается в этот первый сектор. Когда `nread` " "читает первые 16 секторов слайса FreeBSD, он фактически читает весь файл [." "filename]#boot# footnote:[512*16=8192 байта, ровно размер boot]. Более " "подробно о том, как [.filename]#boot# формируется из [.filename]#boot1# и [." "filename]#boot2#, мы увидим в следующем разделе." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:713 msgid "" "Recall that `nread` uses memory address `0x8c00` as the transfer buffer to " "hold the sectors read. This address is conveniently chosen. Indeed, " "because [.filename]#boot1# belongs to the first 512 bytes, it ends up in the " "address range `0x8c00`-`0x8dff`. The 512 bytes that follows (range `0x8e00`-" "`0x8fff`) is used to store the _bsdlabel_ footnote:[Historically known as " "disklabel. If you ever wondered where FreeBSD stored this information, it " "is in this region - see man:bsdlabel[8]]." msgstr "" "Напомним, что `nread` использует адрес памяти `0x8c00` в качестве буфера " "передачи для хранения прочитанных секторов. Этот адрес выбран не случайно. " "Действительно, поскольку [.filename]#boot1# принадлежит первым 512 байтам, " "он оказывается в диапазоне адресов `0x8c00`-`0x8dff`. Следующие 512 байт " "(диапазон `0x8e00`-`0x8fff`) используются для хранения _bsdlabel_ footnote:" "[Исторически известной как disklabel. Если вам когда-либо было интересно, " "где FreeBSD хранит эту информацию, она находится в этой области — см. man:" "bsdlabel[8]]." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:721 msgid "" "Starting at address `0x9000` is the beginning of the BTX server, and " "immediately following is the [.filename]#boot2# client. The BTX server acts " "as a kernel, and executes in protected mode in the most privileged level. " "In contrast, the BTX clients ([.filename]#boot2#, for example), execute in " "user mode. We will see how this is accomplished in the next section. The " "code after the call to `nread` locates the beginning of [.filename]#boot2# " "in the memory buffer, and copies it to memory address `0xc000`. This is " "because the BTX server arranges [.filename]#boot2# to execute in a segment " "starting at `0xa000`. We explore this in detail in the following section." msgstr "" "Начиная с адреса `0x9000` находится начало сервера BTX, и сразу за ним " "следует клиент [.filename]#boot2#. Сервер BTX действует как ядро и " "выполняется в защищённом режиме с наивысшим уровнем привилегий. В отличие от " "этого, клиенты BTX (например, [.filename]#boot2#) выполняются в " "пользовательском режиме. Мы увидим, как это реализовано, в следующем " "разделе. Код после вызова `nread` находит начало [.filename]#boot2# в буфере " "памяти и копирует его по адресу `0xc000`. Это связано с тем, что сервер BTX " "размещает [.filename]#boot2# для выполнения в сегменте, начинающемся с " "`0xa000`. Мы подробно рассмотрим это в следующем разделе." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:724 msgid "" "The last code block of [.filename]#boot1# enables access to memory above 1MB " "footnote:[This is necessary for legacy reasons.] and concludes with a jump " "to the starting point of the BTX server:" msgstr "" "Последний блок кода в [.filename]#boot1# разрешает доступ к памяти выше 1MB " "footnote:[Это необходимо по историческим причинам.] и завершается переходом " "к начальной точке сервера BTX:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:732 #, no-wrap msgid "" "seta20:\n" "\tcli\t\t\t# Disable interrupts\n" "seta20.1:\n" "\tdec %cx\t\t\t# Timeout?\n" "\tjz seta20.3\t\t# Yes\n" msgstr "" "seta20:\n" "\tcli\t\t\t# Disable interrupts\n" "seta20.1:\n" "\tdec %cx\t\t\t# Timeout?\n" "\tjz seta20.3\t\t# Yes\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:747 #, no-wrap msgid "" "\tinb $0x64,%al\t\t# Get status\n" "\ttestb $0x2,%al\t\t# Busy?\n" "\tjnz seta20.1\t\t# Yes\n" "\tmovb $0xd1,%al\t\t# Command: Write\n" "\toutb %al,$0x64\t\t# output port\n" "seta20.2:\n" "\tinb $0x64,%al\t\t# Get status\n" "\ttestb $0x2,%al\t\t# Busy?\n" "\tjnz seta20.2\t\t# Yes\n" "\tmovb $0xdf,%al\t\t# Enable\n" "\toutb %al,$0x60\t\t# A20\n" "seta20.3:\n" "\tsti\t\t\t# Enable interrupts\n" "\tjmp 0x9010\t\t# Start BTX\n" msgstr "" "\tinb $0x64,%al\t\t# Get status\n" "\ttestb $0x2,%al\t\t# Busy?\n" "\tjnz seta20.1\t\t# Yes\n" "\tmovb $0xd1,%al\t\t# Command: Write\n" "\toutb %al,$0x64\t\t# output port\n" "seta20.2:\n" "\tinb $0x64,%al\t\t# Get status\n" "\ttestb $0x2,%al\t\t# Busy?\n" "\tjnz seta20.2\t\t# Yes\n" "\tmovb $0xdf,%al\t\t# Enable\n" "\toutb %al,$0x60\t\t# A20\n" "seta20.3:\n" "\tsti\t\t\t# Enable interrupts\n" "\tjmp 0x9010\t\t# Start BTX\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:749 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-seta20]]" msgstr "[.filename]#stand/i386/boot2/boot1.S# [[boot-boot1-seta20]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:751 msgid "Note that right before the jump, interrupts are enabled." msgstr "" "Обратите внимание, что непосредственно перед переходом прерывания включаются." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:753 #, no-wrap msgid "The BTX Server" msgstr "Сервер BTX" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:757 msgid "" "Next in our boot sequence is the BTX Server. Let us quickly remember how we " "got here:" msgstr "" "Далее в нашей последовательности загрузки идёт сервер BTX. Давайте быстро " "вспомним, как мы сюда попали:" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:759 msgid "" "The BIOS loads the absolute sector one (the MBR, or [.filename]#boot0#), to " "address `0x7c00` and jumps there." msgstr "" "BIOS загружает абсолютный сектор один (MBR или [.filename]#boot0#) по адресу " "`0x7c00` и переходит туда." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:761 msgid "" "[.filename]#boot0# relocates itself to `0x600`, the address it was linked to " "execute, and jumps over there. It then reads the first sector of the " "FreeBSD slice (which consists of [.filename]#boot1#) into address `0x7c00` " "and jumps over there." msgstr "" "[.filename]#boot0# перемещает себя по адресу `0x600`, по которому он был " "слинкован для выполнения, и переходит туда. Затем он читает первый сектор " "среза FreeBSD (который содержит [.filename]#boot1#) в адрес `0x7c00` и " "переходит туда." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:766 msgid "" "[.filename]#boot1# loads the first 16 sectors of the FreeBSD slice into " "address `0x8c00`. This 16 sectors, or 8192 bytes, is the whole file [." "filename]#boot#. The file is a concatenation of [.filename]#boot1# and [." "filename]#boot2#. [.filename]#boot2#, in turn, contains the BTX server and " "the [.filename]#boot2# client. Finally, a jump is made to address `0x9010`, " "the entry point of the BTX server." msgstr "" "[.filename]#boot1# загружает первые 16 секторов среза FreeBSD по адресу " "`0x8c00`. Эти 16 секторов, или 8192 байта, представляют собой весь файл [." "filename]#boot#. Файл является объединением [.filename]#boot1# и [." "filename]#boot2#. [.filename]#boot2#, в свою очередь, содержит сервер BTX и " "клиент [.filename]#boot2#. Наконец, выполняется переход по адресу `0x9010`, " "точке входа сервера BTX." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:770 msgid "" "Before studying the BTX Server in detail, let us further review how the " "single, all-in-one [.filename]#boot# file is created. The way [." "filename]#boot# is built is defined in its [.filename]#Makefile# ([." "filename]#stand/i386/boot2/Makefile#). Let us look at the rule that creates " "the [.filename]#boot# file:" msgstr "" "Прежде чем изучать сервер BTX подробно, давайте рассмотрим, как создается " "единый, всеобъемлющий файл [.filename]#boot#. Способ сборки [." "filename]#boot# определен в его [.filename]#Makefile# ([.filename]#stand/" "i386/boot2/Makefile#). Рассмотрим правило, которое создает файл [." "filename]#boot#:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:775 #, no-wrap msgid "" " boot: boot1 boot2\n" "\tcat boot1 boot2 > boot\n" msgstr "" " boot: boot1 boot2\n" "\tcat boot1 boot2 > boot\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:777 #, no-wrap msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot]]" msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:780 msgid "" "This tells us that [.filename]#boot1# and [.filename]#boot2# are needed, and " "the rule simply concatenates them to produce a single file called [." "filename]#boot#. The rules for creating [.filename]#boot1# are also quite " "simple:" msgstr "" "Это говорит нам, что [.filename]#boot1# и [.filename]#boot2# необходимы, и " "правило просто объединяет их для создания одного файла с именем [." "filename]#boot#. Правила для создания [.filename]#boot1# также довольно " "просты:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:785 #, no-wrap msgid "" " boot1: boot1.out\n" "\t${OBJCOPY} -S -O binary boot1.out ${.TARGET}\n" msgstr "" " boot1: boot1.out\n" "\t${OBJCOPY} -S -O binary boot1.out ${.TARGET}\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:788 #, no-wrap msgid "" " boot1.out: boot1.o\n" "\t${LD} ${LD_FLAGS} -e start --defsym ORG=${ORG1} -T ${LDSCRIPT} -o ${.TARGET} boot1.o\n" msgstr "" " boot1.out: boot1.o\n" "\t${LD} ${LD_FLAGS} -e start --defsym ORG=${ORG1} -T ${LDSCRIPT} -o ${.TARGET} boot1.o\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:790 #, no-wrap msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot1]]" msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot1]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:799 msgid "" "To apply the rule for creating [.filename]#boot1#, [.filename]#boot1.out# " "must be resolved. This, in turn, depends on the existence of [." "filename]#boot1.o#. This last file is simply the result of assembling our " "familiar [.filename]#boot1.S#, without linking. Now, the rule for creating " "[.filename]#boot1.out# is applied. This tells us that [.filename]#boot1.o# " "should be linked with `start` as its entry point, and starting at address " "`0x7c00`. Finally, [.filename]#boot1# is created from [.filename]#boot1." "out# applying the appropriate rule. This rule is the [.filename]#objcopy# " "command applied to [.filename]#boot1.out#. Note the flags passed to [." "filename]#objcopy#: `-S` tells it to strip all relocation and symbolic " "information; `-O binary` indicates the output format, that is, a simple, " "unformatted binary file." msgstr "" "Для применения правила создания [.filename]#boot1# необходимо собрать [." "filename]#boot1.out#. Это, в свою очередь, зависит от наличия [." "filename]#boot1.o#. Последний файл является результатом ассемблирования " "нашего знакомого [.filename]#boot1.S# без компоновки. Теперь применяется " "правило создания [.filename]#boot1.out#. Оно указывает, что [." "filename]#boot1.o# должен быть скомпонован с точкой входа `start` и " "начальным адресом `0x7c00`. Наконец, [.filename]#boot1# создается из [." "filename]#boot1.out# применением соответствующего правила. Это команда [." "filename]#objcopy#, применяемая к [.filename]#boot1.out#. Обратите внимание " "на флаги, передаваемые [.filename]#objcopy#: `-S` указывает на удаление всей " "информации о перемещении и символов; `-O binary` указывает формат вывода, то " "есть простой, неформатированный двоичный файл." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:801 msgid "" "Having [.filename]#boot1#, let us take a look at how [.filename]#boot2# is " "constructed:" msgstr "" "Имея [.filename]#boot1#, давайте посмотрим, как устроен [.filename]#boot2#:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:808 #, no-wrap msgid "" " boot2: boot2.ld\n" "\t@set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \\\n" "\t echo \"$$x bytes available\"; test $$x -ge 0\n" "\t${DD} if=${.ALLSRC} of=${.TARGET} bs=${BOOT2SIZE} conv=sync\n" msgstr "" " boot2: boot2.ld\n" "\t@set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \\\n" "\t echo \"$$x bytes available\"; test $$x -ge 0\n" "\t${DD} if=${.ALLSRC} of=${.TARGET} bs=${BOOT2SIZE} conv=sync\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:812 #, no-wrap msgid "" " boot2.ld: boot2.ldr boot2.bin ${BTXKERN}\n" "\tbtxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \\\n" "\t -o ${.TARGET} -P 1 boot2.bin\n" msgstr "" " boot2.ld: boot2.ldr boot2.bin ${BTXKERN}\n" "\tbtxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \\\n" "\t -o ${.TARGET} -P 1 boot2.bin\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:815 #, no-wrap msgid "" " boot2.ldr:\n" "\t${DD} if=/dev/zero of=${.TARGET} bs=512 count=1\n" msgstr "" " boot2.ldr:\n" "\t${DD} if=/dev/zero of=${.TARGET} bs=512 count=1\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:818 #, no-wrap msgid "" " boot2.bin: boot2.out\n" "\t${OBJCOPY} -S -O binary boot2.out ${.TARGET}\n" msgstr "" " boot2.bin: boot2.out\n" "\t${OBJCOPY} -S -O binary boot2.out ${.TARGET}\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:821 #, no-wrap msgid "" " boot2.out: ${BTXCRT} boot2.o sio.o ashldi3.o\n" "\t${LD} ${LD_FLAGS} --defsym ORG=${ORG2} -T ${LDSCRIPT} -o ${.TARGET} ${.ALLSRC}\n" msgstr "" " boot2.out: ${BTXCRT} boot2.o sio.o ashldi3.o\n" "\t${LD} ${LD_FLAGS} --defsym ORG=${ORG2} -T ${LDSCRIPT} -o ${.TARGET} ${.ALLSRC}\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:828 #, no-wrap msgid "" " boot2.h: boot1.out\n" "\t${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \\\n" "\t { x = $$1 - ORG1; \\\n" "\t printf(\"#define XREADORG %#x\\n\", REL1 + x) }' \\\n" "\t ORG1=`printf \"%d\" ${ORG1}` \\\n" "\t REL1=`printf \"%d\" ${REL1}` > ${.TARGET}\n" msgstr "" " boot2.h: boot1.out\n" "\t${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \\\n" "\t { x = $$1 - ORG1; \\\n" "\t printf(\"#define XREADORG %#x\\n\", REL1 + x) }' \\\n" "\t ORG1=`printf \"%d\" ${ORG1}` \\\n" "\t REL1=`printf \"%d\" ${REL1}` > ${.TARGET}\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:830 #, no-wrap msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2]]" msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:834 msgid "" "The mechanism for building [.filename]#boot2# is far more elaborate. Let us " "point out the most relevant facts. The dependency list is as follows:" msgstr "" "Механизм сборки [.filename]#boot2# гораздо сложнее. Отметим наиболее важные " "моменты. Список зависимостей выглядит следующим образом:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:842 #, no-wrap msgid "" " boot2: boot2.ld\n" " boot2.ld: boot2.ldr boot2.bin ${BTXDIR}\n" " boot2.bin: boot2.out\n" " boot2.out: ${BTXDIR} boot2.o sio.o ashldi3.o\n" " boot2.h: boot1.out\n" msgstr "" " boot2: boot2.ld\n" " boot2.ld: boot2.ldr boot2.bin ${BTXDIR}\n" " boot2.bin: boot2.out\n" " boot2.out: ${BTXDIR} boot2.o sio.o ashldi3.o\n" " boot2.h: boot1.out\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:844 #, no-wrap msgid "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2-more]]" msgstr "[.filename]#stand/i386/boot2/Makefile# [[boot-boot1-make-boot2-more]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:847 msgid "" "Note that initially there is no header file [.filename]#boot2.h#, but its " "creation depends on [.filename]#boot1.out#, which we already have. The rule " "for its creation is a bit terse, but the important thing is that the output, " "[.filename]#boot2.h#, is something like this:" msgstr "" "Отметим, что изначально файл заголовка [.filename]#boot2.h# отсутствует, но " "его создание зависит от [.filename]#boot1.out#, который у нас уже есть. " "Правило его создания немного лаконично, но важно то, что результат, [." "filename]#boot2.h#, выглядит примерно так:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:851 #, no-wrap msgid "#define XREADORG 0x725\n" msgstr "#define XREADORG 0x725\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:853 #, no-wrap msgid "[.filename]#stand/i386/boot2/boot2.h# [[boot-boot1-make-boot2h]]" msgstr "[.filename]#stand/i386/boot2/boot2.h# [[boot-boot1-make-boot2h]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:859 msgid "" "Recall that [.filename]#boot1# was relocated (i.e., copied from `0x7c00` to " "`0x700`). This relocation will now make sense, because as we will see, the " "BTX server reclaims some memory, including the space where [." "filename]#boot1# was originally loaded. However, the BTX server needs " "access to [.filename]#boot1#'s `xread` function; this function, according to " "the output of [.filename]#boot2.h#, is at location `0x725`. Indeed, the BTX " "server uses the `xread` function from [.filename]#boot1#'s relocated code. " "This function is now accessible from within the [.filename]#boot2# client." msgstr "" "Напомним, что [.filename]#boot1# был перемещён (т.е. скопирован из `0x7c00` " "в `0x700`). Это перемещение теперь обретает смысл, потому что, как мы " "увидим, сервер BTX освобождает часть памяти, включая область, куда [." "filename]#boot1# был изначально загружен. Однако серверу BTX необходим " "доступ к функции `xread` из [.filename]#boot1#; согласно выводу [." "filename]#boot2.h#, эта функция находится по адресу `0x725`. Действительно, " "сервер BTX использует функцию `xread` из перемещённого кода [." "filename]#boot1#. Теперь эта функция доступна из клиента [.filename]#boot2#." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:868 msgid "" "The next rule directs the linker to link various files ([.filename]#ashldi3." "o#, [.filename]#boot2.o# and [.filename]#sio.o#). Note that the output " "file, [.filename]#boot2.out#, is linked to execute at address `0x2000` " "(${ORG2}). Recall that [.filename]#boot2# will be executed in user mode, " "within a special user segment set up by the BTX server. This segment starts " "at `0xa000`. Also, remember that the [.filename]#boot2# portion of [." "filename]#boot# was copied to address `0xc000`, that is, offset `0x2000` " "from the start of the user segment, so [.filename]#boot2# will work properly " "when we transfer control to it. Next, [.filename]#boot2.bin# is created " "from [.filename]#boot2.out# by stripping its symbols and format information; " "boot2.bin is a _raw_ binary. Now, note that a file [.filename]#boot2.ldr# " "is created as a 512-byte file full of zeros. This space is reserved for the " "bsdlabel." msgstr "" "Следующее правило указывает компоновщику на необходимость связать различные " "файлы ([.filename]#ashldi3.o#, [.filename]#boot2.o# и [.filename]#sio.o#). " "Обратите внимание, что выходной файл [.filename]#boot2.out# компонуется для " "выполнения по адресу `0x2000` (${ORG2}). Напомним, что [.filename]#boot2# " "будет выполняться в пользовательском режиме внутри специального " "пользовательского сегмента, созданного сервером BTX. Этот сегмент начинается " "с адреса `0xa000`. Также помните, что часть [.filename]#boot2# в [." "filename]#boot# была скопирована по адресу `0xc000`, то есть со смещением " "`0x2000` от начала пользовательского сегмента, поэтому [.filename]#boot2# " "будет работать корректно при передаче управления на него. Далее, [." "filename]#boot2.bin# создается из [.filename]#boot2.out# путем удаления " "символов и информации о формате; boot2.bin представляет собой _сырой_ " "бинарный файл. Теперь обратите внимание, что файл [.filename]#boot2.ldr# " "создается как 512-байтный файл, заполненный нулями. Это пространство " "зарезервировано для bsdlabel." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:873 msgid "" "Now that we have files [.filename]#boot1#, [.filename]#boot2.bin# and [." "filename]#boot2.ldr#, only the BTX server is missing before creating the all-" "in-one [.filename]#boot# file. The BTX server is located in [." "filename]#stand/i386/btx/btx#; it has its own [.filename]#Makefile# with its " "own set of rules for building. The important thing to notice is that it is " "also compiled as a _raw_ binary, and that it is linked to execute at address " "`0x9000`. The details can be found in [.filename]#stand/i386/btx/btx/" "Makefile#." msgstr "" "Теперь, когда у нас есть файлы [.filename]#boot1#, [.filename]#boot2.bin# и " "[.filename]#boot2.ldr#, осталось только добавить сервер BTX перед созданием " "универсального файла [.filename]#boot#. Сервер BTX находится в [." "filename]#stand/i386/btx/btx#; у него есть собственный [.filename]#Makefile# " "со своим набором правил для сборки. Важно отметить, что он также " "компилируется как _сырой_ бинарный файл и линкуется для выполнения по адресу " "`0x9000`. Подробности можно найти в [.filename]#stand/i386/btx/btx/Makefile#." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:880 msgid "" "Having the files that comprise the [.filename]#boot# program, the final step " "is to _merge_ them. This is done by a special program called [." "filename]#btxld# (source located in [.filename]#/usr/src/usr.sbin/btxld#). " "Some arguments to this program include the name of the output file ([." "filename]#boot#), its entry point (`0x2000`) and its file format (raw " "binary). The various files are finally merged by this utility into the file " "[.filename]#boot#, which consists of [.filename]#boot1#, [.filename]#boot2#, " "the `bsdlabel` and the BTX server. This file, which takes exactly 16 " "sectors, or 8192 bytes, is what is actually written to the beginning of the " "FreeBSD slice during installation. Let us now proceed to study the BTX " "server program." msgstr "" "Имея файлы, составляющие программу [.filename]#boot#, последним шагом " "является их _объединение_. Это выполняется специальной программой под " "названием [.filename]#btxld# (исходный код расположен в [.filename]#/usr/src/" "usr.sbin/btxld#). Некоторые аргументы этой программы включают имя выходного " "файла ([.filename]#boot#), его точку входа (`0x2000`) и формат файла " "(бинарный). Различные файлы окончательно объединяются этой утилитой в файл [." "filename]#boot#, который состоит из [.filename]#boot1#, [.filename]#boot2#, " "`bsdlabel` и сервера BTX. Этот файл, занимающий ровно 16 секторов или 8192 " "байта, записывается в начало раздела FreeBSD во время установки. Теперь " "перейдем к изучению программы сервера BTX." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:883 msgid "" "The BTX server prepares a simple environment and switches from 16-bit real " "mode to 32-bit protected mode, right before passing control to the client. " "This includes initializing and updating the following data structures:" msgstr "" "Сервер BTX подготавливает простое окружение и переключается из 16-битного " "реального режима в 32-битный защищённый режим, непосредственно перед " "передачей управления клиенту. Это включает инициализацию и обновление " "следующих структур данных:" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:886 msgid "" "Modifies the `Interrupt Vector Table (IVT)`. The IVT provides exception and " "interrupt handlers for Real-Mode code." msgstr "" "Изменяет `Таблицу Векторов Прерываний (IVT)`. IVT предоставляет обработчики " "исключений и прерываний для кода в Реальном Режиме." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:889 msgid "" "The `Interrupt Descriptor Table (IDT)` is created. Entries are provided for " "processor exceptions, hardware interrupts, two system calls and V86 " "interface. The IDT provides exception and interrupt handlers for Protected-" "Mode code." msgstr "" "Создается `Таблица дескрипторов прерываний (IDT)`. В ней предусмотрены " "записи для исключений процессора, аппаратных прерываний, двух системных " "вызовов и интерфейса V86. IDT предоставляет обработчики исключений и " "прерываний для кода в защищенном режиме." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:891 msgid "" "A `Task-State Segment (TSS)` is created. This is necessary because the " "processor works in the _least_ privileged level when executing the client ([." "filename]#boot2#), but in the _most_ privileged level when executing the BTX " "server." msgstr "" "Создается `Сегмент состояния задачи (TSS)`. Это необходимо, потому что " "процессор работает на _наименее_ привилегированном уровне при выполнении " "клиента ([.filename]#boot2#), но на _наиболее_ привилегированном уровне при " "выполнении сервера BTX." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:894 msgid "" "The GDT (Global Descriptor Table) is set up. Entries (descriptors) are " "provided for supervisor code and data, user code and data, and real-mode " "code and data. footnote:[Real-mode code and data are necessary when " "switching back to real mode from protected mode, as suggested by the Intel " "manuals.]" msgstr "" "Устанавливается GDT (Глобальная Таблица Дескрипторов). Создаются записи " "(дескрипторы) для кода и данных супервизора, кода и данных пользователя, а " "также кода и данных реального режима. footnote:[Код и данные реального " "режима необходимы при переключении обратно в реальный режим из защищённого " "режима, как указано в руководствах Intel.]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:899 msgid "" "Let us now start studying the actual implementation. Recall that [." "filename]#boot1# made a jump to address `0x9010`, the BTX server's entry " "point. Before studying program execution there, note that the BTX server " "has a special header at address range `0x9000-0x900f`, right before its " "entry point. This header is defined as follows:" msgstr "" "Приступим к изучению фактической реализации. Напомним, что [." "filename]#boot1# выполнил переход на адрес `0x9010` — точку входа сервера " "BTX. Прежде чем изучать выполнение программы там, обратите внимание, что " "сервер BTX имеет специальный заголовок в диапазоне адресов `0x9000-0x900f`, " "непосредственно перед точкой входа. Этот заголовок определён следующим " "образом:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:915 #, no-wrap msgid "" "start:\t\t\t\t\t\t# Start of code\n" "/*\n" " * BTX header.\n" " */\n" "btx_hdr:\t.byte 0xeb\t\t\t# Machine ID\n" "\t\t.byte 0xe\t\t\t# Header size\n" "\t\t.ascii \"BTX\"\t\t\t# Magic\n" "\t\t.byte 0x1\t\t\t# Major version\n" "\t\t.byte 0x2\t\t\t# Minor version\n" "\t\t.byte BTX_FLAGS\t\t\t# Flags\n" "\t\t.word PAG_CNT-MEM_ORG>>0xc\t# Paging control\n" "\t\t.word break-start\t\t# Text size\n" "\t\t.long 0x0\t\t\t# Entry address\n" msgstr "" "start:\t\t\t\t\t\t# Start of code\n" "/*\n" " * BTX header.\n" " */\n" "btx_hdr:\t.byte 0xeb\t\t\t# Machine ID\n" "\t\t.byte 0xe\t\t\t# Header size\n" "\t\t.ascii \"BTX\"\t\t\t# Magic\n" "\t\t.byte 0x1\t\t\t# Major version\n" "\t\t.byte 0x2\t\t\t# Minor version\n" "\t\t.byte BTX_FLAGS\t\t\t# Flags\n" "\t\t.word PAG_CNT-MEM_ORG>>0xc\t# Paging control\n" "\t\t.word break-start\t\t# Text size\n" "\t\t.long 0x0\t\t\t# Entry address\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:917 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-header]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-header]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:922 msgid "" "Note the first two bytes are `0xeb` and `0xe`. In the IA-32 architecture, " "these two bytes are interpreted as a relative jump past the header into the " "entry point, so in theory, [.filename]#boot1# could jump here (address " "`0x9000`) instead of address `0x9010`. Note that the last field in the BTX " "header is a pointer to the client's ([.filename]#boot2#) entry pointb2. " "This field is patched at link time." msgstr "" "Обратите внимание, что первые два байта — это `0xeb` и `0xe`. В архитектуре " "IA-32 эти два байта интерпретируются как относительный переход за заголовок " "к точке входа, поэтому теоретически [.filename]#boot1# мог бы перейти сюда " "(адрес `0x9000`) вместо адреса `0x9010`. Обратите внимание, что последнее " "поле в заголовке BTX — это указатель на точку входа клиента ([." "filename]#boot2#)b2. Это поле исправляется во время компоновки." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:924 msgid "Immediately following the header is the BTX server's entry point:" msgstr "Сразу после заголовка следует точка входа сервера BTX:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:938 #, no-wrap msgid "" "/*\n" " * Initialization routine.\n" " */\n" "init:\t\tcli\t\t\t\t# Disable interrupts\n" "\t\txor %ax,%ax\t\t\t# Zero/segment\n" "\t\tmov %ax,%ss\t\t\t# Set up\n" "\t\tmov $MEM_ESP0,%sp\t\t# stack\n" "\t\tmov %ax,%es\t\t\t# Address\n" "\t\tmov %ax,%ds\t\t\t# data\n" "\t\tpushl $0x2\t\t\t# Clear\n" "\t\tpopfl\t\t\t\t# flags\n" msgstr "" "/*\n" " * Initialization routine.\n" " */\n" "init:\t\tcli\t\t\t\t# Disable interrupts\n" "\t\txor %ax,%ax\t\t\t# Zero/segment\n" "\t\tmov %ax,%ss\t\t\t# Set up\n" "\t\tmov $MEM_ESP0,%sp\t\t# stack\n" "\t\tmov %ax,%es\t\t\t# Address\n" "\t\tmov %ax,%ds\t\t\t# data\n" "\t\tpushl $0x2\t\t\t# Clear\n" "\t\tpopfl\t\t\t\t# flags\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:940 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-init]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-init]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:944 msgid "" "This code disables interrupts, sets up a working stack (starting at address " "`0x1800`) and clears the flags in the EFLAGS register. Note that the " "`popfl` instruction pops out a doubleword (4 bytes) from the stack and " "places it in the EFLAGS register. As the value actually popped is `2`, the " "EFLAGS register is effectively cleared (IA-32 requires that bit 2 of the " "EFLAGS register always be 1)." msgstr "" "Этот код отключает прерывания, устанавливает рабочий стек (начиная с адреса " "`0x1800`) и очищает флаги в регистре EFLAGS. Обратите внимание, что " "инструкция `popfl` извлекает двойное слово (4 байта) из стека и помещает его " "в регистр EFLAGS. Поскольку извлекаемое значение фактически равно `2`, " "регистр EFLAGS эффективно очищается (IA-32 требует, чтобы бит 2 регистра " "EFLAGS всегда был равен 1)." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:947 msgid "" "Our next code block clears (sets to `0`) the memory range `0x5e00-0x8fff`. " "This range is where the various data structures will be created:" msgstr "" "Следующий блок кода очищает (устанавливает в `0`) диапазон памяти " "`0x5e00-0x8fff`. В этом диапазоне будут созданы различные структуры данных:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:957 #, no-wrap msgid "" "/*\n" " * Initialize memory.\n" " */\n" "\t\tmov $MEM_IDT,%di\t\t# Memory to initialize\n" "\t\tmov $(MEM_ORG-MEM_IDT)/2,%cx\t# Words to zero\n" "\t\trep\t\t\t\t# Zero-fill\n" "\t\tstosw\t\t\t\t# memory\n" msgstr "" "/*\n" " * Initialize memory.\n" " */\n" "\t\tmov $MEM_IDT,%di\t\t# Memory to initialize\n" "\t\tmov $(MEM_ORG-MEM_IDT)/2,%cx\t# Words to zero\n" "\t\trep\t\t\t\t# Zero-fill\n" "\t\tstosw\t\t\t\t# memory\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:959 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-clear-mem]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-clear-mem]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:962 msgid "" "Recall that [.filename]#boot1# was originally loaded to address `0x7c00`, " "so, with this memory initialization, that copy effectively disappeared. " "However, also recall that [.filename]#boot1# was relocated to `0x700`, so " "_that_ copy is still in memory, and the BTX server will make use of it." msgstr "" "Напомним, что [.filename]#boot1# изначально загружался по адресу `0x7c00`, " "поэтому при такой инициализации памяти эта копия фактически исчезла. Однако " "также напомним, что [.filename]#boot1# был перемещён на адрес `0x700`, " "поэтому _эта_ копия всё ещё находится в памяти, и сервер BTX будет её " "использовать." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:970 msgid "" "Next, the real-mode IVT (Interrupt Vector Table is updated. The IVT is an " "array of segment/offset pairs for exception and interrupt handlers. The " "BIOS normally maps hardware interrupts to interrupt vectors `0x8` to `0xf` " "and `0x70` to `0x77` but, as will be seen, the 8259A Programmable Interrupt " "Controller, the chip controlling the actual mapping of hardware interrupts " "to interrupt vectors, is programmed to remap these interrupt vectors from " "`0x8-0xf` to `0x20-0x27` and from `0x70-0x77` to `0x28-0x2f`. Thus, " "interrupt handlers are provided for interrupt vectors `0x20-0x2f`. The " "reason the BIOS-provided handlers are not used directly is because they work " "in 16-bit real mode, but not 32-bit protected mode. Processor mode will be " "switched to 32-bit protected mode shortly. However, the BTX server sets up " "a mechanism to effectively use the handlers provided by the BIOS:" msgstr "" "Далее обновляется таблица векторов прерываний (IVT) в реальном режиме. IVT " "представляет собой массив пар сегмент/смещение для обработчиков исключений и " "прерываний. BIOS обычно сопоставляет аппаратные прерывания с векторами " "прерываний `0x8`–`0xf` и `0x70`–`0x77`, но, как будет показано, " "программируемый контроллер прерываний 8259A, микросхема, управляющая " "фактическим сопоставлением аппаратных прерываний с векторами прерываний, " "программируется для переназначения этих векторов прерываний с `0x8`–`0xf` на " "`0x20`–`0x27` и с `0x70`–`0x77` на `0x28`–`0x2f`. Таким образом, обработчики " "прерываний предоставляются для векторов прерываний `0x20`–`0x2f`. Причина, " "по которой обработчики, предоставляемые BIOS, не используются напрямую, " "заключается в том, что они работают в 16-битном реальном режиме, но не в 32-" "битном защищённом режиме. Вскоре будет выполнен переход в 32-битный " "защищённый режим. Однако сервер BTX настраивает механизм для эффективного " "использования обработчиков, предоставляемых BIOS:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:985 #, no-wrap msgid "" "/*\n" " * Update real mode IDT for reflecting hardware interrupts.\n" " */\n" "\t\tmov $intr20,%bx\t\t\t# Address first handler\n" "\t\tmov $0x10,%cx\t\t\t# Number of handlers\n" "\t\tmov $0x20*4,%di\t\t\t# First real mode IDT entry\n" "init.0:\t\tmov %bx,(%di)\t\t\t# Store IP\n" "\t\tinc %di\t\t\t\t# Address next\n" "\t\tinc %di\t\t\t\t# entry\n" "\t\tstosw\t\t\t\t# Store CS\n" "\t\tadd $4,%bx\t\t\t# Next handler\n" "\t\tloop init.0\t\t\t# Next IRQ\n" msgstr "" "/*\n" " * Update real mode IDT for reflecting hardware interrupts.\n" " */\n" "\t\tmov $intr20,%bx\t\t\t# Address first handler\n" "\t\tmov $0x10,%cx\t\t\t# Number of handlers\n" "\t\tmov $0x20*4,%di\t\t\t# First real mode IDT entry\n" "init.0:\t\tmov %bx,(%di)\t\t\t# Store IP\n" "\t\tinc %di\t\t\t\t# Address next\n" "\t\tinc %di\t\t\t\t# entry\n" "\t\tstosw\t\t\t\t# Store CS\n" "\t\tadd $4,%bx\t\t\t# Next handler\n" "\t\tloop init.0\t\t\t# Next IRQ\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:987 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-ivt]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-ivt]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:992 msgid "" "The next block creates the IDT (Interrupt Descriptor Table). The IDT is " "analogous, in protected mode, to the IVT in real mode. That is, the IDT " "describes the various exception and interrupt handlers used when the " "processor is executing in protected mode. In essence, it also consists of " "an array of segment/offset pairs, although the structure is somewhat more " "complex, because segments in protected mode are different than in real mode, " "and various protection mechanisms apply:" msgstr "" "Следующий блок создает IDT (таблицу дескрипторов прерываний). IDT в " "защищенном режиме аналогична IVT в реальном режиме. То есть, IDT описывает " "различные обработчики исключений и прерываний, используемые, когда процессор " "работает в защищенном режиме. По сути, она также состоит из массива пар " "сегмент/смещение, хотя структура несколько сложнее, поскольку сегменты в " "защищенном режиме отличаются от реального режима, и применяются различные " "механизмы защиты:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1019 #, no-wrap msgid "" "/*\n" " * Create IDT.\n" " */\n" "\t\tmov $MEM_IDT,%di\t\t# IDT's address\n" "\t\tmov $idtctl,%si\t\t\t# Control string\n" "init.1:\t\tlodsb\t\t\t\t# Get entry\n" "\t\tcbw\t\t\t\t# count\n" "\t\txchg %ax,%cx\t\t\t# as word\n" "\t\tjcxz init.4\t\t\t# If done\n" "\t\tlodsb\t\t\t\t# Get segment\n" "\t\txchg %ax,%dx\t\t\t# P:DPL:type\n" "\t\tlodsw\t\t\t\t# Get control\n" "\t\txchg %ax,%bx\t\t\t# set\n" "\t\tlodsw\t\t\t\t# Get handler offset\n" "\t\tmov $SEL_SCODE,%dh\t\t# Segment selector\n" "init.2:\t\tshr %bx\t\t\t\t# Handle this int?\n" "\t\tjnc init.3\t\t\t# No\n" "\t\tmov %ax,(%di)\t\t\t# Set handler offset\n" "\t\tmov %dh,0x2(%di)\t\t# and selector\n" "\t\tmov %dl,0x5(%di)\t\t# Set P:DPL:type\n" "\t\tadd $0x4,%ax\t\t\t# Next handler\n" "init.3:\t\tlea 0x8(%di),%di\t\t# Next entry\n" "\t\tloop init.2\t\t\t# Till set done\n" "\t\tjmp init.1\t\t\t# Continue\n" msgstr "" "/*\n" " * Create IDT.\n" " */\n" "\t\tmov $MEM_IDT,%di\t\t# IDT's address\n" "\t\tmov $idtctl,%si\t\t\t# Control string\n" "init.1:\t\tlodsb\t\t\t\t# Get entry\n" "\t\tcbw\t\t\t\t# count\n" "\t\txchg %ax,%cx\t\t\t# as word\n" "\t\tjcxz init.4\t\t\t# If done\n" "\t\tlodsb\t\t\t\t# Get segment\n" "\t\txchg %ax,%dx\t\t\t# P:DPL:type\n" "\t\tlodsw\t\t\t\t# Get control\n" "\t\txchg %ax,%bx\t\t\t# set\n" "\t\tlodsw\t\t\t\t# Get handler offset\n" "\t\tmov $SEL_SCODE,%dh\t\t# Segment selector\n" "init.2:\t\tshr %bx\t\t\t\t# Handle this int?\n" "\t\tjnc init.3\t\t\t# No\n" "\t\tmov %ax,(%di)\t\t\t# Set handler offset\n" "\t\tmov %dh,0x2(%di)\t\t# and selector\n" "\t\tmov %dl,0x5(%di)\t\t# Set P:DPL:type\n" "\t\tadd $0x4,%ax\t\t\t# Next handler\n" "init.3:\t\tlea 0x8(%di),%di\t\t# Next entry\n" "\t\tloop init.2\t\t\t# Till set done\n" "\t\tjmp init.1\t\t\t# Continue\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1021 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-idt]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-idt]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1029 msgid "" "Each entry in the `IDT` is 8 bytes long. Besides the segment/offset " "information, they also describe the segment type, privilege level, and " "whether the segment is present in memory or not. The construction is such " "that interrupt vectors from `0` to `0xf` (exceptions) are handled by " "function `intx00`; vector `0x10` (also an exception) is handled by `intx10`; " "hardware interrupts, which are later configured to start at interrupt vector " "`0x20` all the way to interrupt vector `0x2f`, are handled by function " "`intx20`. Lastly, interrupt vector `0x30`, which is used for system calls, " "is handled by `intx30`, and vectors `0x31` and `0x32` are handled by " "`intx31`. It must be noted that only descriptors for interrupt vectors " "`0x30`, `0x31` and `0x32` are given privilege level 3, the same privilege " "level as the [.filename]#boot2# client, which means the client can execute a " "software-generated interrupt to this vectors through the `int` instruction " "without failing (this is the way [.filename]#boot2# use the services " "provided by the BTX server). Also, note that _only_ software-generated " "interrupts are protected from code executing in lesser privilege levels. " "Hardware-generated interrupts and processor-generated exceptions are " "_always_ handled adequately, regardless of the actual privileges involved." msgstr "" "Каждая запись в `IDT` имеет длину 8 байт. Помимо информации о сегменте/" "смещении, они также описывают тип сегмента, уровень привилегий и " "присутствует ли сегмент в памяти. Структура организована так, что векторы " "прерываний от `0` до `0xf` (исключения) обрабатываются функцией `intx00`; " "вектор `0x10` (также исключение) обрабатывается `intx10`; аппаратные " "прерывания, которые позже настраиваются начиная с вектора `0x20` и до " "вектора `0x2f`, обрабатываются функцией `intx20`. Наконец, вектор прерывания " "`0x30`, используемый для системных вызовов, обрабатывается `intx30`, а " "векторы `0x31` и `0x32` обрабатываются `intx31`. Необходимо отметить, что " "только дескрипторы для векторов прерываний `0x30`, `0x31` и `0x32` имеют " "уровень привилегий 3, такой же, как у клиента [.filename]#boot2#, что " "означает, что клиент может выполнить программно-генерируемое прерывание к " "этим векторам через инструкцию `int` без ошибки (это способ, которым [." "filename]#boot2# использует сервисы, предоставляемые сервером BTX). Также " "обратите внимание, что _только_ программно-генерируемые прерывания защищены " "от кода, выполняющегося на более низких уровнях привилегий. Аппаратно-" "генерируемые прерывания и исключения, генерируемые процессором, _всегда_ " "обрабатываются корректно, независимо от фактических привилегий." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1034 msgid "" "The next step is to initialize the TSS (Task-State Segment). The TSS is a " "hardware feature that helps the operating system or executive software " "implement multitasking functionality through process abstraction. The IA-32 " "architecture demands the creation and use of _at least_ one TSS if " "multitasking facilities are used or different privilege levels are defined. " "Since the [.filename]#boot2# client is executed in privilege level 3, but " "the BTX server runs in privilege level 0, a TSS must be defined:" msgstr "" "Следующий шаг — инициализация TSS (сегмента состояния задачи). TSS — это " "аппаратная функция, которая помогает операционной системе или " "исполнительному ПО реализовать многозадачность через абстракцию процессов. " "Архитектура IA-32 требует создания и использования _как минимум_ одного TSS, " "если используются механизмы многозадачности или определены различные уровни " "привилегий. Поскольку клиент [.filename]#boot2# выполняется на уровне " "привилегий 3, а сервер BTX работает на уровне привилегий 0, необходимо " "определить TSS:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1043 #, no-wrap msgid "" "/*\n" " * Initialize TSS.\n" " */\n" "init.4:\t\tmovb $_ESP0H,TSS_ESP0+1(%di)\t# Set ESP0\n" "\t\tmovb $SEL_SDATA,TSS_SS0(%di)\t# Set SS0\n" "\t\tmovb $_TSSIO,TSS_MAP(%di)\t# Set I/O bit map base\n" msgstr "" "/*\n" " * Initialize TSS.\n" " */\n" "init.4:\t\tmovb $_ESP0H,TSS_ESP0+1(%di)\t# Set ESP0\n" "\t\tmovb $SEL_SDATA,TSS_SS0(%di)\t# Set SS0\n" "\t\tmovb $_TSSIO,TSS_MAP(%di)\t# Set I/O bit map base\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1045 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-tss]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-tss]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1049 msgid "" "Note that a value is given for the Privilege Level 0 stack pointer and stack " "segment in the TSS. This is needed because, if an interrupt or exception is " "received while executing [.filename]#boot2# in Privilege Level 3, a change " "to Privilege Level 0 is automatically performed by the processor, so a new " "working stack is needed. Finally, the I/O Map Base Address field of the TSS " "is given a value, which is a 16-bit offset from the beginning of the TSS to " "the I/O Permission Bitmap and the Interrupt Redirection Bitmap." msgstr "" "Обратите внимание, что в TSS указано значение для указателя стека и сегмента " "стека уровня привилегий 0. Это необходимо, потому что если прерывание или " "исключение получено во время выполнения [.filename]#boot2# на уровне " "привилегий 3, процессор автоматически переключается на уровень привилегий 0, " "поэтому требуется новый рабочий стек. Наконец, полю базового адреса карты " "ввода-вывода TSS присваивается значение, которое представляет собой 16-" "битное смещение от начала TSS до битовой карты разрешений ввода-вывода и " "битовой карты перенаправления прерываний." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1052 msgid "" "After the IDT and TSS are created, the processor is ready to switch to " "protected mode. This is done in the next block:" msgstr "" "После создания IDT и TSS процессор готов к переходу в защищённый режим. Это " "выполняется в следующем блоке:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1070 #, no-wrap msgid "" "/*\n" " * Bring up the system.\n" " */\n" "\t\tmov $0x2820,%bx\t\t\t# Set protected mode\n" "\t\tcallw setpic\t\t\t# IRQ offsets\n" "\t\tlidt idtdesc\t\t\t# Set IDT\n" "\t\tlgdt gdtdesc\t\t\t# Set GDT\n" "\t\tmov %cr0,%eax\t\t\t# Switch to protected\n" "\t\tinc %ax\t\t\t\t# mode\n" "\t\tmov %eax,%cr0\t\t\t#\n" "\t\tljmp $SEL_SCODE,$init.8\t\t# To 32-bit code\n" "\t\t.code32\n" "init.8:\t\txorl %ecx,%ecx\t\t\t# Zero\n" "\t\tmovb $SEL_SDATA,%cl\t\t# To 32-bit\n" "\t\tmovw %cx,%ss\t\t\t# stack\n" msgstr "" "/*\n" " * Bring up the system.\n" " */\n" "\t\tmov $0x2820,%bx\t\t\t# Set protected mode\n" "\t\tcallw setpic\t\t\t# IRQ offsets\n" "\t\tlidt idtdesc\t\t\t# Set IDT\n" "\t\tlgdt gdtdesc\t\t\t# Set GDT\n" "\t\tmov %cr0,%eax\t\t\t# Switch to protected\n" "\t\tinc %ax\t\t\t\t# mode\n" "\t\tmov %eax,%cr0\t\t\t#\n" "\t\tljmp $SEL_SCODE,$init.8\t\t# To 32-bit code\n" "\t\t.code32\n" "init.8:\t\txorl %ecx,%ecx\t\t\t# Zero\n" "\t\tmovb $SEL_SDATA,%cl\t\t# To 32-bit\n" "\t\tmovw %cx,%ss\t\t\t# stack\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1072 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-prot]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-prot]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1085 msgid "" "First, a call is made to `setpic` to program the 8259A PIC (Programmable " "Interrupt Controller). This chip is connected to multiple hardware " "interrupt sources. Upon receiving an interrupt from a device, it signals " "the processor with the appropriate interrupt vector. This can be customized " "so that specific interrupts are associated with specific interrupt vectors, " "as explained before. Next, the IDTR (Interrupt Descriptor Table Register) " "and GDTR (Global Descriptor Table Register) are loaded with the instructions " "`lidt` and `lgdt`, respectively. These registers are loaded with the base " "address and limit address for the IDT and GDT. The following three " "instructions set the Protection Enable (PE) bit of the `%cr0` register. " "This effectively switches the processor to 32-bit protected mode. Next, a " "long jump is made to `init.8` using segment selector SEL_SCODE, which " "selects the Supervisor Code Segment. The processor is effectively executing " "in CPL 0, the most privileged level, after this jump. Finally, the " "Supervisor Data Segment is selected for the stack by assigning the segment " "selector SEL_SDATA to the `%ss` register. This data segment also has a " "privilege level of `0`." msgstr "" "Сначала вызывается `setpic` для программирования 8259A PIC (программируемого " "контроллера прерываний). Этот чип подключен к нескольким источникам " "аппаратных прерываний. При получении прерывания от устройства он " "сигнализирует процессору соответствующим вектором прерывания. Это можно " "настроить так, чтобы определённые прерывания были связаны с конкретными " "векторами прерываний, как объяснялось ранее. Затем регистры IDTR (Interrupt " "Descriptor Table Register) и GDTR (Global Descriptor Table Register) " "загружаются инструкциями `lidt` и `lgdt` соответственно. Эти регистры " "загружаются базовым адресом и предельным адресом для IDT и GDT. Следующие " "три инструкции устанавливают бит Protection Enable (PE) в регистре `%cr0`. " "Это фактически переключает процессор в 32-битный защищенный режим. Затем " "выполняется дальний переход на `init.8` с использованием селектора сегмента " "SEL_SCODE, который выбирает сегмент кода супервизора (Supervisor Code " "Segment). После этого перехода процессор фактически работает на уровне CPL 0 " "— наиболее привилегированном уровне. Наконец, для стека выбирается сегмент " "данных супервизора (Supervisor Data Segment) путем присвоения селектора " "сегмента SEL_SDATA регистру `%ss`. Этот сегмент данных также имеет уровень " "привилегий `0`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1087 msgid "" "Our last code block is responsible for loading the TR (Task Register) with " "the segment selector for the TSS we created earlier, and setting the User " "Mode environment before passing execution control to the [.filename]#boot2# " "client." msgstr "" "Наш последний блок кода отвечает за загрузку TR (Регистра Задач) с " "селектором сегмента для TSS, который мы создали ранее, и настройку окружения " "пользовательского режима перед передачей управления исполнения клиенту [." "filename]#boot2#." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1123 #, no-wrap msgid "" "/*\n" " * Launch user task.\n" " */\n" "\t\tmovb $SEL_TSS,%cl\t\t# Set task\n" "\t\tltr %cx\t\t\t\t# register\n" "\t\tmovl $MEM_USR,%edx\t\t# User base address\n" "\t\tmovzwl %ss:BDA_MEM,%eax\t\t# Get free memory\n" "\t\tshll $0xa,%eax\t\t\t# To bytes\n" "\t\tsubl $ARGSPACE,%eax\t\t# Less arg space\n" "\t\tsubl %edx,%eax\t\t\t# Less base\n" "\t\tmovb $SEL_UDATA,%cl\t\t# User data selector\n" "\t\tpushl %ecx\t\t\t# Set SS\n" "\t\tpushl %eax\t\t\t# Set ESP\n" "\t\tpush $0x202\t\t\t# Set flags (IF set)\n" "\t\tpush $SEL_UCODE\t\t\t# Set CS\n" "\t\tpushl btx_hdr+0xc\t\t# Set EIP\n" "\t\tpushl %ecx\t\t\t# Set GS\n" "\t\tpushl %ecx\t\t\t# Set FS\n" "\t\tpushl %ecx\t\t\t# Set DS\n" "\t\tpushl %ecx\t\t\t# Set ES\n" "\t\tpushl %edx\t\t\t# Set EAX\n" "\t\tmovb $0x7,%cl\t\t\t# Set remaining\n" "init.9:\t\tpush $0x0\t\t\t# general\n" "\t\tloop init.9\t\t\t# registers\n" "#ifdef BTX_SERIAL\n" "\t\tcall sio_init\t\t\t# setup the serial console\n" "#endif\n" "\t\tpopa\t\t\t\t# and initialize\n" "\t\tpopl %es\t\t\t# Initialize\n" "\t\tpopl %ds\t\t\t# user\n" "\t\tpopl %fs\t\t\t# segment\n" "\t\tpopl %gs\t\t\t# registers\n" "\t\tiret\t\t\t\t# To user mode\n" msgstr "" "/*\n" " * Launch user task.\n" " */\n" "\t\tmovb $SEL_TSS,%cl\t\t# Set task\n" "\t\tltr %cx\t\t\t\t# register\n" "\t\tmovl $MEM_USR,%edx\t\t# User base address\n" "\t\tmovzwl %ss:BDA_MEM,%eax\t\t# Get free memory\n" "\t\tshll $0xa,%eax\t\t\t# To bytes\n" "\t\tsubl $ARGSPACE,%eax\t\t# Less arg space\n" "\t\tsubl %edx,%eax\t\t\t# Less base\n" "\t\tmovb $SEL_UDATA,%cl\t\t# User data selector\n" "\t\tpushl %ecx\t\t\t# Set SS\n" "\t\tpushl %eax\t\t\t# Set ESP\n" "\t\tpush $0x202\t\t\t# Set flags (IF set)\n" "\t\tpush $SEL_UCODE\t\t\t# Set CS\n" "\t\tpushl btx_hdr+0xc\t\t# Set EIP\n" "\t\tpushl %ecx\t\t\t# Set GS\n" "\t\tpushl %ecx\t\t\t# Set FS\n" "\t\tpushl %ecx\t\t\t# Set DS\n" "\t\tpushl %ecx\t\t\t# Set ES\n" "\t\tpushl %edx\t\t\t# Set EAX\n" "\t\tmovb $0x7,%cl\t\t\t# Set remaining\n" "init.9:\t\tpush $0x0\t\t\t# general\n" "\t\tloop init.9\t\t\t# registers\n" "#ifdef BTX_SERIAL\n" "\t\tcall sio_init\t\t\t# setup the serial console\n" "#endif\n" "\t\tpopa\t\t\t\t# and initialize\n" "\t\tpopl %es\t\t\t# Initialize\n" "\t\tpopl %ds\t\t\t# user\n" "\t\tpopl %fs\t\t\t# segment\n" "\t\tpopl %gs\t\t\t# registers\n" "\t\tiret\t\t\t\t# To user mode\n" #. type: Block title #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1125 #, no-wrap msgid "[.filename]#stand/i386/btx/btx/btx.S# [[btx-end]]" msgstr "[.filename]#stand/i386/btx/btx/btx.S# [[btx-end]]" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1149 msgid "" "Note that the client's environment include a stack segment selector and " "stack pointer (registers `%ss` and `%esp`). Indeed, once the TR is loaded " "with the appropriate stack segment selector (instruction `ltr`), the stack " "pointer is calculated and pushed onto the stack along with the stack's " "segment selector. Next, the value `0x202` is pushed onto the stack; it is " "the value that the EFLAGS will get when control is passed to the client. " "Also, the User Mode code segment selector and the client's entry point are " "pushed. Recall that this entry point is patched in the BTX header at link " "time. Finally, segment selectors (stored in register `%ecx`) for the " "segment registers `%gs, %fs, %ds and %es` are pushed onto the stack, along " "with the value at `%edx` (`0xa000`). Keep in mind the various values that " "have been pushed onto the stack (they will be popped out shortly). Next, " "values for the remaining general purpose registers are also pushed onto the " "stack (note the `loop` that pushes the value `0` seven times). Now, values " "will be started to be popped out of the stack. First, the `popa` " "instruction pops out of the stack the latest seven values pushed. They are " "stored in the general purpose registers in order `%edi, %esi, %ebp, %ebx, " "%edx, %ecx, %eax`. Then, the various segment selectors pushed are popped " "into the various segment registers. Five values still remain on the stack. " "They are popped when the `iret` instruction is executed. This instruction " "first pops the value that was pushed from the BTX header. This value is a " "pointer to [.filename]#boot2#'s entry point. It is placed in the register `" "%eip`, the instruction pointer register. Next, the segment selector for the " "User Code Segment is popped and copied to register `%cs`. Remember that " "this segment's privilege level is 3, the least privileged level. This means " "that we must provide values for the stack of this privilege level. This is " "why the processor, besides further popping the value for the EFLAGS " "register, does two more pops out of the stack. These values go to the stack " "pointer (`%esp`) and the stack segment (`%ss`). Now, execution continues at " "``boot0``'s entry point." msgstr "" "Обратите внимание, что среда клиента включает селектор сегмента стека и " "указатель стека (регистры `%ss` и `%esp`). Действительно, как только TR " "загружается соответствующим селектором сегмента стека (инструкция `ltr`), " "указатель стека вычисляется и помещается в стек вместе с селектором сегмента " "стека. Затем значение `0x202` помещается в стек; это значение, которое " "EFLAGS получит при передаче управления клиенту. Также в стек помещаются " "селектор сегмента кода пользовательского режима и точка входа клиента. " "Напомним, что эта точка входа прописывается в заголовке BTX во время " "компоновки. Наконец, селекторы сегментов (хранящиеся в регистре `%ecx`) для " "регистров сегментов `%gs, %fs, %ds и %es` помещаются в стек вместе со " "значением из `%edx` (`0xa000`). Примите во внимание эти значения, помещенные " "в стек (они скоро будут извлечены). Затем значения для оставшихся регистров " "общего назначения также помещаются в стек (обратите внимание на цикл `loop`, " "который помещает значение `0` семь раз). Теперь начнётся извлечение значений " "из стека. Сначала инструкция `popa` извлекает из стека последние семь " "помещённых значений. Они сохраняются в регистрах общего назначения в порядке " "`%edi, %esi, %ebp, %ebx, %edx, %ecx, %eax`. Затем различные селекторы " "сегментов, помещённые в стек, извлекаются в соответствующие регистры " "сегментов. В стеке остаются ещё пять значений. Они извлекаются при " "выполнении инструкции `iret`. Эта инструкция сначала извлекает значение, " "которое было помещено из заголовка BTX. Это значение является указателем на " "точку входа [.filename]#boot2#. Оно помещается в регистр `%eip` — регистр " "указателя инструкций. Затем селектор сегмента кода пользователя извлекается " "и копируется в регистр `%cs`. Помните, что уровень привилегий этого сегмента " "— 3, наименее привилегированный уровень. Это означает, что мы должны " "предоставить значения для стека этого уровня привилегий. Именно поэтому " "процессор, помимо дальнейшего извлечения значения для регистра EFLAGS, " "выполняет ещё два извлечения из стека. Эти значения попадают в указатель " "стека (`%esp`) и сегмент стека (`%ss`). Теперь выполнение продолжается с " "точки входа ``boot0``." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1153 msgid "" "It is important to note how the User Code Segment is defined. This " "segment's _base address_ is set to `0xa000`. This means that code memory " "addresses are _relative_ to address 0xa000; if code being executed is " "fetched from address `0x2000`, the _actual_ memory addressed is " "`0xa000+0x2000=0xc000`." msgstr "" "Важно отметить, как определяется сегмент пользовательского кода. _Базовый " "адрес_ этого сегмента установлен на `0xa000`. Это означает, что адреса " "памяти кода являются _относительными_ к адресу 0xa000; если код, который " "выполняется, извлекается из адреса `0x2000`, _фактический_ адрес в памяти " "будет `0xa000+0x2000=0xc000`." #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1155 #, no-wrap msgid "boot2 Stage" msgstr "Этап загрузки boot2" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1162 msgid "" "`boot2` defines an important structure, `struct bootinfo`. This structure " "is initialized by `boot2` and passed to the loader, and then further to the " "kernel. Some nodes of this structures are set by `boot2`, the rest by the " "loader. This structure, among other information, contains the kernel " "filename, BIOS harddisk geometry, BIOS drive number for boot device, " "physical memory available, `envp` pointer etc. The definition for it is:" msgstr "" "`boot2` определяет важную структуру, `struct bootinfo`. Эта структура " "инициализируется `boot2` и передаётся загрузчику, а затем ядру. Некоторые " "узлы этой структуры устанавливаются `boot2`, остальные — загрузчиком. Эта " "структура, среди прочей информации, содержит имя файла ядра, геометрию " "жёсткого диска в BIOS, номер диска в BIOS для загрузочного устройства, " "доступную физическую память, указатель `envp` и т.д. Ее определение выглядит " "так:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1187 #, no-wrap msgid "" "/usr/include/machine/bootinfo.h:\n" "struct bootinfo {\n" "\tu_int32_t\tbi_version;\n" "\tu_int32_t\tbi_kernelname;\t\t/* represents a char * */\n" "\tu_int32_t\tbi_nfs_diskless;\t/* struct nfs_diskless * */\n" "\t\t\t\t/* End of fields that are always present. */\n" "#define\tbi_endcommon\tbi_n_bios_used\n" "\tu_int32_t\tbi_n_bios_used;\n" "\tu_int32_t\tbi_bios_geom[N_BIOS_GEOM];\n" "\tu_int32_t\tbi_size;\n" "\tu_int8_t\tbi_memsizes_valid;\n" "\tu_int8_t\tbi_bios_dev;\t\t/* bootdev BIOS unit number */\n" "\tu_int8_t\tbi_pad[2];\n" "\tu_int32_t\tbi_basemem;\n" "\tu_int32_t\tbi_extmem;\n" "\tu_int32_t\tbi_symtab;\t\t/* struct symtab * */\n" "\tu_int32_t\tbi_esymtab;\t\t/* struct symtab * */\n" "\t\t\t\t/* Items below only from advanced bootloader */\n" "\tu_int32_t\tbi_kernend;\t\t/* end of kernel space */\n" "\tu_int32_t\tbi_envp;\t\t/* environment */\n" "\tu_int32_t\tbi_modulep;\t\t/* preloaded modules */\n" "};\n" msgstr "" "/usr/include/machine/bootinfo.h:\n" "struct bootinfo {\n" "\tu_int32_t\tbi_version;\n" "\tu_int32_t\tbi_kernelname;\t\t/* represents a char * */\n" "\tu_int32_t\tbi_nfs_diskless;\t/* struct nfs_diskless * */\n" "\t\t\t\t/* End of fields that are always present. */\n" "#define\tbi_endcommon\tbi_n_bios_used\n" "\tu_int32_t\tbi_n_bios_used;\n" "\tu_int32_t\tbi_bios_geom[N_BIOS_GEOM];\n" "\tu_int32_t\tbi_size;\n" "\tu_int8_t\tbi_memsizes_valid;\n" "\tu_int8_t\tbi_bios_dev;\t\t/* bootdev BIOS unit number */\n" "\tu_int8_t\tbi_pad[2];\n" "\tu_int32_t\tbi_basemem;\n" "\tu_int32_t\tbi_extmem;\n" "\tu_int32_t\tbi_symtab;\t\t/* struct symtab * */\n" "\tu_int32_t\tbi_esymtab;\t\t/* struct symtab * */\n" "\t\t\t\t/* Items below only from advanced bootloader */\n" "\tu_int32_t\tbi_kernend;\t\t/* end of kernel space */\n" "\tu_int32_t\tbi_envp;\t\t/* environment */\n" "\tu_int32_t\tbi_modulep;\t\t/* preloaded modules */\n" "};\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1194 msgid "" "`boot2` enters into an infinite loop waiting for user input, then calls " "`load()`. If the user does not press anything, the loop breaks by a " "timeout, so `load()` will load the default file ([.filename]#/boot/" "loader#). Functions `ino_t lookup(char *filename)` and `int xfsread(ino_t " "inode, void *buf, size_t nbyte)` are used to read the content of a file into " "memory. [.filename]#/boot/loader# is an ELF binary, but where the ELF " "header is prepended with [.filename]#a.out#'s `struct exec` structure. " "`load()` scans the loader's ELF header, loading the content of [.filename]#/" "boot/loader# into memory, and passing the execution to the loader's entry:" msgstr "" "`boot2` входит в бесконечный цикл, ожидая ввода пользователя, затем вызывает " "`load()`. Если пользователь ничего не нажимает, цикл прерывается по " "таймауту, и `load()` загружает файл по умолчанию ([.filename]#/boot/" "loader#). Функции `ino_t lookup(char *filename)` и `int xfsread(ino_t inode, " "void *buf, size_t nbyte)` используются для чтения содержимого файла в " "память. [.filename]#/boot/loader# — это ELF-бинарный файл, но с заголовком " "ELF, перед которым добавлена структура `struct exec` из [.filename]#a.out#. " "`load()` анализирует ELF-заголовок загрузчика, загружает содержимое [." "filename]#/boot/loader# в память и передаёт управление на точку входа " "загрузчика:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1201 #, no-wrap msgid "" "stand/i386/boot2/boot2.c:\n" " __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),\n" "\t MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),\n" "\t 0, 0, 0, VTOP(&bootinfo));\n" msgstr "" "stand/i386/boot2/boot2.c:\n" " __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),\n" "\t MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),\n" "\t 0, 0, 0, VTOP(&bootinfo));\n" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1204 #, no-wrap msgid "loader Stage" msgstr "Этап загрузчика (loader)" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1209 msgid "" "loader is a BTX client as well. I will not describe it here in detail, " "there is a comprehensive man page written by Mike Smith, man:loader[8]. The " "underlying mechanisms and BTX were discussed above." msgstr "" "Загрузчик также является клиентом BTX. Я не буду подробно описывать его " "здесь, существует исчерпывающая man-страница, написанная Майком Смитом: man:" "loader[8]. Основные механизмы и BTX были рассмотрены выше." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1212 msgid "" "The main task for the loader is to boot the kernel. When the kernel is " "loaded into memory, it is being called by the loader:" msgstr "" "Основная задача загрузчика — загрузить ядро. Когда ядро загружено в память, " "загрузчик вызывает его:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1218 #, no-wrap msgid "" "stand/common/boot.c:\n" " /* Call the exec handler from the loader matching the kernel */\n" " file_formats[fp->f_loader]->l_exec(fp);\n" msgstr "" "stand/common/boot.c:\n" " /* Call the exec handler from the loader matching the kernel */\n" " file_formats[fp->f_loader]->l_exec(fp);\n" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1221 #, no-wrap msgid "Kernel Initialization" msgstr "Инициализация ядра" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1228 msgid "" "Let us take a look at the command that links the kernel. This will help " "identify the exact location where the loader passes execution to the " "kernel. This location is the kernel's actual entry point. This command is " "now excluded from [.filename]#sys/conf/Makefile.i386#. The content that " "interests us can be found in [.filename]#/usr/obj/usr/src/i386.i386/sys/" "GENERIC/#." msgstr "" "Давайте рассмотрим команду, которая компонует ядро. Это поможет определить " "точное местоположение, где загрузчик передаёт выполнение ядру. Это " "местоположение является фактической точкой входа ядра. Данная команда теперь " "исключена из [.filename]#sys/conf/Makefile.i386#. Интересующее нас " "содержимое можно найти в [.filename]#/usr/obj/usr/src/i386.i386/sys/GENERIC/" "#." #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1235 #, no-wrap msgid "" "/usr/obj/usr/src/i386.i386/sys/GENERIC/kernel.meta:\n" "ld -m elf_i386_fbsd -Bdynamic -T /usr/src/sys/conf/ldscript.i386 --build-id=sha1 --no-warn-mismatch \\\n" "--warn-common --export-dynamic --dynamic-linker /red/herring -X -o kernel locore.o\n" "\n" msgstr "" "/usr/obj/usr/src/i386.i386/sys/GENERIC/kernel.meta:\n" "ld -m elf_i386_fbsd -Bdynamic -T /usr/src/sys/conf/ldscript.i386 --build-id=sha1 --no-warn-mismatch \\\n" "--warn-common --export-dynamic --dynamic-linker /red/herring -X -o kernel locore.o\n" "\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1241 msgid "" "A few interesting things can be seen here. First, the kernel is an ELF " "dynamically linked binary, but the dynamic linker for kernel is [.filename]#/" "red/herring#, which is definitely a bogus file. Second, taking a look at " "the file [.filename]#sys/conf/ldscript.i386# gives an idea about what ld " "options are used when compiling a kernel. Reading through the first few " "lines, the string" msgstr "" "Вот несколько интересных наблюдений. Во-первых, ядро представляет собой " "динамически связанный бинарный файл ELF, но динамический компоновщик для " "ядра — это [.filename]#/red/herring#, что явно является фиктивным файлом. Во-" "вторых, взглянув на файл [.filename]#sys/conf/ldscript.i386#, можно понять, " "какие параметры ld используются при компиляции ядра. Читая первые несколько " "строк, видим, что строка" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1246 #, no-wrap msgid "" "sys/conf/ldscript.i386:\n" "ENTRY(btext)\n" msgstr "" "sys/conf/ldscript.i386:\n" "ENTRY(btext)\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1250 msgid "" "says that a kernel's entry point is the symbol `btext`. This symbol is " "defined in [.filename]#locore.s#:" msgstr "" "говорит, что точка входа ядра — это символ `btext`. Этот символ определён в " "[.filename]#locore.s#:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1261 #, no-wrap msgid "" "sys/i386/i386/locore.s:\n" "\t.text\n" "/**********************************************************************\n" " *\n" " * This is where the bootblocks start us, set the ball rolling...\n" " *\n" " */\n" "NON_GPROF_ENTRY(btext)\n" msgstr "" "sys/i386/i386/locore.s:\n" "\t.text\n" "/**********************************************************************\n" " *\n" " * This is where the bootblocks start us, set the ball rolling...\n" " *\n" " */\n" "NON_GPROF_ENTRY(btext)\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1265 msgid "" "First, the register EFLAGS is set to a predefined value of 0x00000002. Then " "all the segment registers are initialized:" msgstr "" "Сначала регистр EFLAGS устанавливается в предопределённое значение " "0x00000002. Затем инициализируются все сегментные регистры:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1272 #, no-wrap msgid "" "sys/i386/i386/locore.s:\n" "/* Don't trust what the BIOS gives for eflags. */\n" "\tpushl\t$PSL_KERNEL\n" "\tpopfl\n" msgstr "" "sys/i386/i386/locore.s:\n" "/* Don't trust what the BIOS gives for eflags. */\n" "\tpushl\t$PSL_KERNEL\n" "\tpopfl\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1280 #, no-wrap msgid "" "/*\n" " * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap\n" " * to set %cs, %ds, %es and %ss.\n" " */\n" "\tmov\t%ds, %ax\n" "\tmov\t%ax, %fs\n" "\tmov\t%ax, %gs\n" msgstr "" "/*\n" " * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap\n" " * to set %cs, %ds, %es and %ss.\n" " */\n" "\tmov\t%ds, %ax\n" "\tmov\t%ax, %fs\n" "\tmov\t%ax, %gs\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1284 msgid "" "btext calls the routines `recover_bootinfo()`, `identify_cpu()`, which are " "also defined in [.filename]#locore.s#. Here is a description of what they " "do:" msgstr "" "btext вызывает подпрограммы `recover_bootinfo()` и `identify_cpu()`, которые " "также определены в [.filename]#locore.s#. Вот описание их функций:" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1290 #, no-wrap msgid "`recover_bootinfo`" msgstr "`recover_bootinfo`" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1294 #, no-wrap msgid "" "This routine parses the parameters to the kernel passed from the bootstrap.\n" "The kernel may have been booted in 3 ways: by the loader, described above, by the old disk boot blocks, or by the old diskless boot procedure.\n" "This function determines the booting method, and stores the `struct bootinfo` structure into the kernel memory." msgstr "" "Эта процедура разбирает параметры, переданные ядру при загрузке.\n" "Ядро могло быть загружено тремя способами: загрузчиком (как описано выше), старыми загрузочными блоками диска или по старой процедуре загрузки без диска.\n" "Эта функция определяет метод загрузки и сохраняет структуру `struct bootinfo` в памяти ядра." #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1295 #, no-wrap msgid "`identify_cpu`" msgstr "`identify_cpu`" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1296 #, no-wrap msgid "This function tries to find out what CPU it is running on, storing the value found in a variable `_cpu`." msgstr "Эта функция пытается определить, на каком процессоре она выполняется, сохраняя найденное значение в переменной `_cpu`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1299 msgid "The next steps are enabling VME, if the CPU supports it:" msgstr "" "Следующие шаги включают активацию VME, если процессор поддерживает эту " "функцию:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1307 #, no-wrap msgid "" "sys/i386/i386/mpboot.s:\n" "\ttestl\t$CPUID_VME,%edx\n" "\tjz\t3f\n" "\torl\t$CR4_VME,%eax\n" "3:\tmovl\t%eax,%cr4\n" msgstr "" "sys/i386/i386/mpboot.s:\n" "\ttestl\t$CPUID_VME,%edx\n" "\tjz\t3f\n" "\torl\t$CR4_VME,%eax\n" "3:\tmovl\t%eax,%cr4\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1310 msgid "Then, enabling paging:" msgstr "Затем, включение подкачки:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1320 #, no-wrap msgid "" "sys/i386/i386/mpboot.s:\n" "/* Now enable paging */\n" "\tmovl\tIdlePTD_nopae, %eax\n" "\tmovl\t%eax,%cr3\t\t\t/* load ptd addr into mmu */\n" "\tmovl\t%cr0,%eax\t\t\t/* get control word */\n" "\torl\t$CR0_PE|CR0_PG,%eax\t\t/* enable paging */\n" "\tmovl\t%eax,%cr0\t\t\t/* and let's page NOW! */\n" msgstr "" "sys/i386/i386/mpboot.s:\n" "/* Now enable paging */\n" "\tmovl\tIdlePTD_nopae, %eax\n" "\tmovl\t%eax,%cr3\t\t\t/* load ptd addr into mmu */\n" "\tmovl\t%cr0,%eax\t\t\t/* get control word */\n" "\torl\t$CR0_PE|CR0_PG,%eax\t\t/* enable paging */\n" "\tmovl\t%eax,%cr0\t\t\t/* and let's page NOW! */\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1323 msgid "" "The next three lines of code are because the paging was set, so the jump is " "needed to continue the execution in virtualized address space:" msgstr "" "Следующие три строки кода необходимы, потому что была установлена подкачка, " "поэтому требуется переход для продолжения выполнения в виртуализированном " "адресном пространстве:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1329 #, no-wrap msgid "" "sys/i386/i386/mpboot.s:\n" "\tpushl\t$mp_begin\t\t\t\t/* jump to high mem */\n" "\tret\n" msgstr "" "sys/i386/i386/mpboot.s:\n" "\tpushl\t$mp_begin\t\t\t\t/* jump to high mem */\n" "\tret\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1332 #, no-wrap msgid "" "/* now running relocated at KERNBASE where the system is linked to run */\n" "mp_begin:\t/* now running relocated at KERNBASE */\n" msgstr "" "/* now running relocated at KERNBASE where the system is linked to run */\n" "mp_begin:\t/* now running relocated at KERNBASE */\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1337 msgid "" "The function `init386()` is called with a pointer to the first free physical " "page, after that `mi_startup()`. `init386` is an architecture dependent " "initialization function, and `mi_startup()` is an architecture independent " "one (the 'mi_' prefix stands for Machine Independent). The kernel never " "returns from `mi_startup()`, and by calling it, the kernel finishes booting:" msgstr "" "Функция `init386()` вызывается с указателем на первую свободную физическую " "страницу, после чего следует вызов `mi_startup()`. `init386` — это " "архитектурно-зависимая функция инициализации, а `mi_startup()` — " "архитектурно-независимая (префикс 'mi_' означает Machine Independent, то " "есть «независимая от машины»). Ядро никогда не возвращается из " "`mi_startup()`, и, вызывая её, завершает загрузку:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1347 #, no-wrap msgid "" "sys/i386/i386/locore.s:\n" "\tpushl\tphysfree\t\t\t/* value of first for init386(first) */\n" "\tcall\tinit386\t\t\t\t/* wire 386 chip for unix operation */\n" "\taddl\t$4,%esp\n" "\tmovl\t%eax,%esp\t\t\t/* Switch to true top of stack. */\n" "\tcall\tmi_startup\t\t\t/* autoconfiguration, mountroot etc */\n" "\t/* NOTREACHED */\n" msgstr "" "sys/i386/i386/locore.s:\n" "\tpushl\tphysfree\t\t\t/* value of first for init386(first) */\n" "\tcall\tinit386\t\t\t\t/* wire 386 chip for unix operation */\n" "\taddl\t$4,%esp\n" "\tmovl\t%eax,%esp\t\t\t/* Switch to true top of stack. */\n" "\tcall\tmi_startup\t\t\t/* autoconfiguration, mountroot etc */\n" "\t/* NOTREACHED */\n" #. type: Title === #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1349 #, no-wrap msgid "`init386()`" msgstr "`init386()`" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1354 msgid "" "`init386()` is defined in [.filename]#sys/i386/i386/machdep.c# and performs " "low-level initialization specific to the i386 chip. The switch to protected " "mode was performed by the loader. The loader has created the very first " "task, in which the kernel continues to operate. Before looking at the code, " "consider the tasks the processor must complete to initialize protected mode " "execution:" msgstr "" "`init386()` определена в [.filename]#sys/i386/i386/machdep.c# и выполняет " "низкоуровневую инициализацию, специфичную для чипа i386. Переход в " "защищённый режим был выполнен загрузчиком. Загрузчик создал самую первую " "задачу, в которой ядро продолжает работать. Прежде чем рассматривать код, " "рассмотрим задачи, которые процессор должен выполнить для инициализации " "выполнения в защищённом режиме:" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1356 msgid "" "Initialize the kernel tunable parameters, passed from the bootstrapping " "program." msgstr "" "Инициализировать настраиваемые параметры ядра, переданные из загрузочной " "программы." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1357 msgid "Prepare the GDT." msgstr "Подготовить GDT." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1358 msgid "Prepare the IDT." msgstr "Подготовить IDT." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1359 msgid "Initialize the system console." msgstr "Инициализировать системную консоль." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1360 msgid "Initialize the DDB, if it is compiled into kernel." msgstr "Инициализировать DDB, если он скомпилирован в ядро." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1361 msgid "Initialize the TSS." msgstr "Инициализировать TSS." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1362 msgid "Prepare the LDT." msgstr "Подготовить LDT." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1363 msgid "Set up thread0's pcb." msgstr "Настройка pcb для thread0." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1366 msgid "" "`init386()` initializes the tunable parameters passed from bootstrap by " "setting the environment pointer (envp) and calling `init_param1()`. The " "envp pointer has been passed from loader in the `bootinfo` structure:" msgstr "" "`init386()` инициализирует настраиваемые параметры, переданные из bootstrap, " "устанавливая указатель окружения (envp) и вызывая `init_param1()`. Указатель " "envp был передан из loader в структуре `bootinfo`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1372 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "\t/* Init basic tunables, hz etc */\n" "\tinit_param1();\n" msgstr "" "sys/i386/i386/machdep.c:\n" "\t/* Init basic tunables, hz etc */\n" "\tinit_param1();\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1376 msgid "" "`init_param1()` is defined in [.filename]#sys/kern/subr_param.c#. That file " "has a number of sysctls, and two functions, `init_param1()` and " "`init_param2()`, that are called from `init386()`:" msgstr "" "`init_param1()` определена в [.filename]#sys/kern/subr_param.c#. Этот файл " "содержит ряд sysctl, а также две функции, `init_param1()` и `init_param2()`, " "которые вызываются из `init386()`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1384 #, no-wrap msgid "" "sys/kern/subr_param.c:\n" "\thz = -1;\n" "\tTUNABLE_INT_FETCH(\"kern.hz\", &hz);\n" "\tif (hz == -1)\n" "\t\thz = vm_guest > VM_GUEST_NO ? HZ_VM : HZ;\n" msgstr "" "sys/kern/subr_param.c:\n" "\thz = -1;\n" "\tTUNABLE_INT_FETCH(\"kern.hz\", &hz);\n" "\tif (hz == -1)\n" "\t\thz = vm_guest > VM_GUEST_NO ? HZ_VM : HZ;\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1387 msgid "" "TUNABLE__FETCH is used to fetch the value from the environment:" msgstr "" "`TUNABLE__FETCH` используется для получения значения из окружения:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1392 #, no-wrap msgid "" "/usr/src/sys/sys/kernel.h:\n" "#define\tTUNABLE_INT_FETCH(path, var)\tgetenv_int((path), (var))\n" msgstr "" "/usr/src/sys/sys/kernel.h:\n" "#define\tTUNABLE_INT_FETCH(path, var)\tgetenv_int((path), (var))\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1396 msgid "" "Sysctl `kern.hz` is the system clock tick. Additionally, these sysctls are " "set by `init_param1()`: `kern.maxswzone, kern.maxbcache, kern.maxtsiz, kern." "dfldsiz, kern.maxdsiz, kern.dflssiz, kern.maxssiz, kern.sgrowsiz`." msgstr "" "Sysctl `kern.hz` представляет собой такт системных часов. Кроме того, эти " "параметры sysctl устанавливаются функцией `init_param1()`: `kern.maxswzone, " "kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.maxdsiz, kern.dflssiz, kern." "maxssiz, kern.sgrowsiz`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1404 msgid "" "Then `init386()` prepares the Global Descriptors Table (GDT). Every task on " "an x86 is running in its own virtual address space, and this space is " "addressed by a segment:offset pair. Say, for instance, the current " "instruction to be executed by the processor lies at CS:EIP, then the linear " "virtual address for that instruction would be \"the virtual address of code " "segment CS\" + EIP. For convenience, segments begin at virtual address 0 " "and end at a 4GB boundary. Therefore, the instruction's linear virtual " "address for this example would just be the value of EIP. Segment registers " "such as CS, DS etc are the selectors, i.e., indexes, into GDT (to be more " "precise, an index is not a selector itself, but the INDEX field of a " "selector). FreeBSD's GDT holds descriptors for 15 selectors per CPU:" msgstr "" "Затем `init386()` подготавливает Глобальную Таблицу Дескрипторов (GDT). " "Каждая задача на x86 выполняется в своём собственном виртуальном адресном " "пространстве, и это пространство адресуется парой сегмент:смещение. " "Например, если текущая инструкция, которую должен выполнить процессор, " "находится по адресу CS:EIP, то линейный виртуальный адрес этой инструкции " "будет \"виртуальный адрес кодового сегмента CS\" + EIP. Для удобства " "сегменты начинаются с виртуального адреса 0 и заканчиваются на границе 4 ГБ. " "Таким образом, линейный виртуальный адрес инструкции в данном примере будет " "просто значением EIP. Сегментные регистры, такие как CS, DS и другие, " "являются селекторами, то есть индексами в GDT (если быть более точным, " "индекс — это не сам селектор, а поле INDEX в селекторе). GDT в FreeBSD " "содержит дескрипторы для 15 селекторов на каждый CPU:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1410 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "union descriptor gdt0[NGDT];\t/* initial global descriptor table */\n" "union descriptor *gdt = gdt0;\t/* global descriptor table */\n" msgstr "" "sys/i386/i386/machdep.c:\n" "union descriptor gdt0[NGDT];\t/* initial global descriptor table */\n" "union descriptor *gdt = gdt0;\t/* global descriptor table */\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1435 #, no-wrap msgid "" "sys/x86/include/segments.h:\n" "/*\n" " * Entries in the Global Descriptor Table (GDT)\n" " */\n" "#define\tGNULL_SEL\t0\t/* Null Descriptor */\n" "#define\tGPRIV_SEL\t1\t/* SMP Per-Processor Private Data */\n" "#define\tGUFS_SEL\t2\t/* User %fs Descriptor (order critical: 1) */\n" "#define\tGUGS_SEL\t3\t/* User %gs Descriptor (order critical: 2) */\n" "#define\tGCODE_SEL\t4\t/* Kernel Code Descriptor (order critical: 1) */\n" "#define\tGDATA_SEL\t5\t/* Kernel Data Descriptor (order critical: 2) */\n" "#define\tGUCODE_SEL\t6\t/* User Code Descriptor (order critical: 3) */\n" "#define\tGUDATA_SEL\t7\t/* User Data Descriptor (order critical: 4) */\n" "#define\tGBIOSLOWMEM_SEL\t8\t/* BIOS low memory access (must be entry 8) */\n" "#define\tGPROC0_SEL\t9\t/* Task state process slot zero and up */\n" "#define\tGLDT_SEL\t10\t/* Default User LDT */\n" "#define\tGUSERLDT_SEL\t11\t/* User LDT */\n" "#define\tGPANIC_SEL\t12\t/* Task state to consider panic from */\n" "#define\tGBIOSCODE32_SEL\t13\t/* BIOS interface (32bit Code) */\n" "#define\tGBIOSCODE16_SEL\t14\t/* BIOS interface (16bit Code) */\n" "#define\tGBIOSDATA_SEL\t15\t/* BIOS interface (Data) */\n" "#define\tGBIOSUTIL_SEL\t16\t/* BIOS interface (Utility) */\n" "#define\tGBIOSARGS_SEL\t17\t/* BIOS interface (Arguments) */\n" "#define\tGNDIS_SEL\t18\t/* For the NDIS layer */\n" "#define\tNGDT\t\t19\n" msgstr "" "sys/x86/include/segments.h:\n" "/*\n" " * Entries in the Global Descriptor Table (GDT)\n" " */\n" "#define\tGNULL_SEL\t0\t/* Null Descriptor */\n" "#define\tGPRIV_SEL\t1\t/* SMP Per-Processor Private Data */\n" "#define\tGUFS_SEL\t2\t/* User %fs Descriptor (order critical: 1) */\n" "#define\tGUGS_SEL\t3\t/* User %gs Descriptor (order critical: 2) */\n" "#define\tGCODE_SEL\t4\t/* Kernel Code Descriptor (order critical: 1) */\n" "#define\tGDATA_SEL\t5\t/* Kernel Data Descriptor (order critical: 2) */\n" "#define\tGUCODE_SEL\t6\t/* User Code Descriptor (order critical: 3) */\n" "#define\tGUDATA_SEL\t7\t/* User Data Descriptor (order critical: 4) */\n" "#define\tGBIOSLOWMEM_SEL\t8\t/* BIOS low memory access (must be entry 8) */\n" "#define\tGPROC0_SEL\t9\t/* Task state process slot zero and up */\n" "#define\tGLDT_SEL\t10\t/* Default User LDT */\n" "#define\tGUSERLDT_SEL\t11\t/* User LDT */\n" "#define\tGPANIC_SEL\t12\t/* Task state to consider panic from */\n" "#define\tGBIOSCODE32_SEL\t13\t/* BIOS interface (32bit Code) */\n" "#define\tGBIOSCODE16_SEL\t14\t/* BIOS interface (16bit Code) */\n" "#define\tGBIOSDATA_SEL\t15\t/* BIOS interface (Data) */\n" "#define\tGBIOSUTIL_SEL\t16\t/* BIOS interface (Utility) */\n" "#define\tGBIOSARGS_SEL\t17\t/* BIOS interface (Arguments) */\n" "#define\tGNDIS_SEL\t18\t/* For the NDIS layer */\n" "#define\tNGDT\t\t19\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1439 msgid "" "Note that those #defines are not selectors themselves, but just a field " "INDEX of a selector, so they are exactly the indices of the GDT. for " "example, an actual selector for the kernel code (GCODE_SEL) has the value " "0x20." msgstr "" "Обратите внимание, что эти `#defines` не являются самими селекторами, а лишь " "полем `INDEX` селектора, поэтому они точно соответствуют индексам GDT. " "Например, реальный селектор для кода ядра (`GCODE_SEL`) имеет значение " "`0x20`." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1447 msgid "" "The next step is to initialize the Interrupt Descriptor Table (IDT). This " "table is referenced by the processor when a software or hardware interrupt " "occurs. For example, to make a system call, user application issues the " "`INT 0x80` instruction. This is a software interrupt, so the processor's " "hardware looks up a record with index 0x80 in the IDT. This record points " "to the routine that handles this interrupt, in this particular case, this " "will be the kernel's syscall gate. The IDT may have a maximum of 256 " "(0x100) records. The kernel allocates NIDT records for the IDT, where NIDT " "is the maximum (256):" msgstr "" "Следующий шаг — инициализация таблицы дескрипторов прерываний (IDT). Эта " "таблица используется процессором при возникновении программного или " "аппаратного прерывания. Например, чтобы выполнить системный вызов, " "пользовательское приложение использует инструкцию `INT 0x80`. Это " "программное прерывание, поэтому аппаратное обеспечение процессора ищет " "запись с индексом 0x80 в IDT. Эта запись указывает на процедуру обработки " "данного прерывания, в данном конкретном случае это будет шлюз системных " "вызовов ядра. IDT может содержать максимум 256 (0x100) записей. Ядро " "выделяет NIDT записей для IDT, где NIDT — это максимум (256):" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1453 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "static struct gate_descriptor idt0[NIDT];\n" "struct gate_descriptor *idt = &idt0[0];\t/* interrupt descriptor table */\n" msgstr "" "sys/i386/i386/machdep.c:\n" "static struct gate_descriptor idt0[NIDT];\n" "struct gate_descriptor *idt = &idt0[0];\t/* interrupt descriptor table */\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1457 msgid "" "For each interrupt, an appropriate handler is set. The syscall gate for " "`INT 0x80` is set as well:" msgstr "" "Для каждого прерывания устанавливается соответствующий обработчик. Также " "настраивается шлюз системного вызова для `INT 0x80`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1463 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "\tsetidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall),\n" "\t\t\tSDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));\n" msgstr "" "sys/i386/i386/machdep.c:\n" "\tsetidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall),\n" "\t\t\tSDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1466 msgid "" "So when a userland application issues the `INT 0x80` instruction, control " "will transfer to the function `_Xint0x80_syscall`, which is in the kernel " "code segment and will be executed with supervisor privileges." msgstr "" "Итак, когда пользовательское приложение выполняет инструкцию `INT 0x80`, " "управление передаётся функции `_Xint0x80_syscall`, которая находится в " "сегменте кода ядра и будет выполнена с привилегиями супервизора." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1468 msgid "Console and DDB are then initialized:" msgstr "Консоль и DDB инициализируются:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1479 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "\tcninit();\n" "/* skipped */\n" " kdb_init();\n" "#ifdef KDB\n" "\tif (boothowto & RB_KDB)\n" "\t\tkdb_enter(KDB_WHY_BOOTFLAGS, \"Boot flags requested debugger\");\n" "#endif\n" msgstr "" "sys/i386/i386/machdep.c:\n" "\tcninit();\n" "/* skipped */\n" " kdb_init();\n" "#ifdef KDB\n" "\tif (boothowto & RB_KDB)\n" "\t\tkdb_enter(KDB_WHY_BOOTFLAGS, \"Boot flags requested debugger\");\n" "#endif\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1482 msgid "" "The Task State Segment is another x86 protected mode structure, the TSS is " "used by the hardware to store task information when a task switch occurs." msgstr "" "Сегмент состояния задачи (TSS) — это ещё одна структура защищенного режима " "x86, используемая оборудованием для хранения информации о задаче при " "переключении задач." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1485 msgid "" "The Local Descriptors Table is used to reference userland code and data. " "Several selectors are defined to point to the LDT, they are the system call " "gates and the user code and data selectors:" msgstr "" "Локальная таблица дескрипторов (LDT) используется для ссылки на код и данные " "пользовательского пространства. Определено несколько селекторов, указывающих " "на LDT, включая шлюзы системных вызовов, а также селекторы кода и данных " "пользователя:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1494 #, no-wrap msgid "" "sys/x86/include/segments.h:\n" "#define\tLSYS5CALLS_SEL\t0\t/* forced by intel BCS */\n" "#define\tLSYS5SIGR_SEL\t1\n" "#define\tLUCODE_SEL\t3\n" "#define\tLUDATA_SEL\t5\n" "#define\tNLDT\t\t(LUDATA_SEL + 1)\n" msgstr "" "sys/x86/include/segments.h:\n" "#define\tLSYS5CALLS_SEL\t0\t/* forced by intel BCS */\n" "#define\tLSYS5SIGR_SEL\t1\n" "#define\tLUCODE_SEL\t3\n" "#define\tLUDATA_SEL\t5\n" "#define\tNLDT\t\t(LUDATA_SEL + 1)\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1499 msgid "" "Next, proc0's Process Control Block (`struct pcb`) structure is " "initialized. proc0 is a `struct proc` structure that describes a kernel " "process. It is always present while the kernel is running, therefore it is " "linked with thread0:" msgstr "" "Далее инициализируется структура Блока Управления Процессом (`struct pcb`) " "для proc0. proc0 — это структура `struct proc`, описывающая процесс ядра. " "Она всегда присутствует во время работы ядра, поэтому связана с thread0:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1507 #, no-wrap msgid "" "sys/i386/i386/machdep.c:\n" "register_t\n" "init386(int first)\n" "{\n" " /* ... skipped ... */\n" msgstr "" "sys/i386/i386/machdep.c:\n" "register_t\n" "init386(int first)\n" "{\n" " /* ... skipped ... */\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1511 #, no-wrap msgid "" " proc_linkup0(&proc0, &thread0);\n" " /* ... skipped ... */\n" "}\n" msgstr "" " proc_linkup0(&proc0, &thread0);\n" " /* ... skipped ... */\n" "}\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1515 msgid "" "The structure `struct pcb` is a part of a proc structure. It is defined in " "[.filename]#/usr/include/machine/pcb.h# and has a process's information " "specific to the i386 architecture, such as registers values." msgstr "" "Структура `struct pcb` является частью структуры proc. Она определена в [." "filename]#/usr/include/machine/pcb.h# и содержит информацию процесса, " "специфичную для архитектуры i386, такую как значения регистров." #. type: Title === #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1516 #, no-wrap msgid "`mi_startup()`" msgstr "`mi_startup()`" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1519 msgid "" "This function performs a bubble sort of all the system initialization " "objects and then calls the entry of each object one by one:" msgstr "" "Эта функция выполняет сортировку пузырьком всех объектов инициализации " "системы, а затем вызывает вход каждого объекта по очереди:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1524 #, no-wrap msgid "" "sys/kern/init_main.c:\n" "\tfor (sipp = sysinit; sipp < sysinit_end; sipp++) {\n" msgstr "" "sys/kern/init_main.c:\n" "\tfor (sipp = sysinit; sipp < sysinit_end; sipp++) {\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1526 #, no-wrap msgid "\t\t/* ... skipped ... */\n" msgstr "\t\t/* ... skipped ... */\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1531 #, no-wrap msgid "" "\t\t/* Call function */\n" "\t\t(*((*sipp)->func))((*sipp)->udata);\n" "\t\t/* ... skipped ... */\n" "\t}\n" msgstr "" "\t\t/* Call function */\n" "\t\t(*((*sipp)->func))((*sipp)->udata);\n" "\t\t/* ... skipped ... */\n" "\t}\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1534 msgid "" "Although the sysinit framework is described in the extref:{developers-" "handbook}[Developers' Handbook], I will discuss the internals of it." msgstr "" "Хотя фреймворк sysinit описан в extref:{developers-handbook}[Руководстве " "разработчика], я рассмотрю его внутреннее устройство." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1538 msgid "" "Every system initialization object (sysinit object) is created by calling a " "SYSINIT() macro. Let us take as example an `announce` sysinit object. This " "object prints the copyright message:" msgstr "" "Каждый объект инициализации системы (объект sysinit) создается путем вызова " "макроса SYSINIT(). Возьмем, к примеру, объект sysinit `announce`. Этот " "объект выводит сообщение об авторских правах:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1549 #, no-wrap msgid "" "sys/kern/init_main.c:\n" "static void\n" "print_caddr_t(void *data __unused)\n" "{\n" "\tprintf(\"%s\", (char *)data);\n" "}\n" "/* ... skipped ... */\n" "SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright);\n" msgstr "" "sys/kern/init_main.c:\n" "static void\n" "print_caddr_t(void *data __unused)\n" "{\n" "\tprintf(\"%s\", (char *)data);\n" "}\n" "/* ... skipped ... */\n" "SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright);\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1553 msgid "" "The subsystem ID for this object is SI_SUB_COPYRIGHT (0x0800001). So, the " "copyright message will be printed out first, just after the console " "initialization." msgstr "" "Идентификатор подсистемы для этого объекта — SI_SUB_COPYRIGHT (0x0800001). " "Таким образом, сообщение об авторских правах будет выведено первым, сразу " "после инициализации консоли." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1557 msgid "" "Let us take a look at what exactly the macro `SYSINIT()` does. It expands " "to a `C_SYSINIT()` macro. The `C_SYSINIT()` macro then expands to a static " "`struct sysinit` structure declaration with another `DATA_SET` macro call:" msgstr "" "Давайте рассмотрим, что именно делает макрос `SYSINIT()`. Он раскрывается в " "макрос `C_SYSINIT()`. Макрос `C_SYSINIT()` затем раскрывается в статическое " "объявление структуры `struct sysinit` с вызовом другого макроса `DATA_SET`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1565 #, no-wrap msgid "" "/usr/include/sys/kernel.h:\n" " #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \\\n" " static struct sysinit uniquifier ## _sys_init = { \\ subsystem, \\\n" " order, \\ func, \\ (ident) \\ }; \\ DATA_WSET(sysinit_set,uniquifier ##\n" " _sys_init);\n" msgstr "" "/usr/include/sys/kernel.h:\n" " #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \\\n" " static struct sysinit uniquifier ## _sys_init = { \\ subsystem, \\\n" " order, \\ func, \\ (ident) \\ }; \\ DATA_WSET(sysinit_set,uniquifier ##\n" " _sys_init);\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1569 #, no-wrap msgid "" "#define\tSYSINIT(uniquifier, subsystem, order, func, ident)\t\\\n" "\tC_SYSINIT(uniquifier, subsystem, order,\t\t\t\\\n" "\t(sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident))\n" msgstr "" "#define\tSYSINIT(uniquifier, subsystem, order, func, ident)\t\\\n" "\tC_SYSINIT(uniquifier, subsystem, order,\t\t\t\\\n" "\t(sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)(ident))\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1572 msgid "" "The `DATA_SET()` macro expands to a `_MAKE_SET()`, and that macro is the " "point where all the sysinit magic is hidden:" msgstr "" "Макрос `DATA_SET()` раскрывается в `_MAKE_SET()`, и именно в этом макросе " "скрыта вся магия инициализации системы:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1578 #, no-wrap msgid "" "/usr/include/linker_set.h:\n" "#define TEXT_SET(set, sym) _MAKE_SET(set, sym)\n" "#define DATA_SET(set, sym) _MAKE_SET(set, sym)\n" msgstr "" "/usr/include/linker_set.h:\n" "#define TEXT_SET(set, sym) _MAKE_SET(set, sym)\n" "#define DATA_SET(set, sym) _MAKE_SET(set, sym)\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1582 msgid "" "After executing these macros, various sections were made in the kernel, " "including`set.sysinit_set`. Running objdump on a kernel binary, you may " "notice the presence of such small sections:" msgstr "" "После выполнения этих макросов в ядре были созданы различные разделы, " "включая `set.sysinit_set`. Запустив objdump для бинарного файла ядра, можно " "заметить наличие таких небольших разделов:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1594 #, no-wrap msgid "" "% llvm-objdump -h /kernel\n" "Sections:\n" "Idx Name Size VMA Type\n" " 10 set_sysctl_set 000021d4 01827078 DATA\n" " 16 set_kbddriver_set 00000010 0182a4d0 DATA\n" " 20 set_scterm_set 0000000c 0182c75c DATA\n" " 21 set_cons_set 00000014 0182c768 DATA\n" " 33 set_scrndr_set 00000024 0182c828 DATA\n" " 41 set_sysinit_set 000014d8 018fabb0 DATA\n" msgstr "" "% llvm-objdump -h /kernel\n" "Sections:\n" "Idx Name Size VMA Type\n" " 10 set_sysctl_set 000021d4 01827078 DATA\n" " 16 set_kbddriver_set 00000010 0182a4d0 DATA\n" " 20 set_scterm_set 0000000c 0182c75c DATA\n" " 21 set_cons_set 00000014 0182c768 DATA\n" " 33 set_scrndr_set 00000024 0182c828 DATA\n" " 41 set_sysinit_set 000014d8 018fabb0 DATA\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1598 msgid "" "This screen dump shows that the size of set.sysinit_set section is 0x14d8 " "bytes, so `0x14d8/sizeof(void *)` sysinit objects are compiled into the " "kernel. The other sections such as `set.sysctl_set` represent other linker " "sets." msgstr "" "Это содержимое экрана показывает, что размер раздела set.sysinit_set " "составляет 0x14d8 байт, поэтому `0x14d8/sizeof(void *)` объектов sysinit " "скомпилировано в ядро. Другие разделы, такие как `set.sysctl_set`, " "представляют другие наборы компоновщика." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1600 msgid "" "By defining a variable of type `struct sysinit` the content of `set." "sysinit_set` section will be \"collected\" into that variable:" msgstr "" "Определяя переменную типа `struct sysinit`, содержимое раздела `set." "sysinit_set` будет \"собрано\" в эту переменную:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1605 #, no-wrap msgid "" "sys/kern/init_main.c:\n" " SET_DECLARE(sysinit_set, struct sysinit);\n" msgstr "" "sys/kern/init_main.c:\n" " SET_DECLARE(sysinit_set, struct sysinit);\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1608 msgid "The `struct sysinit` is defined as follows:" msgstr "`struct sysinit` определена следующим образом:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1618 #, no-wrap msgid "" "sys/sys/kernel.h:\n" " struct sysinit {\n" "\tenum sysinit_sub_id\tsubsystem;\t/* subsystem identifier*/\n" "\tenum sysinit_elem_order\torder;\t\t/* init order within subsystem*/\n" "\tsysinit_cfunc_t func;\t\t\t/* function\t\t*/\n" "\tconst void\t*udata;\t\t\t/* multiplexer/argument */\n" "};\n" msgstr "" "sys/sys/kernel.h:\n" " struct sysinit {\n" "\tenum sysinit_sub_id\tsubsystem;\t/* subsystem identifier*/\n" "\tenum sysinit_elem_order\torder;\t\t/* init order within subsystem*/\n" "\tsysinit_cfunc_t func;\t\t\t/* function\t\t*/\n" "\tconst void\t*udata;\t\t\t/* multiplexer/argument */\n" "};\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1623 msgid "" "Returning to the `mi_startup()` discussion, it is must be clear now, how the " "sysinit objects are being organized. The `mi_startup()` function sorts them " "and calls each. The very last object is the system scheduler:" msgstr "" "Возвращаясь к обсуждению `mi_startup()`, теперь должно быть понятно, как " "организованы объекты sysinit. Функция `mi_startup()` сортирует их и вызывает " "каждый. Самый последний объект — это системный планировщик:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1635 #, no-wrap msgid "" "/usr/include/sys/kernel.h:\n" "enum sysinit_sub_id {\n" "\tSI_SUB_DUMMY\t\t= 0x0000000,\t/* not executed; for linker*/\n" "\tSI_SUB_DONE\t\t= 0x0000001,\t/* processed*/\n" "\tSI_SUB_TUNABLES\t\t= 0x0700000,\t/* establish tunable values */\n" "\tSI_SUB_COPYRIGHT\t= 0x0800001,\t/* first use of console*/\n" "...\n" "\tSI_SUB_LAST\t\t= 0xfffffff\t/* final initialization */\n" "};\n" msgstr "" "/usr/include/sys/kernel.h:\n" "enum sysinit_sub_id {\n" "\tSI_SUB_DUMMY\t\t= 0x0000000,\t/* not executed; for linker*/\n" "\tSI_SUB_DONE\t\t= 0x0000001,\t/* processed*/\n" "\tSI_SUB_TUNABLES\t\t= 0x0700000,\t/* establish tunable values */\n" "\tSI_SUB_COPYRIGHT\t= 0x0800001,\t/* first use of console*/\n" "...\n" "\tSI_SUB_LAST\t\t= 0xfffffff\t/* final initialization */\n" "};\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1640 msgid "" "The system scheduler sysinit object is defined in the file [.filename]#sys/" "vm/vm_glue.c#, and the entry point for that object is `scheduler()`. That " "function is actually an infinite loop, and it represents a process with PID " "0, the swapper process. The thread0 structure, mentioned before, is used to " "describe it." msgstr "" "Системный планировщик sysinit определен в файле [.filename]#sys/vm/vm_glue." "c#, а точка входа для этого объекта — `scheduler()`. Эта функция фактически " "представляет собой бесконечный цикл и описывает процесс с PID 0, известный " "как процесс swapper. Структура thread0, упомянутая ранее, используется для " "его описания." #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1642 msgid "" "The first user process, called _init_, is created by the sysinit object " "`init`:" msgstr "" "Первый пользовательский процесс, называемый _init_, создаётся объектом " "sysinit `init`:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1653 #, no-wrap msgid "" "sys/kern/init_main.c:\n" "static void\n" "create_init(const void *udata __unused)\n" "{\n" "\tstruct fork_req fr;\n" "\tstruct ucred *newcred, *oldcred;\n" "\tstruct thread *td;\n" "\tint error;\n" msgstr "" "sys/kern/init_main.c:\n" "static void\n" "create_init(const void *udata __unused)\n" "{\n" "\tstruct fork_req fr;\n" "\tstruct ucred *newcred, *oldcred;\n" "\tstruct thread *td;\n" "\tint error;\n" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1686 #, no-wrap msgid "" "\tbzero(&fr, sizeof(fr));\n" "\tfr.fr_flags = RFFDG | RFPROC | RFSTOPPED;\n" "\tfr.fr_procp = &initproc;\n" "\terror = fork1(&thread0, &fr);\n" "\tif (error)\n" "\t\tpanic(\"cannot fork init: %d\\n\", error);\n" "\tKASSERT(initproc->p_pid == 1, (\"create_init: initproc->p_pid != 1\"));\n" "\t/* divorce init's credentials from the kernel's */\n" "\tnewcred = crget();\n" "\tsx_xlock(&proctree_lock);\n" "\tPROC_LOCK(initproc);\n" "\tinitproc->p_flag |= P_SYSTEM | P_INMEM;\n" "\tinitproc->p_treeflag |= P_TREE_REAPER;\n" "\toldcred = initproc->p_ucred;\n" "\tcrcopy(newcred, oldcred);\n" "#ifdef MAC\n" "\tmac_cred_create_init(newcred);\n" "#endif\n" "#ifdef AUDIT\n" "\taudit_cred_proc1(newcred);\n" "#endif\n" "\tproc_set_cred(initproc, newcred);\n" "\ttd = FIRST_THREAD_IN_PROC(initproc);\n" "\tcrcowfree(td);\n" "\ttd->td_realucred = crcowget(initproc->p_ucred);\n" "\ttd->td_ucred = td->td_realucred;\n" "\tPROC_UNLOCK(initproc);\n" "\tsx_xunlock(&proctree_lock);\n" "\tcrfree(oldcred);\n" "\tcpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL);\n" "}\n" "SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL);\n" msgstr "" "\tbzero(&fr, sizeof(fr));\n" "\tfr.fr_flags = RFFDG | RFPROC | RFSTOPPED;\n" "\tfr.fr_procp = &initproc;\n" "\terror = fork1(&thread0, &fr);\n" "\tif (error)\n" "\t\tpanic(\"cannot fork init: %d\\n\", error);\n" "\tKASSERT(initproc->p_pid == 1, (\"create_init: initproc->p_pid != 1\"));\n" "\t/* divorce init's credentials from the kernel's */\n" "\tnewcred = crget();\n" "\tsx_xlock(&proctree_lock);\n" "\tPROC_LOCK(initproc);\n" "\tinitproc->p_flag |= P_SYSTEM | P_INMEM;\n" "\tinitproc->p_treeflag |= P_TREE_REAPER;\n" "\toldcred = initproc->p_ucred;\n" "\tcrcopy(newcred, oldcred);\n" "#ifdef MAC\n" "\tmac_cred_create_init(newcred);\n" "#endif\n" "#ifdef AUDIT\n" "\taudit_cred_proc1(newcred);\n" "#endif\n" "\tproc_set_cred(initproc, newcred);\n" "\ttd = FIRST_THREAD_IN_PROC(initproc);\n" "\tcrcowfree(td);\n" "\ttd->td_realucred = crcowget(initproc->p_ucred);\n" "\ttd->td_ucred = td->td_realucred;\n" "\tPROC_UNLOCK(initproc);\n" "\tsx_xunlock(&proctree_lock);\n" "\tcrfree(oldcred);\n" "\tcpu_fork_kthread_handler(FIRST_THREAD_IN_PROC(initproc), start_init, NULL);\n" "}\n" "SYSINIT(init, SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL);\n" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1692 msgid "" "The function `create_init()` allocates a new process by calling `fork1()`, " "but does not mark it runnable. When this new process is scheduled for " "execution by the scheduler, the `start_init()` will be called. That " "function is defined in [.filename]#init_main.c#. It tries to load and exec " "the [.filename]#init# binary, probing [.filename]#/sbin/init# first, then [." "filename]#/sbin/oinit#, [.filename]#/sbin/init.bak#, and finally [." "filename]#/rescue/init#:" msgstr "" "Функция `create_init()` выделяет новый процесс, вызывая `fork1()`, но не " "помечает его как готовый к выполнению. Когда этот новый процесс будет " "запланирован для выполнения планировщиком, будет вызвана функция " "`start_init()`. Эта функция определена в [.filename]#init_main.c#. Она " "пытается загрузить и выполнить бинарный файл [.filename]#init#, сначала " "проверяя [.filename]#/sbin/init#, затем [.filename]#/sbin/oinit#, [." "filename]#/sbin/init.bak# и, наконец, [.filename]#/rescue/init#:" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1702 #, no-wrap msgid "" "sys/kern/init_main.c:\n" "static char init_path[MAXPATHLEN] =\n" "#ifdef\tINIT_PATH\n" " __XSTRING(INIT_PATH);\n" "#else\n" " \"/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init\";\n" "#endif\n" msgstr "" "sys/kern/init_main.c:\n" "static char init_path[MAXPATHLEN] =\n" "#ifdef\tINIT_PATH\n" " __XSTRING(INIT_PATH);\n" "#else\n" " \"/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init\";\n" "#endif\n"