如何使用内核创建可引导CD映像?

问题描述:

我有一个内核,要启动,我正在使用命令qemu-system-i386 -kernel kernel.bin.有什么方法可以创建可引导磁盘映像以通过qemu-system-i386 -cdrom CD.iso引导吗?

I have a kernel, to boot I am using command qemu-system-i386 -kernel kernel.bin. Is any way to create bootable disk image to boot with qemu-system-i386 -cdrom CD.iso?

我正在使用以下命令在linux中编译的代码:

The code I am compiling in linux with these commands:

nasm -f elf32 kernel.asm -o kernelasm.o
gcc -m32 -c kernel.c -o kernelc.o
ld -m elf_i386 -T link.ld -o kernel.bin kernelasm.o kernelc.o

,然后使用qemu-system-i386 -kernel kernel.bin

代码: kernel.asm:

CODE: kernel.asm:

[BITS 32]
SECTION .text
    align 4
    dd 0x1BADB002
    dd 0x00
    dd - (0x1BADB002 + 0x00)

global start
global keyboard_handler
global read_port
global write_port
global load_idt

extern kmain
extern keyboard_handler_main

read_port:
    mov edx, [esp + 4]
    in al, dx
    ret

write_port:
    mov   edx, [esp + 4]    
    mov   al, [esp + 4 + 4]  
    out   dx, al  
    ret

load_idt:
    mov edx, [esp + 4]
    lidt [edx]
    sti
    ret

keyboard_handler:                 
    call    keyboard_handler_main
    iretd

start:
    cli
    mov esp, stack_space
    call kmain
    hlt

section .bss
resb 8192
stack_space:

kernel.c:

#include "keyboard_map.h"

#define LINES 25
#define COLUMNS_IN_LINE 80
#define BYTES_FOR_EACH_ELEMENT 2
#define SCREENSIZE BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE * LINES

#define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_STATUS_PORT 0x64
#define IDT_SIZE 256
#define INTERRUPT_GATE 0x8e
#define KERNEL_CODE_SEGMENT_OFFSET 0x08

#define ENTER_KEY_CODE 0x1C

extern unsigned char keyboard_map[128];
extern void keyboard_handler(void);
extern char read_port(unsigned short port);
extern void write_port(unsigned short port, unsigned char data);
extern void load_idt(unsigned long *idt_ptr);

unsigned int current_loc = 0;
char *vidptr = (char*)0xb8000;

struct IDT_entry {
    unsigned short int offset_lowerbits;
    unsigned short int selector;
    unsigned char zero;
    unsigned char type_attr;
    unsigned short int offset_higherbits;
};

struct IDT_entry IDT[IDT_SIZE];


void idt_init(void)
{
    unsigned long keyboard_address;
    unsigned long idt_address;
    unsigned long idt_ptr[2];

    keyboard_address = (unsigned long)keyboard_handler;
    IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
    IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
    IDT[0x21].zero = 0;
    IDT[0x21].type_attr = INTERRUPT_GATE;
    IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;

    write_port(0x20 , 0x11);
    write_port(0xA0 , 0x11);

    write_port(0x21 , 0x20);
    write_port(0xA1 , 0x28);

    write_port(0x21 , 0x00);
    write_port(0xA1 , 0x00);

    write_port(0x21 , 0x01);
    write_port(0xA1 , 0x01);

    write_port(0x21 , 0xff);
    write_port(0xA1 , 0xff);

    idt_address = (unsigned long)IDT ;
    idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
    idt_ptr[1] = idt_address >> 16 ;

    load_idt(idt_ptr);
}

void kb_init(void)
{
    write_port(0x21 , 0xFD);
}

void kprint(const char *str)
{
    unsigned int i = 0;
    while (str[i] != '\0') {
        vidptr[current_loc++] = str[i++];
        vidptr[current_loc++] = 0x07;
    }
}

void kprint_newline(void)
{
    unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE;
    current_loc = current_loc + (line_size - current_loc % (line_size));
}

void clear_screen(void)
{
    unsigned int i = 0;
    while (i < SCREENSIZE) {
        vidptr[i++] = ' ';
        vidptr[i++] = 0x07;
    }
}

