Translation

(itstool) path: sect1/para
English
<literal>boot2</literal> defines an important structure, <literal>struct bootinfo</literal>. This structure is initialized by <literal>boot2</literal> and passed to the loader, and then further to the kernel. Some nodes of this structures are set by <literal>boot2</literal>, the rest by the loader. This structure, among other information, contains the kernel filename, <acronym>BIOS</acronym> harddisk geometry, <acronym>BIOS</acronym> drive number for boot device, physical memory available, <literal>envp</literal> pointer etc. The definition for it is:
53/5570
Context English Chinese (Simplified) (zh_CN) State
/*
* Update real mode IDT for reflecting hardware interrupts.
*/
mov $intr20,%bx # Address first handler
mov $0x10,%cx # Number of handlers
mov $0x20*4,%di # First real mode IDT entry
init.0: mov %bx,(%di) # Store IP
inc %di # Address next
inc %di # entry
stosw # Store CS
add $4,%bx # Next handler
loop init.0 # Next IRQ
/*
* Update real mode IDT for reflecting hardware interrupts.
*/
mov $intr20,%bx # Address first handler
mov $0x10,%cx # Number of handlers
mov $0x20*4,%di # First real mode IDT entry
init.0: mov %bx,(%di) # Store IP
inc %di # Address next
inc %di # entry
stosw # Store CS
add $4,%bx # Next handler
loop init.0 # Next IRQ
The next block creates the <acronym>IDT</acronym> (Interrupt Descriptor Table). The <acronym>IDT</acronym> is analogous, in protected mode, to the <acronym>IVT</acronym> in real mode. That is, the <acronym>IDT</acronym> 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:
/*
* Create IDT.
*/
mov $0x5e00,%di # IDT's address
mov $idtctl,%si # Control string
init.1: lodsb # Get entry
cbw # count
xchg %ax,%cx # as word
jcxz init.4 # If done
lodsb # Get segment
xchg %ax,%dx # P:DPL:type
lodsw # Get control
xchg %ax,%bx # set
lodsw # Get handler offset
mov $SEL_SCODE,%dh # Segment selector
init.2: shr %bx # Handle this int?
jnc init.3 # No
mov %ax,(%di) # Set handler offset
mov %dh,0x2(%di) # and selector
mov %dl,0x5(%di) # Set P:DPL:type
add $0x4,%ax # Next handler
init.3: lea 0x8(%di),%di # Next entry
loop init.2 # Till set done
jmp init.1 # Continue
/*
* Create IDT.
*/
mov $0x5e00,%di # IDT's address
mov $idtctl,%si # Control string
init.1: lodsb # Get entry
cbw # count
xchg %ax,%cx # as word
jcxz init.4 # If done
lodsb # Get segment
xchg %ax,%dx # P:DPL:type
lodsw # Get control
xchg %ax,%bx # set
lodsw # Get handler offset
mov $SEL_SCODE,%dh # Segment selector
init.2: shr %bx # Handle this int?
jnc init.3 # No
mov %ax,(%di) # Set handler offset
mov %dh,0x2(%di) # and selector
mov %dl,0x5(%di) # Set P:DPL:type
add $0x4,%ax # Next handler
init.3: lea 0x8(%di),%di # Next entry
loop init.2 # Till set done
jmp init.1 # Continue
Each entry in the <literal>IDT</literal> 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 <literal>0</literal> to <literal>0xf</literal> (exceptions) are handled by function <literal>intx00</literal>; vector <literal>0x10</literal> (also an exception) is handled by <literal>intx10</literal>; hardware interrupts, which are later configured to start at interrupt vector <literal>0x20</literal> all the way to interrupt vector <literal>0x2f</literal>, are handled by function <literal>intx20</literal>. Lastly, interrupt vector <literal>0x30</literal>, which is used for system calls, is handled by <literal>intx30</literal>, and vectors <literal>0x31</literal> and <literal>0x32</literal> are handled by <literal>intx31</literal>. It must be noted that only descriptors for interrupt vectors <literal>0x30</literal>, <literal>0x31</literal> and <literal>0x32</literal> are given privilege level 3, the same privilege level as the <filename>boot2</filename> client, which means the client can execute a software-generated interrupt to this vectors through the <literal>int</literal> instruction without failing (this is the way <filename>boot2</filename> use the services provided by the <acronym>BTX</acronym> server). Also, note that <emphasis>only</emphasis> software-generated interrupts are protected from code executing in lesser privilege levels. Hardware-generated interrupts and processor-generated exceptions are <emphasis>always</emphasis> handled adequately, regardless of the actual privileges involved.
The next step is to initialize the <acronym>TSS</acronym> (Task-State Segment). The <acronym>TSS</acronym> 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 <emphasis>at least</emphasis> one <acronym>TSS</acronym> if multitasking facilities are used or different privilege levels are defined. Since the <filename>boot2</filename> client is executed in privilege level 3, but the <acronym>BTX</acronym> server does in privilege level 0, a <acronym>TSS</acronym> must be defined:
/*
* Initialize TSS.
*/
init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
/*
* Initialize TSS.
*/
init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
Note that a value is given for the Privilege Level 0 stack pointer and stack segment in the <acronym>TSS</acronym>. This is needed because, if an interrupt or exception is received while executing <filename>boot2</filename> 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 <acronym>TSS</acronym> is given a value, which is a 16-bit offset from the beginning of the <acronym>TSS</acronym> to the I/O Permission Bitmap and the Interrupt Redirection Bitmap.
After the <acronym>IDT</acronym> and <acronym>TSS</acronym> are created, the processor is ready to switch to protected mode. This is done in the next block:
/*
* Bring up the system.
*/
mov $0x2820,%bx # Set protected mode
callw setpic # IRQ offsets
lidt idtdesc # Set IDT
lgdt gdtdesc # Set GDT
mov %cr0,%eax # Switch to protected
inc %ax # mode
mov %eax,%cr0 #
ljmp $SEL_SCODE,$init.8 # To 32-bit code
.code32
init.8: xorl %ecx,%ecx # Zero
movb $SEL_SDATA,%cl # To 32-bit
movw %cx,%ss # stack
/*
* Bring up the system.
*/
mov $0x2820,%bx # Set protected mode
callw setpic # IRQ offsets
lidt idtdesc # Set IDT
lgdt gdtdesc # Set GDT
mov %cr0,%eax # Switch to protected
inc %ax # mode
mov %eax,%cr0 #
ljmp $SEL_SCODE,$init.8 # To 32-bit code
.code32
init.8: xorl %ecx,%ecx # Zero
movb $SEL_SDATA,%cl # To 32-bit
movw %cx,%ss # stack
First, a call is made to <literal>setpic</literal> to program the 8259A <acronym>PIC</acronym> (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 <acronym>IDTR</acronym> (Interrupt Descriptor Table Register) and <acronym>GDTR</acronym> (Global Descriptor Table Register) are loaded with the instructions <literal>lidt</literal> and <literal>lgdt</literal>, respectively. These registers are loaded with the base address and limit address for the <acronym>IDT</acronym> and <acronym>GDT</acronym>. The following three instructions set the Protection Enable (PE) bit of the <literal>%cr0</literal> register. This effectively switches the processor to 32-bit protected mode. Next, a long jump is made to <literal>init.8</literal> 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 <literal>%ss</literal> register. This data segment also has a privilege level of <literal>0</literal>.
Our last code block is responsible for loading the <acronym>TR</acronym> (Task Register) with the segment selector for the <acronym>TSS</acronym> we created earlier, and setting the User Mode environment before passing execution control to the <filename>boot2</filename> client.
/*
* Launch user task.
*/
movb $SEL_TSS,%cl # Set task
ltr %cx # register
movl $0xa000,%edx # User base address
movzwl %ss:BDA_MEM,%eax # Get free memory
shll $0xa,%eax # To bytes
subl $ARGSPACE,%eax # Less arg space
subl %edx,%eax # Less base
movb $SEL_UDATA,%cl # User data selector
pushl %ecx # Set SS
pushl %eax # Set ESP
push $0x202 # Set flags (IF set)
push $SEL_UCODE # Set CS
pushl btx_hdr+0xc # Set EIP
pushl %ecx # Set GS
pushl %ecx # Set FS
pushl %ecx # Set DS
pushl %ecx # Set ES
pushl %edx # Set EAX
movb $0x7,%cl # Set remaining
init.9: push $0x0 # general
loop init.9 # registers
popa # and initialize
popl %es # Initialize
popl %ds # user
popl %fs # segment
popl %gs # registers
iret # To user mode
/*
* Launch user task.
*/
movb $SEL_TSS,%cl # Set task
ltr %cx # register
movl $0xa000,%edx # User base address
movzwl %ss:BDA_MEM,%eax # Get free memory
shll $0xa,%eax # To bytes
subl $ARGSPACE,%eax # Less arg space
subl %edx,%eax # Less base
movb $SEL_UDATA,%cl # User data selector
pushl %ecx # Set SS
pushl %eax # Set ESP
push $0x202 # Set flags (IF set)
push $SEL_UCODE # Set CS
pushl btx_hdr+0xc # Set EIP
pushl %ecx # Set GS
pushl %ecx # Set FS
pushl %ecx # Set DS
pushl %ecx # Set ES
pushl %edx # Set EAX
movb $0x7,%cl # Set remaining
init.9: push $0x0 # general
loop init.9 # registers
popa # and initialize
popl %es # Initialize
popl %ds # user
popl %fs # segment
popl %gs # registers
iret # To user mode
Note that the client's environment include a stack segment selector and stack pointer (registers <literal>%ss</literal> and <literal>%esp</literal>). Indeed, once the <acronym>TR</acronym> is loaded with the appropriate stack segment selector (instruction <literal>ltr</literal>), the stack pointer is calculated and pushed onto the stack along with the stack's segment selector. Next, the value <literal>0x202</literal> 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 <acronym>BTX</acronym> header at link time. Finally, segment selectors (stored in register <literal>%ecx</literal>) for the segment registers <literal>%gs, %fs, %ds and %es</literal> are pushed onto the stack, along with the value at <literal>%edx</literal> (<literal>0xa000</literal>). 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 <literal>loop</literal> that pushes the value <literal>0</literal> seven times). Now, values will be started to be popped out of the stack. First, the <literal>popa</literal> instruction pops out of the stack the latest seven values pushed. They are stored in the general purpose registers in order <literal>%edi, %esi, %ebp, %ebx, %edx, %ecx, %eax</literal>. 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 <literal>iret</literal> instruction is executed. This instruction first pops the value that was pushed from the <acronym>BTX</acronym> header. This value is a pointer to <filename>boot2</filename>'s entry point. It is placed in the register <literal>%eip</literal>, the instruction pointer register. Next, the segment selector for the User Code Segment is popped and copied to register <literal>%cs</literal>. 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 (<literal>%esp</literal>) and the stack segment (<literal>%ss</literal>). Now, execution continues at <literal>boot0</literal>'s entry point.
It is important to note how the User Code Segment is defined. This segment's <emphasis>base address</emphasis> is set to <literal>0xa000</literal>. This means that code memory addresses are <emphasis>relative</emphasis> to address 0xa000; if code being executed is fetched from address <literal>0x2000</literal>, the <emphasis>actual</emphasis> memory addressed is <literal>0xa000+0x2000=0xc000</literal>.
<application>boot2</application> Stage <application>boot2</application> 阶段
<literal>boot2</literal> defines an important structure, <literal>struct bootinfo</literal>. This structure is initialized by <literal>boot2</literal> and passed to the loader, and then further to the kernel. Some nodes of this structures are set by <literal>boot2</literal>, the rest by the loader. This structure, among other information, contains the kernel filename, <acronym>BIOS</acronym> harddisk geometry, <acronym>BIOS</acronym> drive number for boot device, physical memory available, <literal>envp</literal> pointer etc. The definition for it is: boot2 定义了很重要的引导信息数据结构。此结构由 boot2 初始化,然后传递到加载程序,再传到内核。
<filename>/usr/include/machine/bootinfo.h:</filename>
struct bootinfo {
u_int32_t bi_version;
u_int32_t bi_kernelname; /* represents a char * */
u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
/* End of fields that are always present. */
#define bi_endcommon bi_n_bios_used
u_int32_t bi_n_bios_used;
u_int32_t bi_bios_geom[N_BIOS_GEOM];
u_int32_t bi_size;
u_int8_t bi_memsizes_valid;
u_int8_t bi_bios_dev; /* bootdev BIOS unit number */
u_int8_t bi_pad[2];
u_int32_t bi_basemem;
u_int32_t bi_extmem;
u_int32_t bi_symtab; /* struct symtab * */
u_int32_t bi_esymtab; /* struct symtab * */
/* Items below only from advanced bootloader */
u_int32_t bi_kernend; /* end of kernel space */
u_int32_t bi_envp; /* environment */
u_int32_t bi_modulep; /* preloaded modules */
};
<filename>/usr/include/machine/bootinfo.h:</filename>
struct bootinfo {
u_int32_t bi_version;
u_int32_t bi_kernelname; /* represents a char * */
u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
/* End of fields that are always present. */
#define bi_endcommon bi_n_bios_used
u_int32_t bi_n_bios_used;
u_int32_t bi_bios_geom[N_BIOS_GEOM];
u_int32_t bi_size;
u_int8_t bi_memsizes_valid;
u_int8_t bi_bios_dev; /* bootdev BIOS unit number */
u_int8_t bi_pad[2];
u_int32_t bi_basemem;
u_int32_t bi_extmem;
u_int32_t bi_symtab; /* struct symtab * */
u_int32_t bi_esymtab; /* struct symtab * */
/* Items below only from advanced bootloader */
u_int32_t bi_kernend; /* end of kernel space */
u_int32_t bi_envp; /* environment */
u_int32_t bi_modulep; /* preloaded modules */
};
<literal>boot2</literal> enters into an infinite loop waiting for user input, then calls <function>load()</function>. If the user does not press anything, the loop breaks by a timeout, so <function>load()</function> will load the default file (<filename>/boot/loader</filename>). Functions <function>ino_t lookup(char *filename)</function> and <function>int xfsread(ino_t inode, void *buf, size_t nbyte)</function> are used to read the content of a file into memory. <filename>/boot/loader</filename> is an <acronym>ELF</acronym> binary, but where the <acronym>ELF</acronym> header is prepended with <filename>a.out</filename>'s <literal>struct exec</literal> structure. <function>load()</function> scans the loader's ELF header, loading the content of <filename>/boot/loader</filename> into memory, and passing the execution to the loader's entry:
<filename>sys/boot/i386/boot2/boot2.c:</filename>
__exec((caddr_t)addr, RB_BOOTINFO | (opts &amp; RBX_MASK),
MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
0, 0, 0, VTOP(&amp;bootinfo));
<filename>sys/boot/i386/boot2/boot2.c:</filename>
__exec((caddr_t)addr, RB_BOOTINFO | (opts &amp; RBX_MASK),
MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
0, 0, 0, VTOP(&amp;bootinfo));
<application>loader</application> Stage
<application>loader</application> is a <acronym>BTX</acronym> client as well. I will not describe it here in detail, there is a comprehensive man page written by Mike Smith, <citerefentry><refentrytitle>loader</refentrytitle><manvolnum>8</manvolnum></citerefentry>. The underlying mechanisms and <acronym>BTX</acronym> were discussed above.
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:
<filename>sys/boot/common/boot.c:</filename>
/* Call the exec handler from the loader matching the kernel */
module_formats[km-&gt;m_loader]-&gt;l_exec(km);
Kernel Initialization
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.
<filename>sys/conf/Makefile.i386:</filename>
ld -elf -Bdynamic -T /usr/src/sys/conf/ldscript.i386 -export-dynamic \
-dynamic-linker /red/herring -o kernel -X locore.o \
&lt;lots of kernel .o files&gt;
<primary>ELF</primary> <primary> ELF </primary>
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</filename>, which is definitely a bogus file. Second, taking a look at the file <filename>sys/conf/ldscript.i386</filename> gives an idea about what <application>ld</application> options are used when compiling a kernel. Reading through the first few lines, the string
<filename>sys/conf/ldscript.i386:</filename>
ENTRY(btext)
<filename>sys/conf/ldscript.i386:</filename>
ENTRY(btext)
says that a kernel's entry point is the symbol `btext'. This symbol is defined in <filename>locore.s</filename>:
<filename>sys/i386/i386/locore.s:</filename>
.text
/**********************************************************************
*
* This is where the bootblocks start us, set the ball rolling...
*
*/
NON_GPROF_ENTRY(btext)
<filename>sys/i386/i386/locore.s:</filename>
.text
/**********************************************************************
*
* This is where the bootblocks start us, set the ball rolling...
*
*/
NON_GPROF_ENTRY(btext)
User avatar ygy@FreeBSD.org

