Lowlevel
Lowlevel => OS-Design => Thema gestartet von: woigl am 02. August 2005, 20:42
-
Ich bin neu hier und hoffe das mir jemand von euch weiterhelfen kann, da dieses Forum kompetent aussieht.
Kann mir jemand weiterhelfen bei meiner folgenden Frage:
Ich will gerne ein OS programmieren und dann dynamisch die Treiber laden - wie ist hier das vorgehen? hat jemand Beispiele? wie wird dies bei Linux geloest?
Ich will einfach vermeiden das Grafiktreiber, Soundtreiber usw. im Kernel implementiert sind.
-
kommt drauf an wie DUs halt machen willst. du könntest die treiber per grub laden lassen und in den kernelspace mappen. oder du machst es so wie ich, indem du jeden treiber als eigenen prozess laufen lässt und dann alles auf IPC setzt. es kommt halt ganz auf DEINE ansprüche an. außerdem musst du die erstmal ein treiberformat überlegen
-
Sorry - was ist IPC?... Wahrscheinlich kenn ich's, aber die Abkürzung ist mir gerade nicht geläufig.
-
inter-processing-communication
-
Sorry - was ist IPC?... Wahrscheinlich kenn ich's, aber die Abkürzung ist mir gerade nicht geläufig.
ich glaub eher er meinte den PIC, programmable interrupt controller ;)
-
nun noch mal alle:
pic = programmable interrupt controller
pci = peripheral controll interface
ipc = inter-processing-communication
pit = programmabel interval timer
noch was :wink:
-
Fast richtig ;-) IPC = Inter-Process-Communication, dann macht der Begriff nämlich sogar Sinn :)
So jetzt zum Thema:
Ich weiß nicht wie weit du bist, aber zuerst solltest du dir genaue Gedanken machen, wie dein OS aufgebaut sein soll, was der Kernel so alles wie macht und wie dann Anwendungen und Treiber miteinander kooperieren sollen. Dazu kann ich nur, wenn du Zugang zu einer Bibliothek hast, Andrew S. Tanenbaum Werk "Moderne Betriebssystem" in der 2. Auflage Kapitel 12 "Entwurf von Betriebssystemen" und wenn du des Englischen mächtig bist das "design considerations" Kapitel im OS-FAQ Wiki (http://www.mega-tokyo.com/osfaq2/index.php) empfehlen.
-
schon besser ;) aber wenn IPC ein englischer begriff sein soll, wirds kleingeschrieben und es kommen nicht ganz so viele bindestriche rein (inter-process communication) :)
-
Also um mal zu erklaeren wie mein Aufbau aussehen soll:
Ich moechte einen Kernel haben der die Schaltzentrale zwischen Programmen und Treiber ist.
Also wenn ein Programm grafische Ausgaben macht, dann soll es an den Kernel gehen und der leitet es an den jeweiligen Grafiktreiber weiter welcher dann an die Hardware uebergibt.
Ich habe das ganze Buch von Tannenbaum gelesen und habe schon so meine Vorstellungen.
Ebenfalls sollte ich noch erwaehnen das ich den Kernel in C schreiben moechte.
Bitte um weitere Infos...
-
Du musst das Modul erstmal laden. Du kannst Module mit GRUB laden lassen oder sie mit einem eigenen Treiber von einer Festplatte oder was auch immer lesen. Falls du nicht schon beim kompilieren der Module weißt, an welche Addresse sie später geladen werden, musst du die Module dynamisch linken/relocaten. Das ist zum Beispiel bei ELF sehr einfach zu realisieren. Je nach Design des OS kannst du die Treiber mit eigenem Addressspace versehen oder sie in alle Prozesse mappen. Der Kernel kann dann direkt über Jumps oder Taskswitches in den Treiber springen.
-
Du musst das Modul erstmal laden. Du kannst Module mit GRUB laden lassen oder sie mit einem eigenen Treiber von einer Festplatte oder was auch immer lesen. Falls du nicht schon beim kompilieren der Module weißt, an welche Addresse sie später geladen werden, musst du die Module dynamisch linken/relocaten. Das ist zum Beispiel bei ELF sehr einfach zu realisieren. Je nach Design des OS kannst du die Treiber mit eigenem Addressspace versehen oder sie in alle Prozesse mappen. Der Kernel kann dann direkt über Jumps oder Taskswitches in den Treiber springen.
Ja klingt plausibel, aber hat da jemand ein Beispiel... mir fehlt da einiges an Wissen diesbezueglich.
-
grub oder relocations?
-
Ein Beispiel für ELF Relocation kann ich dir geben:
Es ist noch zu beachten, das man damit den Kernel sehr leicht mit korrupten ELF Files manipulieren kann, da mein Code weitgehend nicht überprüft ob die Werte der ELF File gültig sind. Falls du den Code in deinem OS benutzt solltest du also überprüfen ob die Pointer auf gültige Stellen in der ELF File zeigen.
Es sind ausserdem grundlegende libc Funktionen nötig um den Code auszuführen. ( strcmp(), memcpy() usw. )
Ausserdem sind vieleicht meine Structs nicht so sehr leicht zu verstehen, da die Namen nicht alle selbstdokumentierend sind ^^;; und der Code ist nicht sehr gut dokumentiert. :(
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef long s32;
typedef short s16;
#define ELF386_SEC_SYMTABLE 2
#define ELF386_R_32 1
#define ELF386_R_PC32 2
#define ELF386_R_SYMBOL(z) ( (z) >> 8 )
#define ELF386_R_TYPE(z) ( (unsigned char)(z) )
#define ELF386_ST_BINDING(z) ( (z) >> 4 )
#define ELF386_ST_TYPE(z) ( (z) & 0xF )
#define ELF386_MAGINDEX_0 0
#define ELF386_MAGINDEX_1 1
#define ELF386_MAGINDEX_2 2
#define ELF386_MAGINDEX_3 3
#define ELF386_MAG_0 0x7F
#define ELF386_MAG_1 'E'
#define ELF386_MAG_2 'L'
#define ELF386_MAG_3 'F'
typedef struct {
u8 elfMagic[16];
u16 elfType;
u16 elfMachine;
u32 elfVersion;
u32 elfEntry;
u32 elfProgramOffset;
u32 elfSectionOffset;
u32 elfFlags;
u16 elfHeaderSize;
u16 elfProgramSize;
u16 elfProgramCount;
u16 elfSectionSize;
u16 elfSectionCount;
u16 elfStringSection;
} __attribute__ ((packed)) elf386Header;
typedef struct {
u32 sectionName;
u32 sectionType;
u32 sectionFlags;
u32 sectionAddress;
u32 sectionOffset;
u32 sectionSize;
u32 sectionLink;
u32 sectionInfo;
u32 sectionAlign;
u32 sectionEntrySize;
} __attribute__ ((packed)) elf386Section;
typedef struct {
u32 symbolName;
u32 symbolValue;
u32 symbolSize;
u8 symbolInfo;
u8 symbolOther;
u16 symbolSection;
} __attribute__ ((packed)) elf386Symbol;
typedef struct {
u32 relocOffset;
u32 relocInfo;
s32 relocAdded;
} __attribute__ ((packed)) elf386Relocation;
typedef struct {
u32 programType;
u32 programOffset;
u32 programVirtualAddress;
u32 programPhysicalAddress;
u32 programFileSize;
u32 programSize;
u32 programFlags;
u32 programAlign;
} __attribute__ ((packed)) elf386ProgramHeader;
void *elf386LoadModule( char *image, unsigned long size );
int elf386Relocate( char *image );
int elf386DoRelocation( char *image, elf386Section *section, elf386Relocation *relocation );
unsigned long elf386GetSymbol( char *image, unsigned long symbolTable, unsigned long symbolID );
void *elf386GetSymbolOffset( char *image, char *name );
// in memory.h steht die Funktion allocateSystemMemory(), die benutzt wird um die Module zu laden.
#include "../memory.h"
#include "string.h"
void *elf386LoadModule( char* image, unsigned long size ) {
elf386Header *header = (elf386Header *)image;
elf386Section *section;
if ((header->elfMagic[ELF386_MAGINDEX_0] != ELF386_MAG_0) ||
(header->elfMagic[ELF386_MAGINDEX_1] != ELF386_MAG_1) ||
(header->elfMagic[ELF386_MAGINDEX_2] != ELF386_MAG_2) ||
(header->elfMagic[ELF386_MAGINDEX_3] != ELF386_MAG_3)) {
return -1;
}
unsigned long bssSize;
// loop trough the sections and look for the bss section
unsigned long i;
for( i = 0; i < header->elfSectionCount; i++ ) {
section = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * i );
if( section->sectionType != 8 )
continue;
bssSize = section->sectionSize;
break;
}
// allocate memory for the module
void *module = (void *)allocateSystemMemory( size + bssSize );
// copy the image into memory
memcpy( module, image, size );
// zero the bss section
memset( module + size, 0, bssSize );
// setup the bss section
section->sectionOffset = (u32)module + size;
return module;
}
int elf386Relocate( char *image ) {
unsigned long i;
unsigned long j;
elf386Section *section;
elf386Header *header = (elf386Header *)image;
elf386Relocation *relocation;
if ((header->elfMagic[ELF386_MAGINDEX_0] != ELF386_MAG_0) ||
(header->elfMagic[ELF386_MAGINDEX_1] != ELF386_MAG_1) ||
(header->elfMagic[ELF386_MAGINDEX_2] != ELF386_MAG_2) ||
(header->elfMagic[ELF386_MAGINDEX_3] != ELF386_MAG_3)) {
return -1;
}
unsigned long relocSize;
// now we loop trough the sections and look for relocation infos
for( i = 0; i < header->elfSectionCount; i++ ) {
section = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * i );
if( section->sectionType == 4 ) {
relocSize = 12;
}else if( section->sectionType == 9 ) {
relocSize = 8;
}else{
continue;
}
// the section contains relocation infos
for( j = 0; j < section->sectionSize / relocSize; j++ ) {
relocation = (elf386Relocation *)( image + section->sectionOffset + relocSize * j );
// do the relocation
elf386DoRelocation( image, section, relocation );
}
}
}
int elf386DoRelocation( char *image, elf386Section *section, elf386Relocation *relocation ) {
elf386Header *header = (elf386Header *)image;
elf386Section *targetSection = (elf386Section *)(image + header->elfSectionOffset + header->elfSectionSize * section->sectionInfo);
unsigned long *relocationOffset = (unsigned long *)( image + targetSection->sectionOffset + relocation->relocOffset );
unsigned long symbol = elf386GetSymbol( image, section->sectionLink, ELF386_R_SYMBOL( relocation->relocInfo ) );
switch( ELF386_R_TYPE( relocation->relocInfo ) ) {
case ELF386_R_32 :
*relocationOffset = *relocationOffset + symbol;
break;
case ELF386_R_PC32 :
*relocationOffset = *relocationOffset + symbol - (unsigned long)relocationOffset;
break;
}
}
unsigned long elf386GetSymbol( char *image, unsigned long symbolTable, unsigned long symbolID ) {
elf386Header *header = (elf386Header *)image;
elf386Section *table = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * symbolTable );
elf386Symbol *symbol = (elf386Symbol *)( image + table->sectionOffset ) + symbolID;
elf386Section *section;
if( symbol->symbolSection == 0 ) {
// external symbol
section = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * table->sectionLink );
return lookupExternalSymbols( (char *)( image + section->sectionOffset + symbol->symbolName ) );
}else{
// internal symbol
section = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * symbol->symbolSection );
return (unsigned long)( image + section->sectionOffset + symbol->symbolValue );
}
}
void *elf386GetSymbolOffset( char *image, char *name ) {
elf386Header *header = (elf386Header *)image;
elf386Section *section;
elf386Section *strings;
elf386Symbol *symbol;
elf386Section *symbolSection;
if ((header->elfMagic[ELF386_MAGINDEX_0] != ELF386_MAG_0) ||
(header->elfMagic[ELF386_MAGINDEX_1] != ELF386_MAG_1) ||
(header->elfMagic[ELF386_MAGINDEX_2] != ELF386_MAG_2) ||
(header->elfMagic[ELF386_MAGINDEX_3] != ELF386_MAG_3)) {
return 0;
}
// loop trough the sections and look for the text section
unsigned long i;
unsigned long j;
for( i = 0; i < header->elfSectionCount; i++ ) {
section = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * i );
if( section->sectionType == ELF386_SEC_SYMTABLE ) {
strings = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * section->sectionLink );
for( j = 0; j < section->sectionSize / sizeof( elf386Symbol ); j++ ) {
symbol = (elf386Symbol *)( image + section->sectionOffset ) + j;
if( strcmp( image + strings->sectionOffset + symbol->symbolName, name ) == 0 ) {
symbolSection = (elf386Section *)( image + header->elfSectionOffset + header->elfSectionSize * symbol->symbolSection );
return image + symbolSection->sectionOffset + symbol->symbolValue;
}
}
}
}
return 0;
}
Die Funktion allocateSystemMemory() allocated Speicher und mappet ihn in alle Prozesse.
elf386LoadModule() läd ein Modul, das dann noch mit elf386Relocate() relocated werden muss. Die Funktionen elf386DoRelocation() und elf386GetSymbol() sind Hilffunktionen für elf386Relocate. elf386DoRelocation() führt das eigentliche relocaten durch, ersetzt also die Offsets innerhalb der ELF File durch die richtigen Werte. elf386GetSymbol() liefert das Offset eines Symbols, das für den Relocation Vorgang benötigt wird.
elf386GetSymbolOffset() wird benutzt, um z.b. das Offset der main() Funktion zu erhalten.
lookupExternalSymbols( char *name ) kann etwa so aussehen:
void *lookupExternalSymbols( char *name ) {
if( strcmp( name, "printf" ) == 0 ) {
return (void *)printf;
}
}
Ein Beispielcode wie du den Code oben benutzen kannst findest du hier.
Dieser Code führt ein von GRUB geladenes Modul aus.
Die Variable module zeigt auf ein Modul der multibootinfo Struktur.
argc und argv sind Variablen, in denen die Parameter des Modules stehen.
void *kmod = elf386LoadModule( module->mod_start, module->mod_end - module->mod_start );
elf386Relocate( kmod );
void (*entry)( int, char ** ) = elf386GetSymbolOffset( kmod, "main" );
entry( argc, argv );
Das sollte dann die main Funktion mit den Argumenten argc und argv ausführen.
Zu beachten ist noch, das unter Windows standardmässig den Funktionen ein _ vorgestellt wird, du solltest unter Windows also void (*entry)( int, char ** ) = elf386GetSymbolOffset( kmod, "_main" ); benutzen.
-
wenn dir dass noch nicht reicht, hätt ich auch noch PE?
@SSJ7Gohan: eigentlich ist der code recht gut zu verstehen :)
-
Hey, vielen Dank, der Code ist toll...
werd mich mal an die Arbeit machen und mich gegebenenfalls nochmals melden!
Vorab brauch ich jetzt mal das Filesystem und dann gehts an die Treiber.
-
Achso, du musst die Module mit den Flags -r -fno-common Linken, damit sie zu einer relocateable gelinkt werden. -fno-common bringt, das Variablen in der BSS Sektion erstellt werden. Ohne den Flag werden sie als "Common" deklariert, in der BSS Sektion wird jedoch kein Speicher reserviert. Da mein Relocation Code Common Variablen aber noch nicht richtig behandelt, funktionieren Variablen in der BSS Sektion nur wenn du -fno-common aktiviert hast.
-
Achso, du musst die Module mit dem Flag -r Linken, damit sie zu einer relocateable gelinkt werden.
Danke, das wäre gleich ein Vorschlag zum erwitern des Tutorials Kernel in C - vielleichte Module und sonstige Dinge beschreiben?
Danke fuer den Tip zum Linken. :idea: