Autor Thema: C-Kernel ohne Multiboot  (Gelesen 36886 mal)

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« am: 22. May 2011, 12:31 »
Hallo Forum,

ich kämpfe nun schon seit längerer Zeit mit Problemen mit einem kleinen Assembler/C-Kernel.

Gleich vorweg: Ich habe kein "echtes" eingebautes Diskettenlaufwerk, sondern ein USB-Teil, und das versteht sich leider mit meiner openSUSE-Installation nicht so gut (scheint generell nicht unterstützt zu werden). Deshalb hab ich mich entschieden, erstmal kein Multiboot zu verwenden. Hab auch schon einen kleinen Asm-Bootsektor geschrieben, der einen kleinen "Hello World"-Kernel lädt. (Funktioniert tadellos) Nun wollte ich einfach mal diesen Kernel in C umsetzen (nur um zu sehen, wie das im Prinzip läuft), aber der verhält sich bockig. Ich vermute, das Problem liegt am Build-Vorgang, denn der Kernel verwendet keinerlei "böse" Funktionen.

Hier der Code:

; boot.asm
[global _start]
[extern init]

_start:
call init

hang:
jmp hang

times 512-($-$$)-2 db 0
dw 0xAA55

#include "kernel.h"

void init(void)
{
printline("Hello World!\0");
}

void printline(char hw[])
{
int i;
char* video = (char*) 0xb8000;

for (i = 0; hw[i] != '\0'; i++) {
 
        video[i * 2] = hw[i];
 
        video[i * 2 + 1] = 0x07;
}
}

#ifndef KERNEL_H
#define KERNEL_H

void main(void);
void printline(char hw[]);

#endif

/*  Bei _start soll die Ausfuehrung losgehen */
ENTRY(_start)
OUTPUT_FORMAT(binary)
OUTPUT_ARCH(i386:i386)

SECTIONS
{

    . = 0x7E00;

    .text : {
        *(.text)
    }
    .rodata : {
        *(.rodata)
    }
    .data : {
        *(.data)
    }
    .bss : {
_sbss = .;
*(COMMON)
        *(.bss)
_ebss = .;
    }
}

CC = /usr/bin/gcc
LD = /usr/bin/ld
AS = /usr/bin/nasm
CFLAGS = -Wall -Wextra -Werror -nostdlib -nostartfiles -nodefaultlibs -ffreestanding -c
LDFLAGS = -T kernel.ld
ASFLAGS = -f aout

prog:
$(AS) $(ASFLAGS) -o a.out boot.asm
$(CC) $(CFLAGS) -o b.out kernel.c
$(LD) $(LDFLAGS) -o kernel.img a.out b.out
hexdump kernel.img

init:
umount /dev/sda2
ntfs-3g /dev/sda2 /mnt/windata

clean:
rm a.out
rm b.out
rm kernel.img
rm *~

.PHONY: clean

Ich hoffe doch, das mein Anfängerfehler nicht zu gravierend ist.

P.S.: Bei Bedarf kann ich auch den Hexdump anfügen!

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 22. May 2011, 12:39 »
Dein Fehler liegt darin, dass, wenn du den code so verwendest, ohne anderen code meine ich, du gcc 32 bit code erstellen lässt, du aber beim starten noch im 16 bit, so genannten real mode, modus bist.
das heißt für dich jetzt, du musst einen bootloader schreiben, der in den protected mode springt (32 bit modus) und dann deine c funktion aufruft (von einem kernel würde ich noch nicht sprechen, aber das ist haarspalterei).
besonders würde ich dir die tutorial reihe aus der wiki empfehlen, da wir auch erklärt, wie du einen c-kernel aufsetzt. ich glaube sogar, dass es auch drinsteht, wie du einen c-kernel ohne grub erstellst.

mfg
chris
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 22. May 2011, 12:49 »
irgendsowas hab ich mir schon gedacht...
gibt es denn keine gcc flag, um 16bit code zu erstellen?

thx
tiger717

P.S.: Ja, ich weiß schon was Real Mode usw ist. (ich les schon seit ca einem Jahr die Artikel hier, es wundert mich, dass ich noch keinen 2.Windows programmiert habe...) Allerdings bin ich halt kein C-Guru (und werde es niemals sein. Assembler ist doch die schönere Sprache ;) )

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 22. May 2011, 12:53 »
ich verweise mal kurz auf diesen beitrag [Haltbarkeitsdatum überschritten]
mfg
« Letzte Änderung: 22. May 2011, 14:33 von chris12 »
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 22. May 2011, 13:16 »
Wenn man GCC 16-Bit Code erzeugen lässt, ist das Problem, dass Offsets größer als 64KB immer noch nicht möglich sind. Also kommst du z.B. an den Grafikspeicher für Textausgabe nicht ran, weil 0xb8000 zu groß ist.
Dieser Text wird unter jedem Beitrag angezeigt.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 22. May 2011, 14:29 »
Hallo,


ich verweise mal kurz auf diesen beitrag [gefährlicher Link entfernt]
Der dort gemachte Tipp ist nur unter ganz speziellen Voraussetzungen einsetzbar. Vor allem funktioniert das nicht wenn externe Funktionen aufgerufen werden die selber in 32 Bit verfasst sind. Des weiteren geht der gcc immer implizit von Flat-Memory aus und das trifft ja auf den Real-Mode nicht zu. Dieser Tipp kann eventuell in speziellen Situationen funktionieren aber das ist kein generischer Weg um den gcc Real-Mode-Code erzeugen zu lassen!

Ich würde ja fast vorschlagen wollen den alten Beitrag zu löschen aber das wäre IMHO nicht im Sinne eines Forums so das ich darum Bitte da eine Warnung drunter zu setzen (am besten ohne das der Beitrag im Board wieder nach oben kommt).


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 22. May 2011, 16:19 »
Ich finde das etwas übertrieben.
Dieser Text wird unter jedem Beitrag angezeigt.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 22. May 2011, 18:44 »
hab jetzt mein bootloader um den Pmode-init teil erweitert (inzwischen als 2-Stage, code war mit 800 Bytes zu groß). Allerdings gibt es ein neues Problem: der Linker mag mich nicht und fügt den Kernel nicht ein.

; boot.asm
[global _start]
[extern pmode]
[BITS 16]
[section .text]

_start:
call pmode

hang:
jmp hang

times 512-($-$$)-2 db 0
dw 0xAA55

; boot.asm
[global pmode]
[extern init]
[BITS 16]
[section .text]

pmode:
 
cli ; Interrupts ausschalten
lgdt [gdtr] ; GDT Pointer laden
 
mov eax,cr0 ; In PMode wechseln, indem das niedrigste
or al,1 ; Steuerungsbit von cr0 geändert wird
mov cr0,eax ; muss über Umweg über ein anderes Register gemacht werden
 
jmp codesel:PMode ; FarJump zu einer 32-Bit PMode Funktion
 
[BITS 32]
PMode:
mov ax,datasel ; Segmentregister laden
mov ds,ax
mov ss,ax
mov esp,0x90000 ; Stack aufsetzen

call init

hang:
jmp hang

[section .data]

gdtr: ; Desktiptortabelle
   dw gdt_end-gdt-1 ; Limit
   dd gdt ; Basisadresse
gdt:
   dd 0,0 ; Null-Deskriptor
codesel equ $-gdt
   dw 0xFFFF ; Segmentgrösse 0..15
   dw 0x0000 ; Segmentadresse 0..15
   db 0x00 ; Segmentadresse 16..23
   db 0x9A ; Zugriffsberechtigung und Typ
   db 0xCF ; Zusatzinformationen und Segmentgrösse 16...19
   db 0x00 ; Segmentadresse 24..31
datasel equ $-gdt
   dw 0xFFFF ; Segmentgrösse 0..15
   dw 0x0000 ; Segmentadresse 0..15
   db 0x00 ; Segmentadresse 16..23
   db 0x92 ; Zugriffsberechtigung und Typ
   db 0xCF ; Zusatzinformationen und Segmentgrösse 16...19
   db 0x00 ; Segmentadresse 24..31
gdt_end:

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 22. May 2011, 20:00 »
Gleich vorweg: Ich habe kein "echtes" eingebautes Diskettenlaufwerk, sondern ein USB-Teil, und das versteht sich leider mit meiner openSUSE-Installation nicht so gut (scheint generell nicht unterstützt zu werden). Deshalb hab ich mich entschieden, erstmal kein Multiboot zu verwenden.
Muss ich das verstehen? Wieso kannst du ohne funktionierendes Floppylaufwerk einen handgebastelten Bootloader laden, aber keinen GRUB? Vorteil von Multiboot wäre auch noch, dass du den Kernel da einfach (mit dem für Linux sowieso vorhandenen GRUB) von der Platte booten kannst, ohne den MBR zu überschreiben.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 22. May 2011, 20:02 »
hab jetzt mein bootloader um den Pmode-init teil erweitert (inzwischen als 2-Stage, code war mit 800 Bytes zu groß). Allerdings gibt es ein neues Problem: der Linker mag mich nicht und fügt den Kernel nicht ein.

; boot.asm
[global _start]
[extern pmode]
[BITS 16]
[section .text]

_start:
call pmode

hang:
jmp hang

times 512-($-$$)-2 db 0
dw 0xAA55
Nach diesem Stück Code sind die 512 Bytes Bootsektor zu Ende, die das BIOS für dich lädt. Der Rest von deinem Kernel/Bootloader landet also nie im Speicher und das call pmode springt irgendwo in einen undefinierten Bereich.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 22. May 2011, 21:39 »
Gleich vorweg: Ich habe kein "echtes" eingebautes Diskettenlaufwerk, sondern ein USB-Teil, und das versteht sich leider mit meiner openSUSE-Installation nicht so gut (scheint generell nicht unterstützt zu werden). Deshalb hab ich mich entschieden, erstmal kein Multiboot zu verwenden.
Muss ich das verstehen? Wieso kannst du ohne funktionierendes Floppylaufwerk einen handgebastelten Bootloader laden, aber keinen GRUB? Vorteil von Multiboot wäre auch noch, dass du den Kernel da einfach (mit dem für Linux sowieso vorhandenen "GRUB) von der Platte booten kannst, ohne den MBR zu überschreiben.
openSUSE erkennt das Diskettenlaufwerk nicht (einfach mal nach "opensuse usb floppy" googlen). Ich vermute, dass GRUB meinen Code nicht erkennt (ist ja beides auf unixoider Basis). Das BIOS erkennt den Bootloader tadellos.

hab jetzt mein bootloader um den Pmode-init teil erweitert (inzwischen als 2-Stage, code war mit 800 Bytes zu groß). Allerdings gibt es ein neues Problem: der Linker mag mich nicht und fügt den Kernel nicht ein.

; boot.asm
[global _start]
[extern pmode]
[BITS 16]
[section .text]

_start:
call pmode

hang:
jmp hang

times 512-($-$$)-2 db 0
dw 0xAA55
Nach diesem Stück Code sind die 512 Bytes Bootsektor zu Ende, die das BIOS für dich lädt. Der Rest von deinem Kernel/Bootloader landet also nie im Speicher und das call pmode springt irgendwo in einen undefinierten Bereich.
Ja,das stimmt tatsächlich. Aber trotzdem, beim Hexdump erscheint der C-Kernel überhaupt nicht:
0000000 fde8 e901 fffd 0000 0000 0000 0000 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 0ffa 1601 8098 200f 0cc0 0f01 c022 13ea
0000210 0880 6600 10b8 8e00 8ed8 bcd0 0000 0009
0000220 07e8 0000 e900 fffb ffff 9090 8955 83e5
0000230 18ec 04c7 8a24 0080 e800 0002 0000 c3c9
0000240 8955 83e5 10ec 45c7 00f8 0b80 c700 fc45
0000250 0000 0000 25eb 458b 01fc 03c0 f845 558b
0000260 03fc 0855 b60f 8812 8b10 fc45 c001 c083
0000270 0301 f845 00c6 8307 fc45 8b01 fc45 4503
0000280 0f08 00b6 c084 ce75 c3c9 6548 6c6c 206f
0000290 6f57 6c72 2164 0000 0017 809e 0000 0000
00002a0 0000 0000 0000 ffff 0000 9a00 00cf ffff
00002b0 0000 9200 00cf 9090                    
00002b8

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #11 am: 22. May 2011, 21:56 »
Moin

vermute, dass GRUB meinen Code nicht erkennt (ist ja beides auf unixoider Basis). Das BIOS erkennt den Bootloader tadellos.

Vermutest du das nur oder hast du Grub schon getestet? Normalerweise müsste GRUB auch mit USB Diskettenlaufwerken gehen.
Kannst du von deinem USB Diskettenlaufwerk booten?

PNoob

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 22. May 2011, 22:04 »
Moin

vermute, dass GRUB meinen Code nicht erkennt (ist ja beides auf unixoider Basis). Das BIOS erkennt den Bootloader tadellos.

Vermutest du das nur oder hast du Grub schon getestet? Normalerweise müsste GRUB auch mit USB Diskettenlaufwerken gehen.
Kannst du von deinem USB Diskettenlaufwerk booten?

PNoob

Hast du etwa ein USB-Diskettenlaufwerk? Die Sache ist die: Mein BIOS erkennt das Laufwerk (gaaaanz sicher), aber Linux will nie etwas davon gehört haben (sagt nicht mal sowas wie "unknown device"). Außerdem: Ich will hier nur mal testen, wie ich ASM+C gut kombinieren kann. Im späteren OS werd ich dann auch wahrscheinlich Multiboot implentieren. Das Problem liegt ja hier nicht am eigentl. Bootvorgang, sondern am Linker (bzw. Compiler). Wie schon gesagt, will dieser meinen Kernel nicht hinzufügen.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 22. May 2011, 22:09 »
Mir ist gerade aufgefallen, dass wenn ich die Linker-Zeile
    . = 0x7e00;durch
    . = 0x100000;ersetze, ich folgende Fehlermeldung erhalte:
a.out:a.out:(.text+0x9): relocation truncated to fit: 16 against `.text'
a.out:a.out:(.text+0x1e): relocation truncated to fit: 16 against `.text'
b.out:b.out:(.text+0x4): relocation truncated to fit: 16 against `.data'
b.out:b.out:(.text+0xf): relocation truncated to fit: 16 against `.text'

Dass sind jeweils Zeilen mit Speicherzugriffen.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 22. May 2011, 22:56 »
0000000 fde8 e901 fffd 0000 0000 0000 0000 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 0ffa 1601 8098 200f 0cc0 0f01 c022 13ea
0000210 0880 6600 10b8 8e00 8ed8 bcd0 0000 0009
0000220 07e8 0000 e900 fffb ffff 9090 8955 83e5
0000230 18ec 04c7 8a24 0080 e800 0002 0000 c3c9
0000240 8955 83e5 10ec 45c7 00f8 0b80 c700 fc45
0000250 0000 0000 25eb 458b 01fc 03c0 f845 558b
0000260 03fc 0855 b60f 8812 8b10 fc45 c001 c083
0000270 0301 f845 00c6 8307 fc45 8b01 fc45 4503
0000280 0f08 00b6 c084 ce75 c3c9
6548 6c6c 206f
0000290 6f57 6c72 2164 0000 0017 809e 0000 0000
00002a0 0000 0000 0000 ffff 0000 9a00 00cf ffff
00002b0 0000 9200 00cf 9090                    
Das fett markierte ist der Code aus deiner kernel.c. Ist alles da. (Den Kernel in den Bootloader zu linken ist freilich Blödsinn und führt zu nichts funktionierendem, aber das ist ein anderes Problem)

Ach, und GRUB benutzt natürlich nicht den Linuxtreiber, sondern int 13h, also den BIOS-Treiber. Wenn das BIOS mit deinem Floppy zurechtkommt, dann müsste GRUB das auch benutzen können.
« Letzte Änderung: 22. May 2011, 23:02 von taljeth »
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 22. May 2011, 23:56 »
Ich vermute, dass GRUB meinen Code nicht erkennt (ist ja beides auf unixoider Basis).
GRUB ist nicht Linux und nutzt auch nicht den gleichen Code. Wenn dir GRUB nicht gefällt, kannst du auch SYSLINUX benutzen, der kann (mit dem Modul mboot.c32) auch Multibootkernel laden.

Wenn dein Linux das Diskettenlaufwerk nicht erkennt, das BIOS aber schon - wie schreibst du denn dann den Kernel auf die Diskette? Irgendwas klingt da komisch.

Meine Empfehlung wäre übrigens, dass du mehr Arbeit in das Booten von Multibootkerneln steckst und weniger in den eigenen Bootloader. Das erspart dir richtig viel Arbeit und Ärger und ist später, wenn der Kernel mehr kann, wesentlich flexibler - du brauchst nicht für jedes mögliche Bootmedium einen neuen Bootloader.

Gruß,
Svenska

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #16 am: 23. May 2011, 00:43 »
Nen eigenen Bootloader zu schreiben ist wie Svenska schon sagte Mist. Und testen wie Man C und ASM gut verbinden kann, musst du noch früh genug. Spätestens bei den interrupts musst du asm nehmen und bei jedem c kernel müssen so ein paar kleine zeilen asm her, die den stack einrichten. Also asm wird dich auch wenn du in C schreibst nie ganz loslassen.

PNoob

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 23. May 2011, 09:31 »
Hallo,


Also ich hab auch so ein schickes USB-Floppy-Laufwerk und das funktioniert prima. Das BIOS kann davon booten (ich habs mit DOS und MenuetOS ausprobiert) und es wird auch von Windows und Linux anstandslos erkannt und benutzt. Was ich mir als Problem aber vorstellen kann ist wenn das Laufwerk schon beim Booten dran steckt und das BIOS dafür irgendeinen Legacy-Mode aktiviert (damit es eben über Int 13h erreichbar ist) und dann das richtige OS nicht mehr so einfach ran kommt. Normalerweise stecke ich das USB-Floppy-Laufwerk erst an wenn das richtige OS komplett hochgefahren ist (damit da nicht das BIOS seine SMM-Finger im Spiel hat), außer natürlich ich will von der Floppy booten dann stecke ich das Laufwerk schon vor dem Einschalten/Reset an.


Also asm wird dich auch wenn du in C schreibst nie ganz loslassen.
Jetzt mach dem armen Kerl doch nicht noch mehr Angst!
Spätestens wenn echte User-Mode-Programme kommen sollen wird auch eine eigene crt0.asm fällig werden. ;)


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 23. May 2011, 11:23 »
Hallo
P.S.: Ja, ich weiß schon was Real Mode usw ist. (ich les schon seit ca einem Jahr die Artikel hier, es wundert mich, dass ich noch keinen 2.Windows programmiert habe...) Allerdings bin ich halt kein C-Guru (und werde es niemals sein. Assembler ist doch die schönere Sprache ;) )