Suggestion added

boot2 定义了很重要的引导信息数据结构。此结构由 boot2 初始化,然后传递到加载程序,再传到内核。

Suggested change:

boot2 定义了很重要的引导信息数据结构。此结构由 boot2 初始化,然后传递到加载程序,再传到内核。

Failing checks:

Mismatched full stop Mismatched colon XML markup 2 months ago

Loading…

<literal>boot2</literal> defines an important structure, <literal>struct bootinfo</literal>. This structure is initialized by <literal>boot2</literal> and passed to the loader, and then further to the kernel. Some nodes of this structures are set by <literal>boot2</literal>, the rest by the loader. This structure, among other information, contains the kernel filename, <acronym>BIOS</acronym> harddisk geometry, <acronym>BIOS</acronym> drive number for boot device, physical memory available, <literal>envp</literal> pointer etc. The definition for it is:
boot2 定义了很重要的引导信息数据结构。此结构由 boot2 初始化,然后传递到加载程序,再传到内核。
2 months ago
Browse all component changes

Things to check

Suggestions

There is 1 suggestion for this string.

View

XML markup

XML tags in translation do not match source

Reset

Mismatched colon

Source and translation do not both end with a colon

Reset

Mismatched full stop

Source and translation do not both end with a full stop

Reset

Glossary

English Chinese (Simplified) (zh_CN)
No related strings found in the glossary.

Source information

Source string comment
(itstool) path: sect1/para
Source string location
book.translate.xml:2004
String age
a year ago
Source string age
a year ago
Translation file
books/zh_CN/arch-handbook.po, string 199