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.

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

So I did something like this in 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

So in my file boot.SI did like this to find my parameter in the stack:

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:

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? Here's all the beginning of my boot.S file:

/* 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:

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. 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. It relies on the multiboot info structure being passed as a parameter to kmain (originally via EBX from the bootloader).

The code uses the defines in the GRUB Legacy header. If it isn't installed on your system you can find a copy on the GNU site. 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 startn"
         "start:nt"
             /* Set stack pointer to end of stack variable.
                Stack grows down. Align stack to 16 byte boundary */
             "mov $stack + " STRINGIZE(STACK_SIZE) ", %espnt"
             "and $-16, %espnt"

             "cldnt"         /* Ensure string instructions have forward movement */
             "sub $8, %espnt"/* For alignment on call to kmain */
             "push %eaxnt"   /* Pass magicnum in EAX as 2nd parameter */
             "push %ebxnt"   /* Pass multiboot info struct in EBX as 1st parameter */
             "call kmainnt"  /* At this point stack 16 byte aligned, call kernel */
             "add $16, %espnt"

             /* Infinite loop to end */
             "clin"
         ".L0:nt"
             "hltnt"
             "jmp .L0n"
         );

/* 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 :

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:

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.


Debugging

You can build up an ISO with GRUB using kernel.elf . If you create an ISO called myos.iso then you can use QEMU and GDB to debug the code with something like:

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. This first launches QEMU with a GDB stub and then GDB is used to debug the QEMU session. We pass the kernel.elf file to the debugger so we can use symbolic debugging.

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:

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 = 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.

A screenshot of QEMU when this code is run:

In my GRUB configuration I had placed 000 as the command line parameter to the kernel. I added a couple of modules with command line parameters of 001 and 002 .


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

The GRUB 0.97 documentation contains an error : 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

So, I wasn't checking enough memory, and so I always had "rek" as an output, which was corresponding to the first 3 char. 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 ". The rest of the code is then used to put a parameter in the memory, no matter of its size (using %edi and stosl)

链接地址: http://www.djcxy.com/p/66808.html

上一篇: 如何从C中的串口打开,读取和写入

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