简体   繁体   English

如何使用GRUB 0.97 menu.lst将参数传递给内核?

[英]How to pass parameters to Kernel using GRUB 0.97 menu.lst?

I'm working on an OS and I have to create a debug mode. 我正在开发一个操作系统,我必须创建一个调试模式。 In order to do this, I want to add an entry in menu.lst, pointing to the same kernel, but with an added argument. 为了做到这一点,我想在menu.lst中添加一个条目,指向同一个内核,但添加了一个参数。

In the GRUB manual, it's written that everything after kernel's address in the kernel command is passed verbatim to the kernel command line: https://ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html 在GRUB手册中,它写了内核命令中内核地址之后的所有内容都是逐字传递给内核命令行: https//ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html

So I did something like this in menu.lst: 所以我在menu.lst中做了类似的事情:

title   os-debug
    root (fd0)
    kernel /kernel 001
    module /initrd.img

In the stack created by GRUB, the command-line is available at offset 16, as stated here: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Specification 在GRUB创建的堆栈中,命令行在偏移量16处可用,如下所述: https//www.gnu.org/software/grub/manual/multiboot/multiboot.html#Specification

So in my file boot.SI did like this to find my parameter in the stack: 所以在我的文件boot.SI中这样做是为了在堆栈中找到我的参数:

movl 16(%ebx), %ecx

And... It doesn't work (I created a gdbserver in order to debug this specific boot file), but I'm sure I can access the stack correctly , because I'm accessing initrd like this: 并且...它不起作用(我创建了一个gdbserver来调试这个特定的启动文件),但我确信我可以正确访问堆栈,因为我正在访问initrd,如下所示:

movl 24(%ebx), %eax

I have also correctly defined my flags: 我也正确地定义了我的旗帜:

#define MBOOT_FLAGS (MBOOT_PAGE_ALIGN | MBOOT_MEMORY_INFO | 
MBOOT_INFO_CMDLINE)'

Any idea how can i get a parameter to be passed from menu.lst to boot.S? 任何想法如何让我从menu.lst传递参数到boot.S? Here's all the beginning of my boot.S file: 这是我的boot.S文件的所有开头:

/* Multiboot flags. */
 #define MBOOT_FLAGS (MBOOT_PAGE_ALIGN | MBOOT_MEMORY_INFO | 
 MBOOT_INFO_CMDLINE)

 /* Exported symbols. */
 .globl start
 .globl idle_pgdir

 .section .bootstrap

/*
* Grub multiboot header.
*/
.align 4
mboot_header:
    .long  MBOOT_MAGIC                  /* Magic number.              */
    .long  MBOOT_FLAGS                  /* Flags.                     */
    .long  -(MBOOT_MAGIC + MBOOT_FLAGS) /* Checksum.                  */
    .long  mboot_header                 /* Pointer to this structure. */


/*
* Kernel entry point.
*/
start:  
    cmpl $1, 20(%ebx)
    jne halt

    /* Retrieve initrd location. */
    movl 24(%ebx), %eax
    movl (%eax), %eax

    movl 16(%ebx), %ecx
    pushl %ecx

after, the init RAM is built so I have to process with my stack before, but I'm not able to have my argument at this point considering my tests 之后,构建了初始化内存,所以我必须先处理我的堆栈,但考虑到我的测试,我现在无法得到我的论点。

My menu.lst: 我的menu.lst:

timeout 10

title   OS
    root (fd0)
    kernel /kernel
    module /initrd.img

title   OS-debug
    root (fd0)
    kernel /kernel 001
    module /initrd.img

You don't present a minimal complete verifiable example. 您没有提供最小的完整可验证示例。 I had some code on the shelf that I hadn't placed onto Stackoverflow previously. 我在架子上有一些代码,我以前没有把它放在Stackoverflow上。 The following is simply a C file with a multiboot header and the entry point for a kernel that could be used as a base to test out your code. 以下只是一个带有多引导头的C文件,以及可用作测试代码的基础的内核入口点。 It relies on the multiboot info structure being passed as a parameter to kmain (originally via EBX from the bootloader). 它依赖于多引导信息结构作为参数传递给kmain (最初通过引导加载程序中的EBX )。

