PrettyOS für GRUB kompilieren/anpassen



  • Hallo,

    habe, wie gestern angesprochen, PrettyOS Kernel zum Booten mit GRUB kompiliert/angepasst (unter Linux, mit Hilfe der folgenden Quelle http://lowlevel.brainsware.org/wiki/index.php/C-Kernel_mit_GRUB).
    Anbei Makefile makefile_linux:

    SOURCES = kernel.asm $(filter-out kernel.asm ,$(wildcard *.asm *.c))
    OBJECTS = $(addsuffix .o,$(basename $(SOURCES)))
    
    ASFLAGSOBJ= -O32 -f elf
    
    CFLAGS = -std=c99
    CFLAGS += -march=i386
    CFLAGS += -mtune=i386
    CFLAGS += -Werror
    CFLAGS += -Wall
    CFLAGS += -O2
    CFLAGS += -fomit-frame-pointer
    CFLAGS += -ffreestanding
    CFLAGS += -fleading-underscore
    CFLAGS += -nostdlib
    CFLAGS += -nostdinc
    CFLAGS += -fno-builtin
    CFLAGS += -fno-stack-protector
    CFLAGS += -Iinclude
    
    CC = gcc
    
    LDFLAGS= -T kernel.ld -Map kernel.map -nostdinc
    LDFLAGS_GRUB = -T kernelgrub.ld -Map kernelgrub.map -nostdinc
    LD = ld
    
    all: ckernel.sys ckernelgrub.sys
    	@echo No errors for $@.
    
    %.o: %.asm
    	nasm $(ASFLAGSOBJ) $< -o $@
    	@echo No errors for $@.
    
    ckernel.sys: $(OBJECTS)
    	$(LD) $(LDFLAGS) $+ -o $@
    	@echo No errors for $@.
    
    ckernelgrub.sys: $(OBJECTS)
    	$(LD) $(LDFLAGS_GRUB) $+ -o $@
    	@echo No errors for $@.
    
    data.asm: initrd.dat
    	@echo No errors for $@.
    
    clean:
    	rm -fv *.o
    	@echo No errors for $@.
    

    Neu sind dazugekommen: LDFLAGS_GRUB und ckernelgrub.sys.
    Für den Linker gibt es einen zweiten Linkerscript kernelgrub.ld:

    ENTRY (KernelStartFromGRUB)
    
    OUTPUT_FORMAT(elf32-i386)
    
    OUTPUT_ARCH(i386:i386)
    
    SECTIONS
    {
        . = 0x00100000;
    
        .text :
        {
            *(.text)
        }
    
        .rodata :
        {
            *(.rodata)
        }
    
        .data :
        {
            *(.data)
        }
    
        .bss :
        {
            __bss_start = .;
            _sbss = .;
            *(COMMON) *(.bss) _ebss = .;
        }
    
        __end = .;
    }
    

    Die kernel.asm Datei musste wie folgt angepasst werden:

    [Bits 32]
    
    global KernelStart
    global KernelStartFromGRUB
    extern __bss_start
    extern __end
    extern _main     ; entry point in ckernel.c
    
    ; Flags for GRUB header
    GRUB_FLAGS equ 0
    
    ; Magic number for GRUB header
    GRUB_MAGIC_NUMBER equ 0x1BADB002
    
    ; Checksum for GRUB header
    GRUB_HEADER_CHECKSUM equ -(GRUB_MAGIC_NUMBER + GRUB_FLAGS)
    
    section .text    ; ld needs that for coff format
    
    align 4
    MultiBootHeader:            ; This is a "multiboot" header for GRUB, this structure must be first (?)
        dd GRUB_MAGIC_NUMBER
        dd GRUB_FLAGS
        dd GRUB_HEADER_CHECKSUM
    
    ; This is an entry point for native PrettyOS bootloader
    KernelStart:
        mov ax, 0x10
        mov ds, ax   ; data descriptor --> data, stack and extra segment
        mov ss, ax
        mov es, ax
        xor eax, eax ; null desriptor --> FS and GS
        mov fs, ax
        mov gs, ax
    
        ; set bss section to zero
        mov edi, __bss_start
        mov ecx, __end
        sub ecx, __bss_start
        mov al, 0
        rep stosb   ; repeats instruction decrementing ECX until zero
                    ; and stores value from AL incrementing ES:EDI
    
        mov esp, 0x190000 ; set stack below 2 MB limit
    
        call _main  ; ->-> C-Kernel
    
    InfiniteLoop:   ; Perform infinite loop in case the kernel returns from main
        cli         ; Disable interrupts and try to halt the CPU
        hlt
        jmp InfiniteLoop
    
    ; This is an entry point for GRUB
    KernelStartFromGRUB:
        mov esp, 0x00200000
        push eax     ; EAX contains "multiboot" magic number (?)
        push ebx     ; EBX contains address of the "multiboot" structure (?)
        call _main
    
        jmp InfiniteLoop    ; Perform infinite loop in case the kernel returns from main
    

    Und dann habe ich in der ckernel.c die folgenden Zeilen auskommentiert, weil es bei mir auf dem Laptop dort irgendwo hängt:

    #if 0
        /// direct 1st floppy disk
        if( (cmos_read(0x10)>>4) == 4 ) // 1st floppy 1,44 MB: 0100....b
        {
            printformat("1.44 MB 1st floppy is installed\n\n");
    
            flpydsk_set_working_drive(0); // set drive 0 as current drive
    	    flpydsk_install(6);           // floppy disk uses IRQ 6
    	    k_memset((void*)DMA_BUFFER, 0x0, 0x2400);
        }
        else
        {
            printformat("1.44 MB 1st floppy not shown by CMOS\n\n");
        }
        /// direct 1st floppy disk
    #endif
    

    Nach dem Kompilieren hat man einmal die ckernel.sys und die ckernelgrub.sys. Die ckernelgrub.sys ist (laut file):

    # file ckernelgrub.sys 
    ckernelgrub.sys: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
    

    und lässt sich bei mir mit GRUB booten 🙂 Also, es funktioniert, Eingabeprompt erscheint und dort kann man ?, hi eingeben.

    Offene Fragen wären jetzt:
    - Warum funktioniert es? 🙂 GDT, LDT werden ja nun von GRUB initialisiert und es passt zufällig?
    - Was ist "multiboot" magic number, diese werden an die main() übergeben, sinnvoll?:

    push eax     ; EAX contains "multiboot" magic number (?)
        push ebx     ; EBX contains address of the "multiboot" structure (?)
        call _main
    

    Weitere Anregungen?
    🙂



  • Multiboot-kompatible Kernel wollen überprüfen, ob sie überhaupt von einem Multiboot-kompatiblen Bootloader geladen wollen, wenn sie Informationen aus der Multiboot-Struktur erhalten wollen. Deshalb die Magic-Number.

    GRUB setzt btw. eine GDT auf, die der Kernel allerdings durch seine eigene ersetzen muss.



  • abc.w schrieb:

    Offene Fragen wären jetzt:
    - Warum funktioniert es? 🙂 GDT, LDT werden ja nun von GRUB initialisiert und es passt zufällig?
    - Was ist "multiboot" magic number, diese werden an die main() übergeben, sinnvoll?:

    Lesen hilft. 🙂 [Moderator: Muss dass unbedingt sein?]

    http://lowlevel.brainsware.org/wiki/index.php/Multiboot
    http://www.gnu.org/software/grub/manual/multiboot/multiboot.pdf


  • Mod

    @abc.w: danke vielmals für die rasche Umsetzung. Super! 🙂

    GRUB setzt btw. eine GDT auf, die der Kernel allerdings durch seine eigene ersetzen muss.

    Das macht der Kernel von PrettyOS:

    static void init()
    {
        //...
        gdt_install(); // <------ hier passierts
        idt_install();
        isrs_install();
        irq_install();
        initODA();
        timer_install();
        keyboard_install();
    }
    

    🙂

    void gdt_install()
    {
        /* Setup the GDT pointer and limit */
        gdt_register.limit = (sizeof(struct gdt_entry) * NUMBER_GDT_GATES)-1;
        gdt_register.base  = (uint32_t) &gdt;
    
        /* GDT GATES -  desriptors with pointers to the linear memory address */
        gdt_set_gate(0, 0, 0, 0, 0);                // NULL descriptor
        gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // CODE, privilege level 0 for kernel mode (supervisor)
        gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // DATA, privilege level 0 for kernel mode (supervisor)
        gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // CODE, privilege level 3 for user mode
        gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // DATA, privilege level 3 for user mode
    
        write_tss(5, 0x10, 0x0);                    // num, ss0, esp0
    
        gdt_flush((uint32_t)&gdt_register);
        tss_flush();
    }
    


  • Ich möchte einen Vorschlag machen, wie man die "multiboot" Struktur an die main() des Kernels übergibt. In der kernel.asm so:

    [Bits 32]
    
    global KernelStart
    global KernelStartFromGRUB
    extern __bss_start
    extern __end
    extern _main     ; entry point in ckernel.c
    
    ; Flags for GRUB header
    GRUB_FLAGS equ 0
    
    ; Magic number for GRUB header
    GRUB_MAGIC_NUMBER equ 0x1BADB002
    
    ; Checksum for GRUB header
    GRUB_HEADER_CHECKSUM equ -(GRUB_MAGIC_NUMBER + GRUB_FLAGS)
    
    section .text    ; ld needs that for coff format
    
    align 4
    MultiBootHeader:            ; This is a "multiboot" header for GRUB, this structure must be first (?)
        dd GRUB_MAGIC_NUMBER
        dd GRUB_FLAGS
        dd GRUB_HEADER_CHECKSUM
    
    ; This is an entry point for native PrettyOS bootloader
    KernelStart:
        mov ax, 0x10
        mov ds, ax   ; data descriptor --> data, stack and extra segment
        mov ss, ax
        mov es, ax
        xor eax, eax ; null desriptor --> FS and GS
        mov fs, ax
        mov gs, ax
    
        ; set bss section to zero
        mov edi, __bss_start
        mov ecx, __end
        sub ecx, __bss_start
        mov al, 0
        rep stosb   ; repeats instruction decrementing ECX until zero
                    ; and stores value from AL incrementing ES:EDI
    
        mov esp, 0x190000 ; set stack below 2 MB limit
    
        ; Create argc and argv[] parameters on the stack for the kernel main() function
        ; Set both to zero because native bootloader is used and no information
        ; is available here
        push 0      ; argv is NULL
        push 0      ; argc is 0
        call _main  ; ->-> C-Kernel
    
    InfiniteLoop:   ; Perform infinite loop in case the kernel returns from main
        cli         ; Disable interrupts and try to halt the CPU
        hlt
        jmp InfiniteLoop
    
    ; This is an entry point for GRUB
    KernelStartFromGRUB:
        mov esp, 0x00200000
    
        ; Create argc and argv[] parameters on the stack for the kernel main() function
        ; Put in argv[] the "multiboot" magic number and the pointer to the "multiboot" structure
        ; argc must contain the number of valid entries in argv[]
        push 0       ; argv[N - 1], must be NULL
        push eax     ; argv[1], EAX contains "multiboot" magic number
        push ebx     ; argv[0], EBX contains address of the "multiboot" structure
        mov eax, esp
        push eax     ; argv
        push 2       ; argc = 2, because two values in argv[]
        call _main   ; ->-> C-Kernel
    
        jmp InfiniteLoop    ; Perform infinite loop in case the kernel returns from main
    

    In der ckernel.c kann man dann darauf zugreifen:

    int main(int argc, char *argv[])
    {
        init();
    
        // get physical memory which is usable RAM
        uint16_t num_of_entries = *( (uint16_t*)(ADDR_MEM_ENTRIES) );
    
        // Print the parameters of main.
        // This is a test to check, if GRUB magic number and structure are on the stack
        if (2 != argc)
        {
            printformat("Unexpected argc: %d", argc);
        }
        else if (0 == argv)
        {
            printformat("Invalid argv");
        }
        else
        {
            if (0x2BADB002u == (uint32_t)(argv[1]))
            {
                printformat(">Multiboot flag is valid<");
            }
            else
            {
                printformat(">Multiboot flag is not valid<");
            }
    
            printformat(" mbs_flags = %X", 0[(uint32_t*)(argv[0])]);
        }
        ...
    

    Neu sind also die argc und argv in main(): int main(int argc, char *argv[]) und argc und argv werden entsprechend in der kernel.asm gesetzt (s. oben).
    Die mbs_flags sind bei mir übrigens 3E7h 🙂
    Wäre es ok so?



  • Die Idee die Kernel main an eine "normale" main anzupassen ist ja an sich keine schlechte Idee, aber passt das hier?

    In einem normalen Programm dienen argc und argv dazu eine beliebige Anzahl an Parametern (in beliebiger Reihenfolge) zu übergeben, die dann vom Programm geprüft/geparst werden. Diese Parameter sind alles Zeichenketten.

    Der main Funktion im Kernel wird hier allerdings eine feste Anzahl (von der Reihenfolge abhängiger) Werte übergeben, die nicht zwangsläufig Zeichenketten sind.

    Außerdem wird das hinzufügen zusätzlicher Parameter eher schwerer als leichter. Zum einen muss man die Überprüfung von argc anpassen und wenn der neue Parameter irgendwo zwischendrin eingefügt wurde ändern sich die Indizes.

    Bei

    void main(void* mbd, unsigned int magic)
    

    schreibe ich einfach den zusätzlichen Parameter in die Definition und bin fertig.



  • Die jetztige Anpassung der main()-Funktion ist wirklich nicht sehr gelungen. Wir haben es hier auch nicht mit einer nativen Anwendung, sondern mit einem Kernel und dessen Einsprungspunkt zu tun. Und in Anbetracht der Dinge, dass ihr PrettyOS nun auch per GRUB starten könnt wollt ihr vielleicht in die Versuchung kommen, in Zukunft ein paar Multiboot-Informationen auszulesen.

    Vor allem wichtig ist aber, wie schon oben erwähnt, dass ihr überprüft, von wem ihr geladen wurdet (also die Magic-Boot-Nummer überprüft). Wenn ihr in euren eigenen Bootloader dann ebenfalls dem Kernel eine Magicnumber übergibt könnt ihr ganz einfach testen, mit was PrettyOS geladen wurde und dementsprechend handeln: Nehmt ihr nun die Memory-Map von GRUB oder die eigens herausgefundene Größe des RAMs usw. usf.

    Und ehe ich's vergesse: Schön, dass sich die verbissene Ablehnung gegen GRUB so lockert 😛


  • Mod

    Schön, dass sich die verbissene Ablehnung gegen GRUB so lockert

    Ich kenne die Vorzüge, die GRUB dem Nutzer bietet, habe nur den Quasi-Zwang seitens taljeth dies verwenden zu müssen bisher abgelehnt, und ich denke das war richtig so.



  • So, es geht voran.

    Ich hab (mit etwas Hilfe aus #lost) PrettyOS von GRUB laden können.

    Zunächst hab ich abc.ws kernel.asm und unsere miteinander "verschmolzen zu:

    [Bits 32]
    
    extern __bss_start
    extern __end
    extern _main     ; entry point in ckernel.c
    
    ; Flags for GRUB header
    GRUB_FLAGS equ 0
    
    ; Magic number for GRUB header
    GRUB_MAGIC_NUMBER equ 0x1BADB002
    
    ; Checksum for GRUB header
    GRUB_HEADER_CHECKSUM equ -(GRUB_MAGIC_NUMBER + GRUB_FLAGS)
    
    section .text    ; ld needs that for coff format
    
    align 4
    MultiBootHeader:            ; This is a "multiboot" header for GRUB, this structure must be first (?)
        dd GRUB_MAGIC_NUMBER
        dd GRUB_FLAGS
        dd GRUB_HEADER_CHECKSUM
    
    ; This is an entry point for native PrettyOS bootloader
    KernelStart:
        mov ax, 0x10
        mov ds, ax   ; data descriptor --> data, stack and extra segment
        mov ss, ax
        mov es, ax
        xor ax, ax   ; null desriptor --> FS and GS
        mov fs, ax
        mov gs, ax
    
        mov esp, 0x190000 ; set stack below 2 MiB limit
    
        call _main  ; ->-> C-Kernel
    	jmp $
    
    ; This is an entry point for GRUB
    KernelStartFromGRUB:
        mov esp, 0x00200000
        push eax     ; EAX contains "multiboot" magic number (?)
        push ebx     ; EBX contains address of the "multiboot" structure (?)
        call _main
    	jmp $
    

    Dann musste das Linkerscript noch dazu gebracht werden, einen ELF-Kernel zu erzeugen:

    OUTPUT_FORMAT(elf32-i386) 
    OUTPUT_ARCH(i386)
    STARTUP(object_files/kernel/kernel.o)
    SECTIONS
    {
        . = 0x00100000;
        __kernel_beg = .;
        .text   : { __code_start = .;                        }
        . = ALIGN(0x1000);
        .text1  : { object_files/kernel/data.o(.text)        }
        .data   : { __data_start = .;   *(.data)             }
        .rodata : { __rodata_start = .; *(.rodata) *(.rdata) }
        .bss    : { __bss_start = .;    *(.bss) *(COMMON)    }
        __start_cdi_drivers = .;
        .cdi    : { __cdi_start = .;    *(.cdi)              }
        __stop_cdi_drivers = .;
        __kernel_end = .;
        __end = .;
    }
    

    Und zu guter Letzt muss noch GRUB in den Bootsector der Floppy, wozu ich einen Rohling nehme, um mir das zu ersparen:
    http://pkeus.bplaced.net/PrettyOS/FloppyImage.img

    Dort hinein dann die kernel.bin kopieren und booten.

    Es ist noch nicht völlig lauffähig, weil die Übergabe der Memory Map noch nicht klappt.


Anmelden zum Antworten