# SOME DESCRIPTIVE TITLE # Copyright (C) YEAR The FreeBSD Project # This file is distributed under the same license as the FreeBSD Documentation package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: FreeBSD Documentation VERSION\n" "POT-Creation-Date: 2025-11-08 16:17+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:114 #, no-wrap msgid "[.filename]#loader#" msgstr "" #. 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 "" #. 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 "" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:147 #, no-wrap msgid "The BIOS" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:185 #, no-wrap msgid "The Master Boot Record (`boot0`)" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:207 msgid "the 1-byte bootable flag" msgstr "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:208 msgid "the 6 byte descriptor in CHS format" msgstr "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:209 msgid "the 8 byte descriptor in LBA format" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:490 #, no-wrap msgid "`boot1` Stage" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:525 #, no-wrap msgid "" "start:\n" "\tjmp main\n" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:851 #, no-wrap msgid "#define XREADORG 0x725\n" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1155 #, no-wrap msgid "boot2 Stage" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title == #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1204 #, no-wrap msgid "loader Stage" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1290 #, no-wrap msgid "`recover_bootinfo`" msgstr "" #. 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 "" #. type: Table #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1295 #, no-wrap msgid "`identify_cpu`" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title === #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1349 #, no-wrap msgid "`init386()`" msgstr "" #. 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 "" #. 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 "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1358 msgid "Prepare the IDT." msgstr "" #. 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 "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1361 msgid "Initialize the TSS." msgstr "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1362 msgid "Prepare the LDT." msgstr "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1363 msgid "Set up thread0's pcb." msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1468 msgid "Console and DDB are then initialized:" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Title === #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1516 #, no-wrap msgid "`mi_startup()`" msgstr "" #. 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 "" #. type: delimited block . 4 #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1526 #, no-wrap msgid "\t\t/* ... skipped ... */\n" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. type: Plain text #: documentation/content/en/books/arch-handbook/boot/_index.adoc:1608 msgid "The `struct sysinit` is defined as follows:" msgstr "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 "" #. 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 ""