![](/img/trans.png)
[英]How does kernel_thread_helper pass parameters to kernel thread function using inline assembly?
[英]How to pass parameters to Kernel using GRUB 0.97 menu.lst?
我正在開發一個操作系統,我必須創建一個調試模式。 為了做到這一點,我想在menu.lst中添加一個條目,指向同一個內核,但添加了一個參數。
在GRUB手冊中,它寫了內核命令中內核地址之后的所有內容都是逐字傳遞給內核命令行: https : //ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html
所以我在menu.lst中做了類似的事情:
title os-debug
root (fd0)
kernel /kernel 001
module /initrd.img
在GRUB創建的堆棧中,命令行在偏移量16處可用,如下所述: https : //www.gnu.org/software/grub/manual/multiboot/multiboot.html#Specification
所以在我的文件boot.SI中這樣做是為了在堆棧中找到我的參數:
movl 16(%ebx), %ecx
並且...它不起作用(我創建了一個gdbserver來調試這個特定的啟動文件),但我確信我可以正確訪問堆棧,因為我正在訪問initrd,如下所示:
movl 24(%ebx), %eax
我也正確地定義了我的旗幟:
#define MBOOT_FLAGS (MBOOT_PAGE_ALIGN | MBOOT_MEMORY_INFO |
MBOOT_INFO_CMDLINE)'
任何想法如何讓我從menu.lst傳遞參數到boot.S? 這是我的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
之后,構建了初始化內存,所以我必須先處理我的堆棧,但考慮到我的測試,我現在無法得到我的論點。
我的menu.lst:
timeout 10
title OS
root (fd0)
kernel /kernel
module /initrd.img
title OS-debug
root (fd0)
kernel /kernel 001
module /initrd.img
您沒有提供最小的完整可驗證示例。 我在架子上有一些代碼,我以前沒有把它放在Stackoverflow上。 以下只是一個帶有多引導頭的C文件,以及可用作測試代碼的基礎的內核入口點。 它依賴於多引導信息結構作為參數傳遞給kmain
(最初通過引導加載程序中的EBX )。
該代碼使用GRUB Legacy標頭中的定義。 如果您的系統上沒有安裝它,您可以在GNU站點上找到副本 。 還提供了一個基本的鏈接描述文件。
運行時,它應清除屏幕並打印出傳遞給內核的命令行,並將命令行傳遞給每個模塊。
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
:
OUTPUT_FORMAT("elf32-i386")
ENTRY(start)
SECTIONS
{
. = 1M;
.text : {
*(.multiboot)
*(.text)
}
.rodata : {
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(COMMON)
*(.bss)
}
}
您可以使用以下命令編譯這些文件並將其鏈接到名為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
這假設您使用的是交叉編譯器。 您可以在主機環境中使用gcc
(而不是i686-elf-gcc
),但我個人不推薦它。
您可以使用kernel.elf
使用GRUB構建ISO。 如果您創建一個名為myos.iso
的ISO,那么您可以使用QEMU和GDB來調試代碼,例如:
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'
如果要調試故障和中斷,則-no-reboot -no-shutdown -d int
選項很有用。 首先使用GDB存根啟動QEMU ,然后使用GDB調試QEMU會話。 我們將kernel.elf
文件傳遞給調試器,以便我們可以使用符號調試。
當停在kmain
(代碼中的C入口點)時,您實際上可以使用mb_info
命令查看整個mb_info
結構(十六進制):
p/x *mb_info
您將獲得可能類似於此的輸出:
$ 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}}}
如果要使用命令p (char *)mb_info->cmdline
,則可以讓調試器將命令行參數作為字符串打印出來。
運行此代碼時QEMU的屏幕截圖:
在我的GRUB配置中,我將000
作為命令行參數放入內核。 我添加了一些命令行參數為001
和002
的模塊。
我想回答我自己的問題,解釋為什么我的模型不起作用。 Michael Petch的模型工作正常,但在我的實現中我有一個不同的問題
GRUB 0.97文檔包含錯誤 : https : //ftp.gnu.org/old-gnu/Manuals/grub/html_node/kernel.html
該行的其余部分將作為內核命令行逐字傳遞。
它不是“其余部分”,而是“ 整行 ”,在我的例子中: kernel /kernel 001
所以,我沒有檢查足夠的內存,因此我總是將“rek”作為輸出,這對應於前3個字符。 我用這個匯編代碼解決了我的問題:
/* 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
其中#define MBOOT_KPARAM_OFFSET 0x00000008
,這對應於移位“kernel / kernel”所需的偏移量。 然后使用其余代碼將參數放入內存中,無論其大小如何(使用%edi和stosl)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.