Eigenes OS?
-
Intel's code? No, it is my code
I said it was better :P, I think you miss something with the objdump, because your code didnt actually do everything you wrote in C. (do a check on alignment, A == (A AND 0xfffff000) THEN A AND 0xfffff000, ADD A, 0x1000)
// PHPnerd
-
Intel's code? No, it is my code
Please excuse. You were talking so much about Intel that I thought they have published it in there manual.
I corrected it in the thread (Intel -> PHPnerd).I think you miss something with the objdump
I have checked both dumps, the rest is identical AFAIK: http://www.henkessoft.de/OS_Dev/Downloads/kmalloc_objdump_comparison.zip
Ich habe mal vorsichtig begonnen die Themen:
Eigene Betriebssystementwicklung am PC - Teil 2 - Key Queue und Key Handler - Speicher Management - Physical Memory Management (PMM) - k_malloc( size, align, phys ) - Virtual Memory Management (VMM) - Virtueller Speicher und Swapping - Paging
zu verarbeiten. Code zum Downloaden biete ich noch keinen an, weil ich mit PHPnerd noch über die Funktion set_page(...) diskutiere:
page_t* set_page(ULONG address, page_directory_t* dir) { //ULONG tmp = address; address>>=12; // /= PAGESIZE // index <== address ULONG table_index = (address>>10); // ==> page table containing this address //TODO: check it against Intel Manual 3A !!! if( ( ( (ULONG)( dir->tablesPhysical[table_index] ) ) & 0x1 ) && (dir->tables[table_index]) ) putch('!'); else { putch('?'); /*printformat(" address: %x\n",tmp)*/; ULONG phys; dir->tables[table_index] = (page_table_t*) k_malloc( sizeof(page_table_t), 1, &phys ); k_memset(dir->tables[table_index], 0, PAGESIZE); dir->tablesPhysical[table_index] = phys | 0x7; // 111b meaning: PRESENT=1, RW=1, USER=1 } return &dir->tables[table_index]->pages[address%1024]; }
Da gibt es noch Unklarheiten bei der Abfrage, ob eine page table wirklich schon assigned ist. JM fragt hier mittels if(dir->tables[table_index]) ab, während PHPnerd der Meinung ist, dass dies nicht reicht und man auch das Present Bit abfragen sollte. Ich habe mal das der Physical Tables beid er Abfrage hinzu gefügt, weil das im else-Zweig auf 1 gesetzt wird. Sollte soweit gehen.
Da bin ich noch nicht ganz sicher in diesem Bereich des Pagings, auch was die Visualisierung der Baumstruktur CR3 -> PD mit PDE -> PT mit PTE -> Page mit Offset -> physikalische Adresse und der jeweils zugehörigen Zustands-Bits von PD, PT und Page angeht.
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm
PHPnerd ist der Meinung, JM's Code im VMM-Bereich sei sehr schlecht und empfiehlt eine "Neukonstruktion" auf Basis Intel Manual 3A (da steht aber nichts drinnen, was ich nicht schon wusste, die Sekundärliteratur ist inzwischen ziemlich gut). Ich verwende zunächst mal obige set_page(...), bis ich etwas Besseres habe.
-
Nochmal eine ganz andere Frage an die Assembler-Spezialisten zum Teil 1:
http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId412221
Dort finden sich [Bits 16] und [Bits 32] in einer Übersetzungseinheit (Übergang von RM nach PM). Vielleicht kann man das so verändern, so dass man COFF (32bit, stringentes Format, akzeptiert keinen 16 bit Bereich) anstelle aout (16 und 32 bit gemischt, tolles Format!) verwenden kann? Benötigen wir diesen Eintrag [Bits 16] wirklich? Andere erzählen mir, man sollte den 16/32 Bit Zirkus im Bootloader abwickeln und den Kernel komplett in 32 Bit halten. Ich weiß, dass ich dies versucht habe (man kann kernel.asm ja als verlängerten Bootloader ansehen), aber ein Sprung aus dem Binär-Format nach C-Funktionen war nicht erlaubt.
-
PMM (Bit Array) und VMM (Paging) ist nun im Tutorial umgesetzt:
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId918986
http://www.henkessoft.de/OS_Dev/Downloads/20090503_PrettyOS_104.zipAllerdings bin ich didaktisch noch nicht zufrieden.
EDIT(5.5.): Ich habe nun noch folgende Funktion zur Sichtbarmachung der Paging-Zusammenhänge eingeführt:
void analyze_PD_PT_Pages(ULONG PDE, ULONG PTE, page_directory_t* dir) { k_clear_screen(); ULONG cr3; asm volatile("mov %%cr3, %0": "=r"(cr3)); // read cr3 printformat( "PDBR cr3: %x\n", cr3 ); printformat( "PD phys. addr.: %x\n", dir->physicalAddr ); int i,j; for(i=0;i<PDE;++i) { printformat( " PDE %d: phys: %x virt: %x\n", i, dir->tablesPhysical[i], dir->tables[i] ); for(j=0;j<PTE;++j) { printformat( " PTE %d: addr: %x frame-addr: %x\n", j, &(dir->tables[i]->pages[j]), (ULONG)( dir->tables[i]->pages[j].frame_addr)<<12 ); } } }
in kernel.c:
analyze_PD_PT_Pages(1,10,kernel_directory);
screen output:
PDBR cr3: 00202000h PD phys. addr.: 00202000h PDE 0: phys: 00205027h virt: 00205000h PTE 0: addr: 00205000h frame-addr: 00000000h PTE 1: addr: 00205004h frame-addr: 00001000h PTE 2: addr: 00205008h frame-addr: 00002000h PTE 3: addr: 0020500Ch frame-addr: 00003000h PTE 4: addr: 00205010h frame-addr: 00004000h PTE 5: addr: 00205014h frame-addr: 00005000h PTE 6: addr: 00205018h frame-addr: 00006000h PTE 7: addr: 0020501Ch frame-addr: 00007000h PTE 8: addr: 00205020h frame-addr: 00008000h PTE 9: addr: 00205024h frame-addr: 00009000h
Jetzt versteht man die Zusammenhänge besser und z.B. auch, warum der Bereich von 205000h bis 206000h noch gemappt wurde:
map phys addr to virt addr from 0x0 to the end of used memory:
?old: 00205000h sz: 00001000h a: 1 new: 00206000hNun ist schon besser. Das werde ich noch ins Tut einbauen, vielleicht noch mit den wichtigsten Bits der Pages hinter den Adressen.
-
übrigend funktioniert der source code vom c-kernel bei mir auch nicht.
weder mit make.exe noch mit einer batch
die gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert, die fehlen im zip
-
gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert
Das solltest Du nicht machen, sondern diese nach C:\djgpp installieren. Pfad und Env korrekt setzen wie beschrieben. Nimm genau den DJGPP aus dem im Tutorial angegebenen Link wegen dem aout-Format. Falls dann etwas nicht klappt, bitte erst Fehlermeldung lesen und probieren. Wenn Du nicht mehr weiter kommst, genaue Meldung posten. Hast Du schon mit Assembler und C-Compiler gearbeitet?
-
upps
stimmt ja *boing*
ich probiers demnächst aus
-
Das Thema Heap ist nun ebenfalls im Tutorial grundlegend verarbeitet. Wer sich als Leser tiefer hinein bohren möchte, kann es mit den Links machen, für PrettyOS ist nur die Anwendung wichtig.
-
Ich wollte das Ende des Kernelbereichs bestimmen. Dazu veränderte ich das Linker-Skript wie folgt:
OUTPUT_FORMAT("binary") ENTRY(RealMode) phys = 0x00008000; SECTIONS { .text phys : { *(.text) } .data : { *(.data) } .bss : { *(.bss) } _endkernel = .; }
//os.c extern ULONG* endkernel; ULONG get_end_kernel(){ return *endkernel; } //ckernel.c int main() { k_clear_screen(); printformat("end of kernel %x\n", get_end_kernel()); //... }
liefert:
end of kernel F000FF53h
Nach meinen Berechnungen sollte natürlich ein deutlich niedrigerer Wert heraus kommen. 0x8000 (start) + 0x3540 (Hexeditor MyOS.bin) - 0x0200 (bootloader sector) = 0xB340
Auch wenn ich _endkernel = .; hinter data setze, kommt das Gleiche heraus. Was mache ich verkehrt?
-
so wie ich das sehe, deklarierst du mit
extern ULONG* endkernel;
die Variable als Zeiger; das ist noch korrekt, aber du willst da die Adresse, und nicht den Inhalt an dieser Stelle, also
ULONG get_end_kernel(){ return endkernel; }
(beachte: ohne stern!) somit kannst du den Pointer auch vom Typ void* oder char* machen, da du ja die Daten, die am Ende deines Kernels sind nicht auslesen möchtest, oder vl. schon^^
-
Klar Adresse der Variablen, nicht Inhalt. Habe da beides durchmischt.
So klappt es:extern ULONG endkernel; ULONG get_end_kernel() { return (ULONG) &endkernel; }
und _endkernel vor bss (Block Started by Symbol)
OUTPUT_FORMAT("binary") ENTRY(RealMode) phys = 0x00008000; SECTIONS { .text phys : { *(.text) } .data : { *(.data) } _endkernel = .; .bss : { *(.bss) } }
ergeben:
end of kernel 0000B320h
Das passt gut zu meiner Schätzung.
Hinter dem .bss Block ist der Wert höher:
end of kernel 0000BC00h
bss: http://en.wikipedia.org/wiki/Block_Started_by_Symbol
Was ist das wirkliche Ende des Kernels? B320h (ohne bss) oder BC00h (incl. bss)?
Ich denke incl. bss ist korrekt.
-
bei der ckernel.c, wozu ist der string gut?
der wird doch nirgendwo ausgegeben oder?
bei mir werdeb nur die strings aus dem bootloader ausgegeben.
mfg
lukas
ps:
es wäre cool, wenn du dein tutorial mit einem dateisystem erweitern würdest.
-
bei der ckernel.c, wozu ist der string gut?
Den PrettyOS ...? Ja, den kannst Du streichen, das erfolgt auch in den weiteren Versionen, da man Version und Datum nur an einer Stelle halten sollte.
es wäre cool, wenn du dein Tutorial mit einem Dateisystem erweitern würdest.
Eigenes Dateisystem, VFS, RAM Disk etc. kommt noch. Zur Zeit hänge ich immer noch am Taskswitching fest.
-
gut, ich hab mich schon gewundert.
task switching?
also wie fenster ohne fenster?
-
task switching?
Multitasking, also z.B. Timer-gesteuertes Umschalten zwischen Prozessen mit allem Drum und Dran. Schwieriges Thema, zumindest für mich.
-
Erhard Henkes schrieb:
Multitasking, also z.B. Timer-gesteuertes Umschalten zwischen Prozessen mit allem Drum und Dran. Schwieriges Thema, zumindest für mich.
das grundprinzip ist doch klar, oder? task wird vom timer-interrupt unterbrochen, der speichert alle registerinhalte der task und beim verlassen setzt er registerinhalte für die nächste task. schwierig sind dabei nur irgendwelche abgefahrenen scheduling-algorithmen, aber darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.
-
darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.
Der Mechanismus funktioniert bereits, über mit vier Tasks.
das grundprinzip ist doch klar, oder?
Ja, mir macht die Abstimmung zwischen TSS entry (nur eine), CR3 (Physikalische Adresse der Page Directory des jeweiligen virtuellen Adressraums), Struktur task_t (für jede Task eine) und eigener Kernel_Stack pro Task noch etwas Probleme. Da stecke ich noch etwas fest. Ich möchte den Task Switch prinzipiell auch außerhalb eines Interrupts durchführen können, um ihn zu studieren. Denn im timer_handler macht es schnell bumm, dann ist es schwer zu analysieren, was eigentlich los ist (GPF, OpcodeF, DebugF, Reset, doing nothing, ...). Erst mit vollständigen log-Funktionen - passend zu den Strukturen task_t und tss_entry_t erkennt man, was im Hintergrund abgeht:
void task_log(task_t* t) { printformat("id: %d ", t->id); // Process ID printformat("ebp: %x ",t->ebp); // Base pointer printformat("esp: %x ",t->esp); // Stack pointer printformat("eip: %x ",t->eip); // Instruction pointer printformat("PD: %x ", t->page_directory); // Page directory. printformat("k_stack: %x ",t->kernel_stack); // Kernel stack location. printformat("next: %x\n", t->next); // The next task in a linked list. } void TSS_log(tss_entry_t* tss) { printformat("prev_tss: %x ", tss->prev_tss); printformat("esp0: %x ", tss->esp0); printformat("ss0: %x ", tss->ss0); printformat("esp1: %x ", tss->esp1); printformat("ss1: %x ", tss->ss1); printformat("esp2: %x ", tss->esp2); printformat("ss2: %x ", tss->ss2); printformat("cr3: %x ", tss->cr3); printformat("eip: %x ", tss->eip); printformat("eflags: %x ", tss->eflags); printformat("eax: %x ", tss->eax); printformat("ecx: %x ", tss->ecx); printformat("edx: %x ", tss->edx); printformat("ebx: %x ", tss->ebx); printformat("esp: %x ", tss->esp); printformat("ebp: %x ", tss->ebp); printformat("esi: %x ", tss->esi); printformat("edi: %x ", tss->edi); printformat("es: %x ", tss->es); printformat("cs: %x ", tss->cs); printformat("ss: %x ", tss->ss); printformat("ds: %x ", tss->ds); printformat("fs: %x ", tss->fs); printformat("gs: %x ", tss->gs); printformat("ldt: %x ", tss->ldt); printformat("trap: %x ", tss->trap); //only 0 or 1 printformat("iomap_base: %x ", tss->iomap_base); }
Siehe Intel Manual 3A, "Figure 6-2. 32-Bit Task-State Segment (TSS)"
Die Struktur task_t muss sicher noch erweitert werden. ich möchte diese für das Tutorial aber auch nicht überfrachten, damit man den Überblick nicht verliert.
-
Da es im timer_handler mit einer exception ausgestoppt wird, könnte das Problem im asm stub liegen. Ich verwende dort folgenden Code:
..
Der task_switch erfolgt im timer_handler...
Was ist die beste Lösung und warum?
..
-
Ein wesentlicher Punkt, auf den Du unbedingt achten mußt:
Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch und alles geht daneben. Bei dem 16-bit-taskswitching, was ich Dir in der Mail geschickt habe, war das ein entscheidender Faktor.
Da ist übrigens auch eine Funktion mit drin, die einen Taskswitch erzwingt.
Hab mich aber nie getraut, das alles auf 32-bit zu erweitern.
-
Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch
Danke für den Hinweis! Ich werde da noch ein ts-flag in meiner ODA (OS Data Area) Struktur einbauen, das zuvor abgefragt wird. Die nutze ich sowieso viel zu wenig.
typedef struct oda { //... //tasking UCHAR ts_flag; // 0: taskswitch off 1: taskswitch on }oda_t; //... pODA->ts_flag = 1; //... if( pODA->ts_flag == 1 ) task_switch();