Eigenes OS?
-
Roll-back starts now:
Tutorial: http://www.henkessoft.de/OS_Dev/OS_Dev1.htm
Source Code: ..Zunächst mal einige Fragen an euch zu den Tools (nasm/gcc/ld):
- Im Tutorial werden gcc 3.1 und ld 2.13 (im Rahmen von DJGPP, Download-Link von osdever.net empfohlen: http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe ) verwendet.
Einer der Gründe besteht darin, dass wir damit das aout-Format (Linux a.out object file) linken konnten. abc.w konnte dieses Problem, soweit ich es verstanden habe, auch nicht lösen, sodass wir dabei blieben. Mit dem gcc 3.1 kommt man allerdings immer suspekt daher. Ein Fehler könnte ja am Uralt-Compiler liegen.
Mögliche NASM output-Formate sind:
bin, aout, aoutb, coff, elf32, elf, elf64, as86, obj ...Verwendet man für die NASM outputs COFF bzw. WIN32, dann gibt es folgende nette Meldung:
kernel.asm: ... : error: COFF format does not support non-32-bit relocations
Soweit ich das verstanden habe, hängt dies an unserem eleganten Übergang von 16 Bit nach 32 Bit. Hier erhält man von "Fachleuten" den Rat, GRUB einzusetzen und sich auf den C-Kernel zu konzentrieren. Das widerspricht aber dem Geist des bisherigen Tutorials, das die Dinge bottom-up zeigen will. Ich habe außer diesem aout-Format bisher kein Format gefunden, dass sowohl 16- als auch 32-Bit verarbeitet und von ld zum restlichen C-Code gebunden wird. Gibt es ein modernes Format, das dies kann?Ich bin nicht sicher, inweiweit dies ein echtes Problem oder nur Ansichtssache ist. GCC 3.1 stammt ja immerhin schon von May 15, 2002. Also so richtig uralt ist er nicht.
Warum wurde z.B. pushfd / popfd im asm-Code nicht akzeptiert? Das kann doch nicht am gcc-Baudatum 2002 liegen, den 386er asm-Set gibt es seit den 80ern.
- zur AT&T-Syntax: %eax %%eax *%%eax
wo muss man genau was verwenden?
- Im Tutorial werden gcc 3.1 und ld 2.13 (im Rahmen von DJGPP, Download-Link von osdever.net empfohlen: http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe ) verwendet.
-
Erhard Henkes schrieb:
Auf jeden Fall hat mir die Beschäftigung mit den Themen viel gebracht, bin nun auch oft genug gegen den Baum gefahren, um zu wissen, was etwas taugt und was einem bei Problemen nur noch den Rest gibt.
meiner meinung nach hast du zu viele themen gleichzeitig bearbeitet. das liegt wohl auch daran, dass es keine richtige trennung zwischen den komponenten des 'referenzsystems' von JM gibt (vermutlich ist er ein opfer der verfrickelten x86-technologie geworden, die einem instabile software-konstrukte aufzwingt). dabei kann man virtual memory, multitasking, heaps und exceptions ziemlich unabhängig voneinander betrachten. aber egal, nur nicht aufgeben...
-
Den Mut habe ich keinesfalls verloren, ganz im Gegenteil! Man kann ein solches Tutorial keinesfalls schreiben, wenn man schon 20 verschiedene OS entwickelt hat, weil man sich dann für garnix mehr entscheiden kann und alles nur noch theoretisch/abstrakt sieht, aber auch nicht aus dem Nichts. Inzwischen verstehe ich, warum JM nicht weiter macht.
dabei kann man virtual memory, multitasking, heaps und exceptions ziemlich unabhängig voneinander betrachten
Ja, das stimmt. Es gibt daneben noch weitere Punkte, nämlich die Trennung des physikalischen und virtuellen MM, die Software-Interrupts, die Syscalls und die Ausführung von Programmen. Das VFS und die RAM-Disk gefallen mir gut von JM, wobei sein VFS noch nicht ausgefeilt ist, aber das kann man ja noch machen. Heap-Mechanismus funktioniert auch gut. Vielleicht liefert uns PHPnerd etwas Besseres, aber sein Ansatz klingt für mich gleich zu JM, aber ich bin kein Informatiker.
-
Ich habe mit Teil 2 begonnen und die Themen FIFO circular Key Queue und "lean" key handler verarbeitet, um den Anschluss an das Tutorial zu bekommen.
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm (Tut)
http://www.henkessoft.de/OS_Dev/Downloads/20090501_PrettyOS_101.zipWas mir wirklich Sorgen macht, ist, dass ich mit DJGPP arbeite, gilt als obsolet. leider haben wir 16-bit im Gepäck. Vielleicht kann man das auf 32-Bit abändern und auf COFF umsteigen.
-
Wird es nicht mehr weiter entwickelt oder was?????
Ganz im Gegenteil, jetzt geht es erst richtig los, allerdings geordnet, so robust wie möglich und transparent, damit eigenes Experimentieren gelingt. Einfach dabei bleiben, oder noch besser, zusammen mit mir entwickeln. Ich werde nicht aufgeben. Das Thema OSDEV ist faszinierend. Ich werde ab jetzt aber jede einzelne Code-Zeile - egal ob fremd oder von mir selbst - auf die Goldwaage legen. Anders geht es nicht.
Es gibt bezüglich Teil 2 folgende technische Fragestellungen:
- GRUB oder Tiny Bootloader?
- DJGPP oder neuere gcc-ld-binutils-Toolchain
-
Let's all take the Intel Manuals, close the jamesMolloy website, and start by ourself. We just use GRUB to load, so we take the GRUB specifications too.
We read the GRUB specifications, and we find out how to make our starting OS multiboot able for GRUB. When done, we move to C, by just calling our C function.
Read Intel manuals, on IDT, GDT, ISR and IRQ's (exceptions), work out some code.
We already know howto use VGA in protected mode: writing to 0xb8000, write some nice code to make our life easier.
Then, go on to intel manuals again, and go starting writing your PMM and VMM. (Volume 3A, Protected Mode Memory Management)
Then, when all is working, do some research on internet about different algorithms of memory allocation (heap management). Write your own, without looking to others code.
Debug debug debug all you got. Try to get your friends starting it and doing weird things.
When done, Take Intel manuals and find the chapters about task management, but especially (when you want to use Software Multitasking) the chapter about the architecture itself. I think it is chapter 3 of Volume 1, not sure
Then go on writing some code to change the registers of the CPU. Just try to switch static tasks, then try make dynamic ones (so a task can start a new task).
Go on to write code for device drivers and file systems, write some drivers, and
support a nice filesystem (FAT32 for example). Then support executables, port newlib, compile the whole GCC packet. Run it on your OS, compile NASM. Write some simple nice text editor. Do not forget to make a command line interface for executing your commands.Now you can make your OS on your OS. And you can take a drink.
When someone got so far, tip me, and i will tell you next steps (LOL, I will be back after some years and check the forum (just joking, will still be here)).
Success you all! When you want some details (tips), ask for them.
// PHPnerd
-
Thank you for the guideline showing the path forward to OS excellence.
-
PHPnerd schrieb:
Then, go on to intel manuals again...
...but don't rely on too much intel crap. only the very low level stuff (which must be coded in asm, anyway) should be hooked on x86. separate cpu specific code from the rest of the system, so you can...
1. easily port your OS to another architecture
2. run and debug parts of the OS inside a test bench (perhaps under windoze)
-
Erhard Henkes schrieb:
- zur AT&T-Syntax: %eax %%eax *%%eax
wo muss man genau was verwenden?
Man muss unterscheiden zwischen Assembler-Syntax für den GNU Assembler und dem Assembler-Inline Formalismus für den gcc Compiler.
Für den GNU Assembler gilt: Alle Register-Bezeichnungen, weil sie was besonderes sind, im Sinne, keine Variablen uns so was, bekommen ein % Zeichen. Man kann ruhig Variablen anlegen, die eax, ebx und sonst wie heissen, und auf diese Variablen zugreifen:
.section .data eax: .long 0 # Variable eax ebx: .long 0 # Variable ebx ecx: .long 0, 0, 0 # Ein Array ecx[3] .section .text _Helper: movl %eax, eax # Speichere Inhalt des CPU-Registers EAX (%eax) in der Variable eax (eax) movl %ebx, ebx # Speichere Inhalt des CPU-Registers EBX (%ebx) in der Variable ebx (ebx) movl $eax, %eax # Lade Addresse der Variable eax ($eax) ins CPU-Register EAX (%eax) movl $ecx, %ecx # Lade Anfangsadresse des Arrays ecx ($ecx) ins CPU-Register ECX (%ecx) movl %eax, (%ecx) # Speichere Inhalt des CPU-Registers EAX (%eax) im Array-Element ecx[0] movl %ebx, 4(%ecx) # Speichere Inhalt des CPU-Registers EBX (%ebx) im Array-Element ecx[1] movl %ecx, 8(%ecx) # Speichere Inhalt des CPU-Registers ECX (%ecx) im Array-Element ecx[2] ret
Bei indirekten Sprüngen ist es so, dass ein Sternchen benützt werden muss/soll:
_Sprung: jmpl *1000 # Effektive Adresse 1000 jmpl *%eax # Effektive Adresse im CPU-Register EAX (%eax) jmpl *4(%eax) # Effektive Adresse im CPU-Register EAX (%eax) und Displacement = 4 jmpl *(%eax, %ebx) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX (%ebx) jmpl *32(%eax, %ebx, 4) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX, Skalierung = 4, Displacement = 32 ret
Vergisst man ein Sternchen zu setzen, kommt vom gas eine Warnung:
... Warning: indirect jmp without `*'
Warum das so ist, weiss ich ehrlich gesagt nicht, habe im Manual dazu nichts gefunden und habe ehrlich gesagt, nicht intensiv danach gesucht.
Bezüglich Assembler-Inline im C-Code kann ich leider nicht viel sagen. Ich weiss nur, dass man bei Registern zwei % Zeichen setzen muss. Ansonsten lässt sich, denke ich, obige Info auch so übernehmen. Und es gibt noch eine weitere Schar von Regeln für den Inline, die man kennen sollte...
Ich hoffe, ich konnte bisschen Licht auf die AT&T Syntax werfen (die ja zu unrecht verteufelt wird)...
- zur AT&T-Syntax: %eax %%eax *%%eax
-
die ja zu unrecht verteufelt wird
Ich fange an, mich daran zu gewöhnen. Die C-Syntax ist ja auch nicht jedes Coders Lieblingsspeise.
Momentan kämpfe ich gerade mit dem Thema Paging und versuche die Dinge so weit wie möglich zu visualisieren.
Dabei habe ich eine Frage zum Alignment: Wenn die memory_placement_address nach einem k_malloc bei exakt 200000h liegt bei PAGESIZE = 1000h und man belegt nun weitere 2000h, wo muss es dann weiter gehen, bei 202000h oder 203000h? Der Algo, den ich von JM übernommen habe, macht momentan bei 203000h weiter, kommt mir verschwenderisch vor. Aber vielleicht habe ich da etwas noch nicht ganz verstanden.
if( align == 1 && (placement_address & 0xFFFFF000) ) { placement_address &= 0xFFFFF000; placement_address += PAGESIZE; }
Wenn ( placement_address == (placement_address & 0xFFFFF000) ) könnte man das Addieren der PAGESIZE doch sparen. Aufrunden muss man doch nur bei placement_address > (placement_address & 0xFFFFF000) ?
Beispiel:
placement_address startet mit 200000h. Wegen align=0 startet es auch genau dort. Bei align=1 hätte es bei 201000h begonnen. Ist das korrekt oder muss ich obige Abfrage auf die letzten 3 Hex-Nuller ergänzen?setup bitset:
old: 00200000h sz: 00002000h a: 0 new: 00202000hmake a page directory:
old: 00203000h sz: 00002004h a: 1 new: 00205004hDaher halte ich diese verfeinerte Version für richtig:
if( align == 1 /*&& (placement_address & 0xFFFFF000)*/ ) { if( !(placement_address == (placement_address & 0xFFFFF000) ) ) // New !!! { placement_address &= 0xFFFFF000; placement_address += PAGESIZE; } }
Please comment.
Das && (placement_address & 0xFFFFF000) von JM verstehe ich auch nicht. Das kann man doch genau so gut weg lassen?
-
The Intel manuals tells everything about the x86(-64) architecture, including paging. Why would you do that stuff in ASM? But that's you own choice, what I posted is how I would go work.
use:
if(aligned == 1 && (placement_address & 0x00000fff)) { placement_address &= 0xfffff000; placement_address += 0x1000; }
Open the WIndows calulcator, science mode, and do something in it like this. Just putting in some number, anding with 0x00000fff or 0xfffff000.
Something everybody here should know: each part of a hexidecimal number (0x12345678, 1 is a part, 2 a part, etc.) is 4 bits in length. so 0xfffff000 makes 12 bits zero
Success
// PHPnerd
-
Ah, that's o.k. It rounds up to next 1000h, if it is not xxxxx000h. Good.
-
No, if address is xxxxx000h, it wont do anything. If it is not, it will make it so. And add a page, so no data will be overwritten.
Calculate it
// PHPnerd
-
Please excuse, at the first view I thought it's the same as JM's code. But it's the opposite. Follows the same logic I proposed. Right?
-
OK, let's compare the three codes:
PHPnerd:
if(aligned == 1 && (placement_address & 0x00000fff)) { placement_address &= 0xfffff000; placement_address += PAGESIZE; }
My proposal:
if( align == 1 ) { if( !(placement_address == (placement_address & 0xFFFFF000) ) ) { placement_address &= 0xFFFFF000; placement_address += PAGESIZE; } }
JM's code wasting memory - very bad!
if( align == 1 && (placement_address & 0xFFFFF000) ) { placement_address &= 0xFFFFF000; placement_address += PAGESIZE; }
I have to admit that your code is elegant, but my code might be the best to understand.
-
Yeah, results the same, but my way will be lots faster. (some cycles, so some billions of a second). Mine is better to understand, i dont understand yours
// PHPnerd
-
OK, let's do it our own way! I will use my code.
I have compared the objdump of both kmalloc (w/o optimization).
There is only a tiny difference in these asm lines://PHPnerd’s Code: 31: a1 90 00 00 00 mov 0x90,%eax 36: a9 ff 0f 00 00 test $0xfff,%eax 3b: 74 0f je 4c <_k_malloc+0x2e> 3d: 25 00 f0 ff ff and $0xfffff000,%eax //My Code: 31: a1 90 00 00 00 mov 0x90,%eax 36: 25 00 f0 ff ff and $0xfffff000,%eax 3b: 39 05 90 00 00 00 cmp %eax,0x90 41: 74 0a je 4d <_k_malloc+0x2f>
I need 18 and PHPnerd 17 bytes. Your code is really better?
I think that's a good example for the tutorial: How to compare your own code with other solutions?
Well, the topic "alignment" now is clarified.
-
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.