这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
Multiboot_kFreeBSD [2019/05/24 10:16] whr Add reference |
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 could boot 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. | ||
| 行 47: | 行 47: | ||
| A FreeBSD 10.3-RELEASE-p29 amd64 operating system is used to develop the kernel; the kernel version is also 10.3-RELEASE-p29, with many [[https://sourceforge.net/p/hacking-freebsd/freebsd-patches/ci/master/tree/10.3/|customizations]]. | A FreeBSD 10.3-RELEASE-p29 amd64 operating system is used to develop the kernel; the kernel version is also 10.3-RELEASE-p29, with many [[https://sourceforge.net/p/hacking-freebsd/freebsd-patches/ci/master/tree/10.3/|customizations]]. | ||
| The targeted kernel architecture is i386. | The targeted kernel architecture is i386. | ||
| + | |||
| + | Testing is done on a QEMU i386 system emulator, that running on a Debian GNU/Linux 9 operating system. | ||
| ===== Goals ===== | ===== Goals ===== | ||
| 行 52: | 行 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. | ||
| - | In addition to those goals, it is obvious that the kernel image should still be compatibale 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. | ||
| + | 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. | ||
| - | 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 multboot support in BTX loader is designed for Xen only, it requires the first Multiboot module be original kFreeBSD image. | + | {{:multiboot-kfreebsd:btx-loader-treated-kfreebsd-as-xen.png?nolink|BTX loader complaining no kFreeBSD loaded as first Multiboot module}} |
| - | Trying to load a Multiboot kernel without loading any Multiboot modules would resulted in BTX loader complaining 'FreeBSD kernel not loaded'. | + | |
| - | To workaround this issue, I decided to modify BTX loader to [[https://sourceforge.net/p/hacking-freebsd/freebsd-patches/ci/master/tree/10.3/boot-disable-multiboot.diff|remove Multiboot support in it completely]]. Alternately using an older version of BTX loader is also working. | + | To workaround this issue, I did first modified BTX loader to [[https://sourceforge.net/p/hacking-freebsd/freebsd-patches/ci/master/tree/10.3/boot-i386-loader-multiboot-deprioritize.diff|try the Multiboot format (for Xen) after kFreeBSD format]]; but considered the Xen domain 0 support wasn't available in kernel version 10.3-RELEASE anyway, I later decided to [[https://sourceforge.net/p/hacking-freebsd/freebsd-patches/ci/master/tree/10.3/boot-disable-multiboot.diff|remove Multiboot support in it completely]]. Alternately using an older version of BTX loader is also working. |
| ===== Insert a Multiboot header ===== | ===== Insert a Multiboot header ===== | ||
| This part of work is very similar to what **Radek Szymczyszyn** does in DragonFly BSD kernel; in fact the issue of placing the header in first 8 KiB of the image is same between kFreeBSD and DragonFly BSD kernel. (4.2.2) | This part of work is very similar to what **Radek Szymczyszyn** does in DragonFly BSD kernel; in fact the issue of placing the header in first 8 KiB of the image is same between kFreeBSD and DragonFly BSD kernel. (4.2.2) | ||
| - | By almostly just copying existing code, the Multiboot header was added into source file ''i386/i386/locore.s'': | + | By almost just copying existing code, the Multiboot header was added into source file ''i386/i386/locore.s'': |
| <file asm i386/i386/locore.s> | <file asm i386/i386/locore.s> | ||
| .section .mbheader | .section .mbheader | ||
| 行 81: | 行 85: | ||
| The section ''.mbheader'' is used to control the location of Multiboot header in final image, using the [[https://svnweb.freebsd.org/base/releng/10.3/sys/conf/ldscript.i386?view=markup|linker script]]. | The section ''.mbheader'' is used to control the location of Multiboot header in final image, using the [[https://svnweb.freebsd.org/base/releng/10.3/sys/conf/ldscript.i386?view=markup|linker script]]. | ||
| - | Unfortunately the GNU ''ld(1)'' doesn't provide a clear way to sepcify the location of a section; Radek Szymczyszyn has actually asked a [[https://stackoverflow.com/questions/19912881/how-to-tell-force-gnu-ld-to-put-a-section-symbol-in-a-specific-part-of-the-out|question at Stack Overflow website]] for this issue. | + | Unfortunately the GNU ''ld(1)'' doesn't provide a clear way to specify the location of a section; Radek Szymczyszyn has actually asked a [[https://stackoverflow.com/questions/19912881/how-to-tell-force-gnu-ld-to-put-a-section-symbol-in-a-specific-part-of-the-out|question at Stack Overflow website]] for this issue. |
| - | Finally there is hacking I made to linker script: | + | Finally there is a hack I made to linker script: |
| <file diff> | <file diff> | ||
| diff -ru --exclude-from freebsd-src-diff-exclude-names --new-file a/sys/conf/ldscript.i386 b/sys/conf/ldscript.i386 | diff -ru --exclude-from freebsd-src-diff-exclude-names --new-file a/sys/conf/ldscript.i386 b/sys/conf/ldscript.i386 | ||
| 行 102: | 行 106: | ||
| Along with ''.interp'' section, the ''PT_INTERP'' is also removed by this modification; don't worry, it is useless too. | Along with ''.interp'' section, the ''PT_INTERP'' is also removed by this modification; don't worry, it is useless too. | ||
| - | Since ''.interp'' section is no longer exists, I tried to remove this interpreter string from final image; simply removing **ld(1)** option ''--dynamic-linker'' dosen't work because **ld(1)** will then uses default interpreter ''/usr/libexec/ld-elf.so.1'' or ''/usr/lib/libc.so.1'', for emulation types ''elf_i386_fbsd'' or ''elf_i386''. I finally set interpreter to an empty string; it disappeared in image completely after that: | + | Since ''.interp'' section is no longer exists, I tried to remove this interpreter string from final image; simply removing **ld(1)** option ''--dynamic-linker'' doesn't work because **ld(1)** will then uses default interpreter ''/usr/libexec/ld-elf.so.1'' or ''/usr/lib/libc.so.1'', for emulation types ''elf_i386_fbsd'' or ''elf_i386''. I finally set interpreter to an empty string; it disappeared in image completely after that: |
| <file diff> | <file diff> | ||
| diff -ru --exclude-from freebsd-src-diff-exclude-names --new-file a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk | diff -ru --exclude-from freebsd-src-diff-exclude-names --new-file a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk | ||
| 行 128: | 行 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. | ||
| 行 164: | 行 168: | ||
| * XXX this step is delayed in case recover_bootinfo needs to return via | * XXX this step is delayed in case recover_bootinfo needs to return via | ||
| </file> | </file> | ||
| - | As the code describes, if a Multboot bootloader magic not found, jump to original path to perform tasks to deal with bootinfo passed by BTX loader. | + | As the code describes, if a Multiboot bootloader magic not found, jump to original path to perform tasks to deal with bootinfo passed by BTX loader. |
| There is also a variable ''multiboot_env''; this is used to access Multiboot specific information, such as the memory information we requested via ''MULTIBOOT_MEMORY_INFO'', and the kernel command line. Its address is passed by bootloader in ''ebx'' register; this address must be saved for use later (for goal 3 and laters) before ''ebx'' got overwritten. | There is also a variable ''multiboot_env''; this is used to access Multiboot specific information, such as the memory information we requested via ''MULTIBOOT_MEMORY_INFO'', and the kernel command line. Its address is passed by bootloader in ''ebx'' register; this address must be saved for use later (for goal 3 and laters) before ''ebx'' got overwritten. | ||
| 行 172: | 行 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. | ||
| 行 205: | 行 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 activited, 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?view=markup | + | 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> | ||
| 行 224: | 行 228: | ||
| When kFreeBSD is booted by BTX loader, this information was already passed by BTX loader (called SMAP), as the loader has done those BIOS calls already. The kernel will trying to call BIOS by itself if the information isn't available from bootloader. | When kFreeBSD is booted by BTX loader, this information was already passed by BTX loader (called SMAP), as the loader has done those BIOS calls already. The kernel will trying to call BIOS by itself if the information isn't available from bootloader. | ||
| - | This problem has made me suspect that memory detection code in kernel has somehow faulted, because the same kernel panic occurs even it is booted by BTX loader, after I manually wipped out SMAP information in ''bootinfo''. | + | This problem has made me suspect that memory detection code in kernel has somehow faulted, because the same kernel panic occurs even it is booted by BTX loader, after I manually wiped out SMAP information in ''bootinfo''. |
| By manually skipping those memory detection code in ''gdb(1)'' and let it fallback to RTC I/O for memory size, the kernel could continue start up normally. However this resulted in final detected memory size be limited to 64 MiB, in a QEMU emulated machine have 512 MiB memory. | By manually skipping those memory detection code in ''gdb(1)'' and let it fallback to RTC I/O for memory size, the kernel could continue start up normally. However this resulted in final detected memory size be limited to 64 MiB, in a QEMU emulated machine have 512 MiB memory. | ||
| 行 285: | 行 289: | ||
| Near the entry point ''btext'', after a temporary stack has setup, the magic number is checked again for Multiboot; function ''recover_multiboot_env'' may get called to save Multiboot specific information. | Near the entry point ''btext'', after a temporary stack has setup, the magic number is checked again for Multiboot; function ''recover_multiboot_env'' may get called to save Multiboot specific information. | ||
| - | The kernel command line and Multiboot modules information may also available, and will be saved from this function if any; this will be descript in later chapter. | + | The kernel command line and Multiboot modules information may also available, and will be saved from this function if any; this will be described in later chapter. |
| Next is to modify function ''getmemsize'' to make use of those memory size if it available: | Next is to modify function ''getmemsize'' to make use of those memory size if it available: | ||
| 行 320: | 行 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. | ||
| 行 328: | 行 332: | ||
| ===== Prasing the kernel command line ===== | ===== Prasing the kernel command line ===== | ||
| - | Multiboot Specification provided a way to configure the kernel from bootloader by passing it a command line. Unlike usual userspace programs in UNIX, this command line is a C string ending with 0; kernels have to parse it into ''argv'' style string matrix manually if needed. | + | Multiboot Specification provided a way to configure the kernel from bootloader by passing it a command line. Unlike usual user-space programs in UNIX, this command line is a C string ending with 0; kernels have to parse it into ''argv'' style string matrix manually if needed. |
| 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. | ||
| 行 334: | 行 338: | ||
| 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''. | 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 userspace 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 userspace as they can only be set from kernel environment, which are turn from the bootloader; those 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 automaticly 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''. |
| - | To make those kernel tunables taking effect, initialization of kernel environment must be taken before any tunable was retrived. | + | To make those kernel tunables taking effect, initialization of kernel environment must be taken before any tunable was retrieved. |
| From the beginning of function ''init386'', kernel module information pointer and kernel environment pointer was prepared, then ''init_param1'' was called: | From the beginning of function ''init386'', kernel module information pointer and kernel environment pointer was prepared, then ''init_param1'' was called: | ||
| 行 359: | 行 363: | ||
| init_param1(); | init_param1(); | ||
| </file> | </file> | ||
| - | This code shows that both ''preload_metadata'' and kernel environment was retrived from ''bootinfo'' structare, which won't be available if the kernel was booted from a Multiboot bootloader. | + | This code shows that both ''preload_metadata'' and kernel environment was retrieved from ''bootinfo'' structure, which won't be available if the kernel was booted from a Multiboot bootloader. |
| It should also be pointed out that function ''init_param1'' will be called immediately after initialization of kernel environment, there is a comment for this call indicates some tunables will be initialized; this means there is no chance for any later initialization of kernel environment for Multiboot kernel command line. | It should also be pointed out that function ''init_param1'' will be called immediately after initialization of kernel environment, there is a comment for this call indicates some tunables will be initialized; this means there is no chance for any later initialization of kernel environment for Multiboot kernel command line. | ||
| 行 379: | 行 383: | ||
| </file> | </file> | ||
| - | Parsing the command line should use no more that 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> | ||
| 行 427: | 行 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'' values, 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. | ||
| 行 434: | 行 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''. | ||
| 行 455: | 行 459: | ||
| Each preloaded file need at least 4 nodes to describe it, ''MODINFO_NAME'', ''MODINFO_TYPE'', ''MODINFO_ADDR'' and ''MODINFO_SIZE''; type ''MODINFO_NAME'' is specical, it must be placed before all other nodes that describes a particular file, because this type indicates the appearance of a new preloaded file, and all other type of nodes followed to describe this file. | Each preloaded file need at least 4 nodes to describe it, ''MODINFO_NAME'', ''MODINFO_TYPE'', ''MODINFO_ADDR'' and ''MODINFO_SIZE''; type ''MODINFO_NAME'' is specical, it must be placed before all other nodes that describes a particular file, because this type indicates the appearance of a new preloaded file, and all other type of nodes followed to describe this file. | ||
| - | Initially all address information in this metadate buffer are physical addresses; they will be converted to virtual addresses later in function ''preload_bootstrap_relocate''. | + | Initially all address information in this metadata buffer are physical addresses; they will be converted to virtual addresses later in function ''preload_bootstrap_relocate''. |
| - | As previously stated, the first file that needed to be descript is the kernel image itself; the nodes will be constructed as follows: | + | As previously stated, the first file that needed to be described is the kernel image itself; the nodes will be constructed as follows: |
| * ''MODINFO_NAME'' set from variable ''kernelname'' | * ''MODINFO_NAME'' set from variable ''kernelname'' | ||
| 行 465: | 行 469: | ||
| * ''MODINFO_SIZE'' is calculated from ''_end'' and ''KERNLOAD'' | * ''MODINFO_SIZE'' is calculated from ''_end'' and ''KERNLOAD'' | ||
| - | The Multiboot module information is again retrived at function ''recover_multiboot_env''. | + | The Multiboot module information is again retrieved at function ''recover_multiboot_env''. |
| The beginning of function ''init386'' was modified again to add another call to initialize ''bootinfo.bi_modulep'': | The beginning of function ''init386'' was modified again to add another call to initialize ''bootinfo.bi_modulep'': | ||
| <file diff> | <file diff> | ||
| 行 505: | 行 509: | ||
| This function at first call ''fill_module_info_from_multiboot'' with a null pointer to get the required buffer size, then allocate such a buffer, call ''fill_module_info_from_multiboot'' again with that buffer. | This function at first call ''fill_module_info_from_multiboot'' with a null pointer to get the required buffer size, then allocate such a buffer, call ''fill_module_info_from_multiboot'' again with that buffer. | ||
| - | To retrive information of Multiboot modules, this new function ''fill_module_info_from_multiboot'' need to access ''multiboot_mods'' that previously saved; ''multiboot_mods'' contains 2 field, module count and a pointer to module information block array: | + | To retrieve information of Multiboot modules, this new function ''fill_module_info_from_multiboot'' need to access ''multiboot_mods'' that previously saved; ''multiboot_mods'' contains 2 field, module count and a pointer to module information block array: |
| <file c i386/include/multiboot.h> | <file c i386/include/multiboot.h> | ||
| extern struct { | extern struct { | ||
| 行 525: | 行 529: | ||
| ... | ... | ||
| </file> | </file> | ||
| - | Each Multiboot module is descript by 3 values, a start and an end address, and and pointer to an optional command line, as following structure in C: | + | Each Multiboot module is described by 3 values, a start and an end address, and and pointer to an optional command line, as following structure in C: |
| <file c i386/include/multiboot.h> | <file c i386/include/multiboot.h> | ||
| struct multiboot_module { | struct multiboot_module { | ||
| 行 536: | 行 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. | ||
| - | Coverting 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. | ||
| * ''MODINFO_TYPE'' is determined by examining and validating the ELF header of the module, then set to either ''elf module'' or ''md_image''. | * ''MODINFO_TYPE'' is determined by examining and validating the ELF header of the module, then set to either ''elf module'' or ''md_image''. | ||
| - | * ''MODINFO_ARGS'' will also be set if there appear 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 ==== | ||
| 行 565: | 行 569: | ||
| {{:multiboot-kfreebsd:kernel-panic-on-accessing-initrd-2.png?nolink|Kernel panic on mounting initrd with 2048 MiB RAM}} | {{:multiboot-kfreebsd:kernel-panic-on-accessing-initrd-2.png?nolink|Kernel panic on mounting initrd with 2048 MiB RAM}} | ||
| - | This time the kernel panic orrurred when it actually trying to mount the initrd; which means the initial work of **md(4)** for this initrd has been successfully done. This phenomenon indicates the initrd is very likely been corrupted when the kernel starts up. | + | This time the kernel panic orrurred when it actually trying to mount the initrd; which means the initial work of **md(4)** for this initrd has been successfully done. This phenomenon indicates the initrd is very likely been corrupted during the kernel starting up. |
| Debugging in **gdb(1)** shows something suspicious: | Debugging in **gdb(1)** shows something suspicious: | ||
| 行 643: | 行 647: | ||
| $5 = 0x1427000 | $5 = 0x1427000 | ||
| </file> | </file> | ||
| - | The Multiboot module information structare shows this module was loaded between physical addresses 0x120c000 and 0x320c000; but the ''first'' value that points to first free page of physical memory, in function ''init386'' is 0x1427000. Since this 'free' memory will also be used when parsing kernel command line and storing the module metadata, preparing metadata for the module will also corrupting it! | + | The Multiboot module information structure shows this module was loaded between physical addresses 0x120c000 and 0x320c000; but the ''first'' value that points to first free page of physical memory, in function ''init386'' is 0x1427000. Since this 'free' memory will also be used when parsing kernel command line and storing the module metadata, preparing metadata for the module will also corrupting it! |
| Variable ''first'' is passed from ''begin'' in ''i386/i386/locore.s'', by another variable ''physfree''; which is turn set from ''bootinfo.bi_kernend'', or if that isn't available, ''&_end'', in function ''create_pagetables''. | Variable ''first'' is passed from ''begin'' in ''i386/i386/locore.s'', by another variable ''physfree''; which is turn set from ''bootinfo.bi_kernend'', or if that isn't available, ''&_end'', in function ''create_pagetables''. | ||
| - | BTX loader sets ''bootinfo.bi_kernend'' according to ending location of the loaded kernel image and all modules. Multiboot bootloader, of course won't set it, so the kernel fallback to set ''physfree'' using ''&_end'' value, so it simply didn't aware any addition modules when using the memory. | + | BTX loader sets ''bootinfo.bi_kernend'' according to ending location of the loaded kernel image and all modules. Multiboot bootloader, of course won't set it, the kernel fallback to set ''physfree'' using ''&_end'' value, so it simply didn't aware any addition modules when using the memory. |
| To get the correct ending of Multiboot modules, ''bootinfo.bi_kernend'' must be set if any of such modules present. This must be done before ''bootinfo.bi_kernend'' is used, in ''create_pagetables`'' preferably in `recover_multiboot_env`. | To get the correct ending of Multiboot modules, ''bootinfo.bi_kernend'' must be set if any of such modules present. This must be done before ''bootinfo.bi_kernend'' is used, in ''create_pagetables`'' preferably in `recover_multiboot_env`. | ||
| 行 725: | 行 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. | ||
| 行 733: | 行 737: | ||
| For the KLD modules that built for the kernel, the data segment always have ''Offset'' less than ''VirtAddr''; meaning such a segment must be moved to a higher memory (1 page in this case), combined with the initialization of ''.bss'' section, there is a risk of corrupting later segments, or even wrose, later Multiboot modules. | For the KLD modules that built for the kernel, the data segment always have ''Offset'' less than ''VirtAddr''; meaning such a segment must be moved to a higher memory (1 page in this case), combined with the initialization of ''.bss'' section, there is a risk of corrupting later segments, or even wrose, later Multiboot modules. | ||
| - | First, this implementation assuming that all data segments that need to be relocated will be the last loadable segment of given KLD module; second, the ending addresses of Mulitboot modules will be checked, modules that would need to write over the end of current Mulitboot module will be skipped for KLD handling; such a KLD module will remain in memory but won't functional due to missing module metadata. | + | First, this implementation assuming that all data segments that need to be relocated will be the last loadable segment of given KLD module; second, the ending addresses of Multiboot modules will be checked, modules that would need to write over the end of current Multiboot module will be skipped for KLD handling; such a KLD module will remain in memory but won't functional due to missing module metadata. |
| Remember those KLD modules are shared objects, they need to be linked into kernel to function. | Remember those KLD modules are shared objects, they need to be linked into kernel to function. | ||
| 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. | ||
| - | The implementation of retriving symbol information is mostly copying from the BTX loader; 2 more metadata nodes will added if such information is available from a particular KLD module; node types ''MODINFO_METADATA | MODINFOMD_SSYM'' and ''MODINFO_METADATA | MODINFOMD_ESYM'' storing start and end address of symbol table. The addresses stored in metadata nodes are physical addresses, and expecting to be converted to virtual addresses later, just like ''MODINFO_ADDR''. | + | The implementation of retrieving symbol information is mostly copying from the BTX loader; 2 more metadata nodes will added if such information is available from a particular KLD module; node types ''MODINFO_METADATA | MODINFOMD_SSYM'' and ''MODINFO_METADATA | MODINFOMD_ESYM'' storing start and end address of symbol table. The addresses stored in metadata nodes are physical addresses, and expecting to be converted to virtual addresses later, just like ''MODINFO_ADDR''. |
| ===== Final test ===== | ===== Final test ===== | ||
| 行 750: | 行 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. | ||