void keyboard_handler_main(void)
{
    unsigned char status;
    char keycode;

    write_port(0x20, 0x20);

    status = read_port(KEYBOARD_STATUS_PORT);
    if (status & 0x01) {
        keycode = read_port(KEYBOARD_DATA_PORT);
        if(keycode < 0)
            return;

        if(keycode == ENTER_KEY_CODE) {
            kprint_newline();
            return;
        }

        vidptr[current_loc++] = keyboard_map[(unsigned char) keycode];
        vidptr[current_loc++] = 0x07;
    }
}

void kmain(void)
{
    const char *str = "my first kernel with keyboard support";
    clear_screen();
    kprint(str);
    kprint_newline();
    kprint_newline();

    idt_init();
    kb_init();

    while(1);
}

keyboard_map.h:

keyboard_map.h:

unsigned char keyboard_map[128] =
{
    0,  27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
  '9', '0', '-', '=', '\b', /* Backspace */
  '\t',         /* Tab */
  'q', 'w', 'e', 'r',   /* 19 */
  't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* Enter key */
    0,          /* 29   - Control */
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 39 */
 '\'', '`',   0,        /* Left shift */
 '\\', 'z', 'x', 'c', 'v', 'b', 'n',            /* 49 */
  'm', ',', '.', '/',   0,              /* Right shift */
  '*',
    0,  /* Alt */
  ' ',  /* Space bar */
    0,  /* Caps lock */
    0,  /* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,  /* < ... F10 */
    0,  /* 69 - Num lock*/
    0,  /* Scroll Lock */
    0,  /* Home key */
    0,  /* Up Arrow */
    0,  /* Page Up */
  '-',
    0,  /* Left Arrow */
    0,
    0,  /* Right Arrow */
  '+',
    0,  /* 79 - End key*/
    0,  /* Down Arrow */
    0,  /* Page Down */
    0,  /* Insert Key */
    0,  /* Delete Key */
    0,   0,   0,
    0,  /* F11 Key */
    0,  /* F12 Key */
    0,  /* All other keys are undefined */
};

link.ld:

OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
 {
   . = 0x100000;
   .text : { *(.text) }
   .data : { *(.data) }
   .bss  : { *(.bss)  }
 }

首先,我为您提供有关引导过程如何真正工作的基本概念.实际上,当您运行命令qemu-system-i386 -kernel kernel.bin时,Qemu会将内核二进制文件加载到位置0x7c000的内存中,从该位置进一步进行引导. 如果要从ISO引导,则必须告诉BIOS我的iso中有一个可引导映像(标记引导标志),并给出适当的说明来装载内核.

First I give you basic idea of how booting process really works. Actually when you run command qemu-system-i386 -kernel kernel.bin Qemu loads up your kernel binary into memory at location 0x7c000 from where booting further proceed. If you want to boot from ISO then you have to tell the BIOS that there is a Bootable image (mark boot flag) in my iso, and give it proper instructions to laod up your kernel.

该怎么做?
您必须设置一个可以由BIOS在0x7c000及更高版本加载的Bootloader,它将您的内核映像加载到内存中并跳转到内核入口点.
因此,将您的ISO标记为活动(启动标志)并添加启动加载程序代码

How to do that?
You have to setup a Bootloader that can be loaded by your BIOS at 0x7c000 and later It would load your Kernel Image into memory and jump to kernel entry point.
So, mark your ISO active (boot flag) and add boot loader code.

我可以看到您已经设置了多引导入口点代码

I can see you already have setup multiboot entrypoint code

align 4
dd 0x1BADB002
dd 0x00
dd - (0x1BADB002 + 0x00)

您可以从此处 http://wiki上了解有关设置 grub引导链的更多信息. .osdev.org/GRUB_2 您还可以使用 syslinux引导程序 http://www. syslinux.org/wiki/index.php?title=The_Syslinux_Project

You can read more about setting up grub boot chain from here http://wiki.osdev.org/GRUB_2 You can also use syslinux bootloader http://www.syslinux.org/wiki/index.php?title=The_Syslinux_Project

syslinux 将isolinux.bin,syslinux.cfg和mboot.c32复制到内核二进制映像的构建路径. 配置syslinux.cfg并执行以下命令.

syslinux Copy isolinux.bin, syslinux.cfg and mboot.c32 to your build path of your kernel binary image. configure syslinux.cfg and execute the following command.

mkisofs.exe -o %OUTPUT_DIR%\%BUILD_NAME%.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table %ISO_DIR%