The code uses the defines in the GRUB Legacy header. 该代码使用GRUB Legacy标头中的定义。 If it isn't installed on your system you can find a copy on the GNU site . 如果您的系统上没有安装它,您可以在GNU站点上找到副本 A basic linker script is also presented. 还提供了一个基本的链接描述文件。

When run it should clear the screen and print out the command line that was passed to the kernel and the command line passed to each of the modules. 运行时,它应清除屏幕并打印出传递给内核的命令行,并将命令行传递给每个模块。

kernel.c

#include <multiboot.h>
#include <stdint.h>

/* STRINGIZE is a C macro that allow us to convert an integer to a string
 * for use by the C pre-processor */
#define STRINGIZE_INTERNAL(x) #x
#define STRINGIZE(x) STRINGIZE_INTERNAL(x)

/* 32k stack */
#define STACK_SIZE 32768

/* Define the multiboot structure that will be detectable by the multiboot
 * loader. Request the loader to provide us a memory information */

#define MULTIBOOT_FLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN)

struct multiboot_header mb_header
__attribute__ ((aligned (4), section(".multiboot"))) = {
    .magic    = MULTIBOOT_HEADER_MAGIC,
    .flags    = MULTIBOOT_FLAGS,
    .checksum = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_FLAGS)
};

/* Allocate space for a stack */
uint8_t stack[STACK_SIZE];

/* Entry point set in linker script that the mulitboot loader will transfer control to */
extern void start(void);
__asm__ (".global start\n"
         "start:\n\t"
             /* Set stack pointer to end of stack variable.
                Stack grows down. Align stack to 16 byte boundary */
             "mov $stack + " STRINGIZE(STACK_SIZE) ", %esp\n\t"
             "and $-16, %esp\n\t"

             "cld\n\t"         /* Ensure string instructions have forward movement */
             "sub $8, %esp\n\t"/* For alignment on call to kmain */
             "push %eax\n\t"   /* Pass magicnum in EAX as 2nd parameter */
             "push %ebx\n\t"   /* Pass multiboot info struct in EBX as 1st parameter */
             "call kmain\n\t"  /* At this point stack 16 byte aligned, call kernel */
             "add $16, %esp\n\t"

             /* Infinite loop to end */
             "cli\n"
         ".L0:\n\t"
             "hlt\n\t"
             "jmp .L0\n"
         );

/* Text mode video pointer */
volatile uint16_t *const video_memory = (uint16_t *)0xb8000;

#define VID_TEXT_COLUMNS 80
#define VID_TEXT_ROWS    25

void clear_screen_attr (uint8_t attr)
{
    uint16_t curpos = 0;
    while (curpos < VID_TEXT_COLUMNS * VID_TEXT_ROWS)
        video_memory[curpos++] = attr << 8 | ' ';
}

void print_string_xyattr (const char *str, uint16_t x, uint16_t y, uint8_t attr)
{
    uint16_t curpos = (x + y * VID_TEXT_COLUMNS);
    while (*str)
        video_memory[curpos++] = attr << 8 | *str++;
}

/* kmain is main C entry point */
void kmain(multiboot_info_t *mb_info, uint32_t magicnum)
{
    uint16_t curline = 0;
    multiboot_module_t *mb_modules;
    uint16_t modindex;

    clear_screen_attr (0x07);

    /* Verify we were booted from multiboot loader and print MB to the display */
    if (magicnum == MULTIBOOT_BOOTLOADER_MAGIC) {
        print_string_xyattr ("Multiboot Magic found", 0, curline++, 0x07);
        print_string_xyattr ("Command line: ", 0, curline, 0x07);
        print_string_xyattr ((const char *)mb_info->cmdline, 14, curline++, 0x57);

        /* For each module print out the command line arguments */
        mb_modules = (multiboot_module_t *)mb_info->mods_addr;
        for (modindex = 0; modindex < mb_info->mods_count; modindex++) {
            print_string_xyattr ("Module Cmd line:", 0, curline, 0x07);
            print_string_xyattr ((const char *)mb_modules[modindex].cmdline,
                                 17, curline++, 0x57);
        }
    }
    else
        print_string_xyattr ("Multiboot Magic not found", 0, curline++, 0x07);
}

linker.ld : linker.ld

OUTPUT_FORMAT("elf32-i386")
ENTRY(start)

SECTIONS
{
    . = 1M;

    .text : {
        *(.multiboot)
        *(.text)
    }

    .rodata : {
        *(.rodata)
    }

    .data : {
        *(.data)
    }

    .bss : {
        *(COMMON)
        *(.bss)
    }

}

You can compile and link these files to a final ELF executable called kernel.elf with commands like: 您可以使用以下命令编译这些文件并将其链接到名为kernel.elf的最终ELF可执行文件:

i686-elf-gcc -c -m32 -std=gnu99 -ffreestanding -nostdlib -O3 -Wall -Wextra \
    -g3 -I/usr/include/multiboot -o kernel.o kernel.c
i686-elf-gcc -m32 -Wl,--build-id=none -T linker.ld -ffreestanding -nostdlib \ 
    -lgcc -o kernel.elf kernel.o

This assumes you are using a cross compiler. 这假设您使用的是交叉编译器。 You may be able to get away with using just gcc (instead of i686-elf-gcc ) in your host environment although I personally don't recommend it. 您可以在主机环境中使用gcc (而不是i686-elf-gcc ),但我个人不推荐它。


Debugging 调试

You can build up an ISO with GRUB using kernel.elf . 您可以使用kernel.elf使用GRUB构建ISO。 If you create an ISO called myos.iso then you can use QEMU and GDB to debug the code with something like: 如果您创建一个名为myos.iso的ISO,那么您可以使用QEMUGDB来调试代码,例如:

qemu-system-i386 -cdrom myos.iso -d int -no-reboot -no-shutdown -S -s &
gdb kernel.elf \
        -ex 'target remote localhost:1234' \
        -ex 'break *kmain' \
        -ex 'continue'

The -no-reboot -no-shutdown -d int options are useful if you are debugging faults and interrupts. 如果要调试故障和中断,则-no-reboot -no-shutdown -d int选项很有用。 This first launches QEMU with a GDB stub and then GDB is used to debug the QEMU session. 首先使用GDB存根启动QEMU ,然后使用GDB调试QEMU会话。 We pass the kernel.elf file to the debugger so we can use symbolic debugging. 我们将kernel.elf文件传递给调试器,以便我们可以使用符号调试。

When stopped at kmain (the C entry point in the code) you can actually view the entire mb_info structure (in hex) with a command like: 当停在kmain (代码中的C入口点)时,您实际上可以使用mb_info命令查看整个mb_info结构(十六进制):

p/x *mb_info

You'd get output that may look similar to this: 您将获得可能类似于此的输出:

$1 = {flags = 0x1a6f, mem_lower = 0x27f, mem_upper = 0x1fb80, boot_device = 0xe0ffffff, cmdline = 0x10078, mods_count = 0x2, mods_addr = 0x100ac, u = {aout_sym = {tabsize = 0x12, strsize = 0x28, addr = 0x10164, reserved = 0xf}, elf_sec = {num = 0x12, size = 0x28, addr = 0x10164, shndx = 0xf}}, mmap_length = 0x90, mmap_addr = 0x100d4, drives_length = 0x0, drives_addr = 0x0, config_table = 0x0, boot_loader_name = 0x1007c, apm_table = 0x0, vbe_control_info = 0x10434, vbe_mode_info = 0x10634, vbe_mode = 0x3, vbe_interface_seg = 0xffff, vbe_interface_off = 0x6000, vbe_interface_len = 0x4f, framebuffer_addr = 0xb8000, framebuffer_pitch = 0xa0, framebuffer_width = 0x50, framebuffer_height = 0x19, framebuffer_bpp = 0x10, framebuffer_type = 0x2, {{ framebuffer_palette_addr = 0x0, framebuffer_palette_num_colors = 0x0}, { framebuffer_red_field_position = 0x0, framebuffer_red_mask_size = 0x0, framebuffer_green_field_position = 0x0, framebuffer_green_mask_size = 0x0, framebuffer_blue_field_position $ 1 = {flags = 0x1a6f,mem_lower = 0x27f,mem_upper = 0x1fb80,boot_device = 0xe0ffffff,cmdline = 0x10078,mods_count = 0x2,mods_addr = 0x100ac,u = {aout_sym = {tabsize = 0x12,strsize = 0x28,addr = 0x10164,reserved = 0xf},elf_sec = {num = 0x12,size = 0x28,addr = 0x10164,shndx = 0xf}},mmap_length = 0x90,mmap_addr = 0x100d4,drives_length = 0x0,drives_addr = 0x0,config_table = 0x0,boot_loader_name = 0x1007c,apm_table = 0x0,vbe_control_info = 0x10434,vbe_mode_info = 0x10634,vbe_mode = 0x3,vbe_interface_seg = 0xffff,vbe_interface_off = 0x6000,vbe_interface_len = 0x4f,framebuffer_addr = 0xb8000,framebuffer_pitch = 0xa0,framebuffer_width = 0x50,framebuffer_height = 0x19,framebuffer_bpp = 0x10,framebuffer_type = 0x2 ,{{framebuffer_palette_addr = 0x0,framebuffer_palette_num_colors = 0x0},{framebuffer_red_field_position = 0x0,framebuffer_red_mask_size = 0x0,framebuffer_green_field_position = 0x0,framebuffer_green_mask_size = 0x0,framebuffer_blue_field_position = 0x0, framebuffer_blue_mask_size = 0x0}}} = 0x0,framebuffer_blue_mask_size = 0x0}}}

If you were to use the command p (char *)mb_info->cmdline you can get the debugger to print the command line parameter as a string for you. 如果要使用命令p (char *)mb_info->cmdline ,则可以让调试器将命令行参数作为字符串打印出来。

A screenshot of QEMU when this code is run: 运行此代码时QEMU的屏幕截图:

在此输入图像描述

In my GRUB configuration I had placed 000 as the command line parameter to the kernel. 在我的GRUB配置中,我将000作为命令行参数放入内核。 I added a couple of modules with command line parameters of 001 and 002 . 我添加了一些命令行参数为001002的模块。

I'd like to answer my own question to explain why my model was not working. 我想回答我自己的问题,解释为什么我的模型不起作用。 The model of Michael Petch works correctly, but in my implementation I had a different problem Michael Petch的模型工作正常,但在我的实现中我有一个不同的问题

The GRUB 0.97 documentation contains an error : https://ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html GRUB 0.97文档包含错误https//ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html

The rest of the line is passed verbatim as the kernel command-line. 该行的其余部分将作为内核命令行逐字传递。

It's not "the rest of the line" but instead " the whole line ", in my case: kernel /kernel 001 它不是“其余部分”,而是“ 整行 ”,在我的例子中: kernel /kernel 001

So, I wasn't checking enough memory, and so I always had "rek" as an output, which was corresponding to the first 3 char. 所以,我没有检查足够的内存,因此我总是将“rek”作为输出,这对应于前3个字符。 I resolved my issue with this assembly code: 我用这个汇编代码解决了我的问题:

/* Retrieve command-line passed by GRUB. */
movl $cmdline, %edi
movl 16(%ebx), %ecx
addl $MBOOT_KPARAM_OFFSET, %ecx
jmp bottom
top:
    addl $4, %ecx 
    stosl
bottom:
    movl (%ecx), %eax
    cmpl $0,%eax
    jne top 

Where #define MBOOT_KPARAM_OFFSET 0x00000008 , which is corresponding to the offset needed to shift "kernel /kernel ". 其中#define MBOOT_KPARAM_OFFSET 0x00000008 ,这对应于移位“kernel / kernel”所需的偏移量。 The rest of the code is then used to put a parameter in the memory, no matter of its size (using %edi and stosl) 然后使用其余代码将参数放入内存中,无论其大小如何(使用%edi和stosl)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM