这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
Multiboot_kFreeBSD [2019/07/23 04:45] whr Fix typo |
Multiboot_kFreeBSD [2024/10/23 09:34] (当前版本) whr Correct grammar issues |
||
|---|---|---|---|
| 行 2: | 行 2: | ||
| - | Copyright 2019 Rivoreo | + | Copyright 2019-2021 Rivoreo |
| Licensed under Creative Commons Attribution-Sharealike 4.0 International. | Licensed under Creative Commons Attribution-Sharealike 4.0 International. | ||
| - | As a FreeBSD kernel hacker, there are only 2 bootloaders I known that capable for booting the kernel of FreeBSD for x86; one is the BTX loader(8) that is a part of FreeBSD project, while another is GRUB 2. | + | As a FreeBSD kernel hacker, I knowns there are only 2 bootloaders I known that capable for booting the kernel of FreeBSD for x86; one is the BTX loader(8) that is a part of FreeBSD project, while another is GRUB 2. |
| It could be very useful to make the FreeBSD kernel bootable with Multiboot protocol, as Multiboot are supported by many x86 bootloaders, such as GRUB (both legacy and version 2), syslinux (via mboot.32) and Linux **kexec(2)**; as well as QEMU direct kernel boot, this could make kernel debugging easier. | It could be very useful to make the FreeBSD kernel bootable with Multiboot protocol, as Multiboot are supported by many x86 bootloaders, such as GRUB (both legacy and version 2), syslinux (via mboot.32) and Linux **kexec(2)**; as well as QEMU direct kernel boot, this could make kernel debugging easier. | ||
| 行 54: | 行 54: | ||
| - Add a multiboot header, to make the kernel image to be recognized as a multiboot kernel. | - Add a multiboot header, to make the kernel image to be recognized as a multiboot kernel. | ||
| - Make the kernel bootable from multiboot bootloader. | - Make the kernel bootable from multiboot bootloader. | ||
| - | - Parse the command line passed from multiboot bootloader, to construct kFreeBSD specific ''boothowto'' variable and kernel environment variables. | + | - Parse the command line passed from multiboot bootloader, to construct kFreeBSD-specific ''boothowto'' variable and kernel environment variables. |
| - Use multiboot module passed from bootloader as an initrd. | - Use multiboot module passed from bootloader as an initrd. | ||
| - Use multiboot modules passed from bootloader as kFreeBSD KLD modules. | - Use multiboot modules passed from bootloader as kFreeBSD KLD modules. | ||
| 行 60: | 行 60: | ||
| In addition to those goals, it is obvious that the kernel image should still be compatible with BTX loader; however it is not possible for BTX loader provided by version 10.3-RELEASE or later, due to a design issue of BTX loader. | In addition to those goals, it is obvious that the kernel image should still be compatible with BTX loader; however it is not possible for BTX loader provided by version 10.3-RELEASE or later, due to a design issue of BTX loader. | ||
| - | Since 10.3-RELEASE, the BTX loader included in distribution supports Multiboot, but only for Xen (the Xen image is a Multiboot-compliant kernel). The real issue is the loader detecting multiboot kernel image at first; it will trying to load kernel with Multiboot protocol only, if a Multiboot-compliant kernel is detected; Because this multiboot support in BTX loader is designed for Xen only, it requires the first Multiboot module be original kFreeBSD image. | + | Since 10.3-RELEASE, the BTX loader included in distribution supports Multiboot, but only for Xen (the Xen image is a Multiboot-compliant kernel). The real issue is the loader detecting multiboot kernel image at first; it will trying to load kernel with Multiboot protocol only, if a Multiboot-compliant kernel is detected. Because this multiboot support in BTX loader is designed for Xen only, it requires the first Multiboot module be original kFreeBSD image. |
| Trying to load a Multiboot kernel without loading any Multiboot modules would resulted in BTX loader complaining 'No FreeBSD kernel provided, aborting'. This happens right after achievement of goal 1. | Trying to load a Multiboot kernel without loading any Multiboot modules would resulted in BTX loader complaining 'No FreeBSD kernel provided, aborting'. This happens right after achievement of goal 1. | ||
| 行 132: | 行 132: | ||
| ===== Make the kernel bootable and functional ===== | ===== Make the kernel bootable and functional ===== | ||
| - | When FreeBSD BTX loader boots a kernel up, it passes some add addiction information, in ''struct bootinfo'' ''bootinfo'' and some other variables, most importantly ''boothowto''; kFreeBSD collects those information in function ''recover_bootinfo'', defined in source file ''i386/i386/locore.s''. | + | When FreeBSD BTX loader boots a kernel up, it passes some add addiction information, in ''struct bootinfo'' ''bootinfo'' and some other variables, most importantly ''boothowto''; kFreeBSD collects these information in function ''recover_bootinfo'', defined in source file ''i386/i386/locore.s''. |
| This step must be skipped if kernel is booted by a Multiboot bootloader; because Multiboot bootloaders are not aware those FreeBSD specific information, and if the kernel will simply halt if such information isn't present. | This step must be skipped if kernel is booted by a Multiboot bootloader; because Multiboot bootloaders are not aware those FreeBSD specific information, and if the kernel will simply halt if such information isn't present. | ||
| 行 176: | 行 176: | ||
| {{:multiboot-kfreebsd:kernel-hangs-in-qemu.png?nolink|Kernel hangs in QEMU}} | {{:multiboot-kfreebsd:kernel-hangs-in-qemu.png?nolink|Kernel hangs in QEMU}} | ||
| - | QEMU has a built-in GDB compatible server, for remote debugging with ''gdb(1)''. This is very useful for kernel debugging. To enable this server, simply adding option ''-s'' or ''-gdb tcp::<port>''; if ''-s'' is used, it will **listen(2)** TCP port 1234. See **qemu-system(1)** for more information. | + | QEMU has a built-in GDB-compatible server, for remote debugging with ''gdb(1)''. This is very useful for kernel debugging. To enable this server, simply adding option ''-s'' or ''-gdb tcp::<port>''; if ''-s'' is used, it will **listen(2)** TCP port 1234. See **qemu-system(1)** for more information. |
| With help of **gdb(1)**, I found the kernel encountered a panic shortly after boot; the panic occurs very early at kernel internal start up progress, before local console initialization. | With help of **gdb(1)**, I found the kernel encountered a panic shortly after boot; the panic occurs very early at kernel internal start up progress, before local console initialization. | ||
| 行 209: | 行 209: | ||
| #4 0x00000000 in ?? () | #4 0x00000000 in ?? () | ||
| </file> | </file> | ||
| - | Although this backtrace didn't provide many useful information of where the issue occurred, by the fact that **panic(9)** has been called, indicating paging was activated, IDT and GDT has been setup; just before console finishes its initialization, the problem should sit in function ''init386'', between those code: | + | Although this backtrace didn't provide many useful information of where the issue occurred, by the fact that **panic(9)** has been called, indicating paging was activated, IDT and GDT has been setup; just before console finishes its initialization. The problem should be sitting in function ''init386'', between those code: |
| https://svnweb.freebsd.org/base/releng/10.3/sys/i386/i386/machdep.c?revision=296373&view=markup#l3367 | https://svnweb.freebsd.org/base/releng/10.3/sys/i386/i386/machdep.c?revision=296373&view=markup#l3367 | ||
| <file c i386/i386/machdep.c> | <file c i386/i386/machdep.c> | ||
| 行 324: | 行 324: | ||
| </file> | </file> | ||
| - | The kernel is now functional with Multiboot, achieve goal 2. | + | The kernel is now functional with Multiboot, achieving goal 2. |
| Following screenshots showing it booted up with QEMU direct kernel loading, but asking for root device due to kernel environment isn't available. | Following screenshots showing it booted up with QEMU direct kernel loading, but asking for root device due to kernel environment isn't available. | ||
| 行 336: | 行 336: | ||
| FreeBSD BTX loader also support some command line options to change the behavior of the loader itself or kFreeBSD; when the meaning of a command line option needs to be passed into kernel, loader uses a bitwise variable to store it; when this variable later been passed into kernel, it will be store into ''boothowto''; possible bits of ''boothowto'' is defined in ''sys/reboot.h'', note only some of the bits are making sence to pass from a bootloader. Some useful bits are ''RB_SINGLE'' to boot into single user mode, by passing option ''-s'' to **init(8)**, and ''RB_VERBOSE'' to turn verbose logging on by setting ''bootverbose'' variable. | FreeBSD BTX loader also support some command line options to change the behavior of the loader itself or kFreeBSD; when the meaning of a command line option needs to be passed into kernel, loader uses a bitwise variable to store it; when this variable later been passed into kernel, it will be store into ''boothowto''; possible bits of ''boothowto'' is defined in ''sys/reboot.h'', note only some of the bits are making sence to pass from a bootloader. Some useful bits are ''RB_SINGLE'' to boot into single user mode, by passing option ''-s'' to **init(8)**, and ''RB_VERBOSE'' to turn verbose logging on by setting ''bootverbose'' variable. | ||
| - | Those options that originally been interpreted by BTX loader should be turned to the kernel itself to parse; the result should be store to ''boothowto'' variable as well. This means filling ''boothowto'' from command line options should happens as early as possible; it is best to do that before any uses of ''boothowto''. | + | Those options that originally been interpreted by BTX loader should be turned into the kernel itself to parse; the result should be store to ''boothowto'' variable as well. This means filling ''boothowto'' from command line options should happens as early as possible; it is best to do that before any uses of ''boothowto''. |
| - | Another scheme the BTX loader passing information to kernel is **kernel environment**. Just like the environment for user-space programs, the kernel environment is built up by individual environment variables; while an environment variable is a C string with format ''<key>=<value>''. | + | Another scheme the BTX loader passing information to kernel is **kernel environment**. Just like the environment for user-space programs, the kernel environment is built up by individual environment variables; where an environment variable is a C string with format ''<key>=<value>''. |
| - | The kernel environment in kFreeBSD in an important scheme to adjust kernel configuration on boot time, many environment variables are used to set initial value of corresponding **sysctl** variables. Some variables are read-only to user-space as they can only be set from kernel environment, which are turn from the bootloader; they are called **kernel tunables**. | + | The kernel environment in kFreeBSD in an important scheme to adjust kernel configuration on boot time; many environment variables are used to set initial value of corresponding **sysctl** variables. Some variables are read-only to user-space as they can only be set from kernel environment, which are turn from the bootloader; they are called **kernel tunables**. |
| Most important variables for automatically booting the system are ''vfs.root.monutfrom*''; otherwise the kernel will asking for which device to mount as root, like if ''RB_ASKNAME'' is set in ''boothowto''. | Most important variables for automatically booting the system are ''vfs.root.monutfrom*''; otherwise the kernel will asking for which device to mount as root, like if ''RB_ASKNAME'' is set in ''boothowto''. | ||
| 行 385: | 行 385: | ||
| Parsing the command line should use no more than a page of memory, because the maximum length of a command line is limited by Multiboot Specification, which is 4 KiB, exactly same as the common page size on i386. | Parsing the command line should use no more than a page of memory, because the maximum length of a command line is limited by Multiboot Specification, which is 4 KiB, exactly same as the common page size on i386. | ||
| - | The added code will allocate one page and pass its virtual address to a new function ''parse_kernel_command_line''; this function will parsing the command line and initializing kernel environment, so a later call to ''init_static_kenv'' will be skipped in case of Multiboot. | + | The added code will allocate one page and pass its virtual address to a new function ''parse_kernel_command_line''; this function will parse the command line and initialize kernel environment, so a later call to ''init_static_kenv'' will be skipped in case of Multiboot. |
| This allocated page will holding initial static kernel environment, after the command line is fully parsed, in ''parse_kernel_command_line''. | This allocated page will holding initial static kernel environment, after the command line is fully parsed, in ''parse_kernel_command_line''. | ||
| <file diff> | <file diff> | ||
| 行 431: | 行 431: | ||
| The implementation of function ''parse_kernel_command_line'' is added in source file ''kern/init_main.c''. | The implementation of function ''parse_kernel_command_line'' is added in source file ''kern/init_main.c''. | ||
| - | In addition to the options that can be translated to ''boothowto'' flags, 2 custom options was also implemented: | + | In addition to the options that can be translated to ''boothowto'' flags, 3 custom options was also implemented: |
| * ''-e <env-var>'' Set a kernel environment variable, exactly same as putting a variable without an option. | * ''-e <env-var>'' Set a kernel environment variable, exactly same as putting a variable without an option. | ||
| 行 438: | 行 438: | ||
| ===== Handle Multiboot modules ===== | ===== Handle Multiboot modules ===== | ||
| - | A part from Multiboot kernel image itself, Multiboot bootloaders may also load one or more files into memory for kernel, those preloaded files usually called modules in Multiboot. | + | A part from Multiboot kernel image itself, Multiboot bootloaders may also load one or more files into memory for kernel, these preloaded files usually called modules in Multiboot. |
| - | There are 2 typical usages of the preloaded files in kFreeBSD; memory disk for root file system (initrd), and KLD modules. | + | There are 2 typical uses of the preloaded files in kFreeBSD, memory disk for root file system (initrd), and KLD modules. |
| FreeBSD BTX loader load addition files as KLD modules by default; an explicit specification of file type is required of any other types. To load an initrd, the type must be set as ''md_image'' or ''mfs_root''. | FreeBSD BTX loader load addition files as KLD modules by default; an explicit specification of file type is required of any other types. To load an initrd, the type must be set as ''md_image'' or ''mfs_root''. | ||
| 行 540: | 行 540: | ||
| Field ''pad'' is a reserved in current specification, and should always be set to 0 by bootloaders. | Field ''pad'' is a reserved in current specification, and should always be set to 0 by bootloaders. | ||
| - | Converting this Multiboot specific information to kFreeBSD preloaded file metadata is done as follows. | + | Converting this Multiboot specific information to kFreeBSD preloaded file metadata is done as followings. |
| * ''MODINFO_NAME'' will set from module command line, because many Multiboot bootloaders set the file name as the first part of the command line. | * ''MODINFO_NAME'' will set from module command line, because many Multiboot bootloaders set the file name as the first part of the command line. | ||
| 行 546: | 行 546: | ||
| * ''MODINFO_ARGS'' will also be set if there are any additional command line after the module name. | * ''MODINFO_ARGS'' will also be set if there are any additional command line after the module name. | ||
| * ''MODINFO_ADDR'' and ''MODINFO_SIZE'' is set from ''mod_start'' and ''mod_end'', the address is keeping in physical address for further converts. | * ''MODINFO_ADDR'' and ''MODINFO_SIZE'' is set from ''mod_start'' and ''mod_end'', the address is keeping in physical address for further converts. | ||
| - | * Some extra examines and adjustments will be taken if this module is a KLD module (have a valid ELF header), to make this KLD functional later. | + | * Some extra examines and adjustments will be taken if this module is a KLD module (having a valid ELF header), in order to make this KLD functional later. |
| ==== Testing with initrd ==== | ==== Testing with initrd ==== | ||
| 行 729: | 行 729: | ||
| ''Offset'' means offset to the ELF file. ''VirtAddr'' is the targeting address; it is relative because this file is a shared object. ''PhysAddr'' is ignored since KLD only care about virtual addresses. ''FileSiz'' indicates size of this segment store in the ELF file. ''MemSiz'' is the size that must be preserved after loading this segment into memory. | ''Offset'' means offset to the ELF file. ''VirtAddr'' is the targeting address; it is relative because this file is a shared object. ''PhysAddr'' is ignored since KLD only care about virtual addresses. ''FileSiz'' indicates size of this segment store in the ELF file. ''MemSiz'' is the size that must be preserved after loading this segment into memory. | ||
| - | Only segments have the ''LOAD'' type (''PT_LOAD'') are need to be loaded. | + | Only segments with the ''LOAD'' type (''PT_LOAD'') are needed to be loaded. |
| As above example, not all loadable segments have its offset equals to its relative virtual address; this will causing problems when the entire ELF file have been loaded by a Multiboot bootloader; don't expect this KLD module will work without fixing the loading location. | As above example, not all loadable segments have its offset equals to its relative virtual address; this will causing problems when the entire ELF file have been loaded by a Multiboot bootloader; don't expect this KLD module will work without fixing the loading location. | ||
| 行 742: | 行 742: | ||
| In order to link a KLD module, the KLD subsystem need to known its ''.dynamic'' section, that been mapped with ''DYNAMIC'' segment. The ''DYNAMIC'' segment is assumed to be included in another ''LOAD'' segment, usually the data segment; the relative start address of ''DYNAMIC'' segment is stored in a new metadata node with type ''MODINFO_METADATA | MODINFOMD_DYNAMIC''. | In order to link a KLD module, the KLD subsystem need to known its ''.dynamic'' section, that been mapped with ''DYNAMIC'' segment. The ''DYNAMIC'' segment is assumed to be included in another ''LOAD'' segment, usually the data segment; the relative start address of ''DYNAMIC'' segment is stored in a new metadata node with type ''MODINFO_METADATA | MODINFOMD_DYNAMIC''. | ||
| - | BTX loader will also examining the section headers of KLD modules if any, to get more information of its symbol table; it is optional because section headers are not used to make a KLD module functional, but providing useful information of symbols, that could be used in the in-kernel debugger DDB. | + | BTX loader will also examine the section headers of KLD modules if any, to get more information of its symbol table; it is optional because section headers are not used to make a KLD module functional, but providing useful information of symbols, that could be used in the in-kernel debugger DDB. |
| A KLD module could have its symbol table stripped to reduce file size; this step will be skipped if symbol table is not there. | A KLD module could have its symbol table stripped to reduce file size; this step will be skipped if symbol table is not there. | ||
| 行 754: | 行 754: | ||
| qemu-system-i386 -m 512 -kernel kernel -append "-i /init -vp" -initrd tmpfs.ko,msdosfs.ko,fuse.ko,initrd,ipl.ko,ipfw.ko,dummynet.ko,ipdivert.ko,if_tap.ko,netgraph.ko,vesa.ko,snp.ko,imgact_binmisc.ko,svr4.ko | qemu-system-i386 -m 512 -kernel kernel -append "-i /init -vp" -initrd tmpfs.ko,msdosfs.ko,fuse.ko,initrd,ipl.ko,ipfw.ko,dummynet.ko,ipdivert.ko,if_tap.ko,netgraph.ko,vesa.ko,snp.ko,imgact_binmisc.ko,svr4.ko | ||
| </file> | </file> | ||
| - | The boot is booting in verbose mode this time, to show more information including preloaded files. | + | The kernel is booting in verbose mode this time, to show more information including preloaded files. |
| Option ''-p'' is used to pause on each console output line, for early startup progress. | Option ''-p'' is used to pause on each console output line, for early startup progress. | ||