Als Build-Vorgang mach ich erst den ganzen Make-Kram in Linux -> Reboot auf Windows, Rawwrite anschmeißen -> Reboot, Fleißig [F8] drücken und mein Floppy auswählen
(Ich weiß, dass es kompliziert ist, dass braucht mir keiner zu sagen)

Aber warum kann ich den Kernel nicht extern haben? Vielleicht, wenn ich die 2.Stage in eine eigene Sektion packe?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 23. May 2011, 13:08 »
Hallo,


(Ich weiß, dass es kompliziert ist, dass braucht mir keiner zu sagen)
Das ist nicht nur kompliziert sondern oberumständlich. Ich kann mir einfach nicht vorstellen das es da keinen einfacheren Weg geben soll. Was zeigt den unter Linux lsusb an?

Aber warum kann ich den Kernel nicht extern haben? Vielleicht, wenn ich die 2.Stage in eine eigene Sektion packe?
Wenn Du den Kernel extern, also nicht als Teil des Boot-Loaders, haben willst dann muss Dein Kernel ein Format haben das Dein Boot-Loader versteht. Mit dem was es da alles zu beachten gibt und welche Möglichkeiten da verfügbar sind könnte man die halbe Forumsdatenbank zutexten, deswegen kurz und schmerzlos: Mach den Kernel als ELF und nimm GRUB!


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen