PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Shell



APK
02.02.2005, 15:29
Ich habe mich heute mal hingesetzt und eine SEHR einfache shell erstellt.
Bisher kennt sie nur die funktionen "clear", diese löscht den Bildschirm und "exit" damit wird sie beendet und die loop startet. Mal sehen was ihr davon haltet, sagt mir was ihr davon haltete, wenns akzeptabel ist werde ich daran weiterarbeiten.

Ich habe die init.c geändert, die selbsterstellten funktionen stehen alle in der datei func.c .
Einige Stringfunktionen habe ich auch zwangsläufig geschrieben da ich sie nicht in den bestehenden Dateien gefunden habe.
Sind wahrscheinlich noch sehr optmierbar aber sonst sicher ganz brauchbar

mfg
APK

Im Anhang ist die Veränderte version.

Hansinator
03.02.2005, 14:29
Ich dachte übrigens an eine midgard kernel shell.
Eine in den kernel einkompilierte shell, um die wichtigsten funktionen des kernels manuell zu steuern.
Interrupt-setup, -auslösung, -sperrung/freigabe oder scheduling und speicheralloziierung.
Gross muss die ja nicht sein, und nur auf wunsch einkompiliert werden.

lizer
03.02.2005, 14:31
Zum testen wäre sowas ganz brauchbar, stimmt; aber später ist das doch ziemlich nutzlos.

Hansinator
03.02.2005, 14:38
Eben nicht, weil das ding, wenn es eh mit kernelpriorität läuft, einige funktionen vom alten dos-debug implementieren könnte.
Sowas wolltest du doch oder lizer?

Smartie
03.02.2005, 14:39
Nutzlos? Also, ich hatte schon immer den Wunsch, direkt im speicher irgendwelcher Programme rumfuhrwerken zu können *G*

lizer
03.02.2005, 14:46
Nutzlos? Also, ich hatte schon immer den Wunsch, direkt im speicher irgendwelcher Programme rumfuhrwerken zu können *G*Zu dem Zeitpunkt, in dem die 'Kernel-Shell' läuft (wohl gemerkt: in den Kernel kompiliert!), wirst du noch keine Programme starten können :)
Des weiteren kann man solche Funktionen wie oben beschrieben auch in eine herkömmliche nicht-kernel Shell :) einbauen und diese mit Kernel-Prioritäten starten bzw. als Modul laden.

Hansinator
03.02.2005, 14:51
Nagut, die kernel shell muss nicht im kernel sein, aber als bestandteil vom kernel laufen..
Genau deknos, das will ich auch mal, daher rührt die idee hauptsächlich :)

Die shell sollte mit irgendner tastenkombi aufgerufen werden können, so wie diese dings-ice debug sache, kennt das jemand?
Sodass der scheduler wärend die shell an is, nichmehr scheduled sondern nurnoch die shell ausführt bis zum verlassen.

Wenn die shell später nachgeladen wird, hat man nur den vorteil, dass andere programme schutzmassnahmen ergreifen können, um zu verhindern das das ding mit allen rechten in den kernel gelangt.
Sowas haben spielehersteller für diesen dings-ice (glaub blackice heisst das ding) debugger auch in ihre progs eingebaut, dass die dann garnicht erst starten (wegen dem kopierschutz).

APK
03.02.2005, 17:37
Eine Kernelshell hört sich seltsam an, vor allem da anscheinend keiner so genau weiß welche funktionen die dann haben muss ;)
Ich habe das mit der Shell mal begonnen damit mal etwas da ist mit dem man weiterarbeiten kann.
Ich werde mal in richtung Kernel-shell versuchen was zu machen aber weiß nicht wieweit ich kommen werde, hört sich irgendwie nach etwas an an dem man ewig arbeiten könnte.
Das mit KernelVariablen wäre sicher realisierbar, auch wenn es sicher nicht so einfach ist.

Als anfang, welche Funktionen müsste so eine Shell den beherschen ? Kernelvariablen, was sonst noch ?
Ich werde mich jetzt mal genauer mit den Funktionen von Midgard vertraut machen, bis jetzt waren für mich hauptsächlich die Eingabe-Ausgabe funktionen Interessant.

Also ich bin für Vorschläge zu Funktionen dankbar, und auch zu Vorschlägen wie solche umgesetzt werden könnten ;)

mfg
APK

Hansinator
03.02.2005, 18:19
Kenelvariablen brauch die shell wohl eher nicht..
Es gibt da ein paar interrupt funktionen, bei denen es ganz nett wäre, diese per shell auszuführen ;)
Die wären:
setirq
setint
sehr interessant auch irqflage bzw irqflagd
und vielleicht mkentry
wobei ich grad nicht weiss wie wir das mit den komplexen return values handhaben sollen.

ich weiss nich, lizer sag du mal was, welche funktionen noch interessant wären
die setup funktionen? interruptsetup?

lizer
03.02.2005, 18:24
Interessant wären auf jeden Fall Funktionen wie ps, die alle Prozesse auflisten und Infos dazu geben können.

Hab noch mal 'ne einfache Shell als Ersatz für die loop-Funktion gecodet, der Code von APK war irgendwie... Chaos (sorry :)). Außerdem kriegt ihr so gleich den aktuellen Code. Als Ausgangspunkt für weitere Funktionen der Shell bitte shell.c benutzen, einfach den Befehl oben in den Array rein und unten in den Switch, was passieren soll.

Have fun!

Hansinator
03.02.2005, 18:46
Sehr gut, danke.
Hab jetz ne woche krankenschein, da kann ich mich eingehend damit befassen.
Aber erst is die segmentierung dran ;)

APK
04.02.2005, 20:13
Der Code war wirklich Chaos ich weiß, war ja eher nur als Beispiel gedacht oder als Anregung, hat ja dann auch funktioniert ;) . Ich habe gesehen das es jetzt auch schon eine string-lib gibt :) ich hatte mir vorher für die shell schon selbst eine kleine geschrieben. Ich hab mal daraus die funktionen entfernt die bei der neues version schon dabei sind. Ich gebe die mal als anhang dazu vielleicht ist es ja brauchbar.

Ein anderes Thema, in welche richtung sollte Midgard optimiert werde ? Im prinzip gibts ja nur 3 möglichkeiten: RAM, CPU und Programmgröße. Hab mir das überlegt da in der string.h eine cmp-funktion eine andere aufruft, wäre dann wohl eine Optimierung in Richtung Programmgröße.

So nebenbei ist '\0' gleich 0 ?

Falls ich manchmal etwas seltsame Fragen stelle verzeiht mir ich habe C/C++ im selbststudium gelernt, wir fangen erst jetzt in der Schule damit an ;)

mfg
APK

Hansinator
04.02.2005, 22:39
Naja kommt drauf an!

'\0' ist gleich 0 aber nicht gleich '0'
Also es ist die zahl 0, aber nicht das ascii zeichen 0 ;)

Das die funktionen sich gegenseitig aufrufen ist ok so denke ich, hauptsache die sind da.
Die funktionen die da drin sind sollten auch so bleiben, denn soweit ich weiss sind's die gleichen string funktionen wie in der dings c lib (die nach der ansi norm halt).
Danach sollten wir uns "grob" richten ;)

Sagmal, du zockst clonk?

APK
05.02.2005, 10:02
@Lizer

Kannst du mir kurz beschreiben was die funktion tmint macht ? Das sie fürs wechseln zwischen den Prozessen ist habe ich schon herausgefunden, sie ruft irgendwie schudele auf oder ? Kann leider kein Assembler :(

[COLOR=SlateGray]
Ja ich spiele Clonk aber nicht übers Internet (wegen 56k Modem)

Hansinator
05.02.2005, 12:52
Der tag den du suchst heisst [ot] ;)
und nicht vergessen den auch wieder zu schliessen.

tmint ist der timer interrupt handler.
Die funktion sichert erst die register, bestätigt den interrupt, ruft den scheduler auf (sonst würde ja nie automatisch gescheduled werden ;)) und dann pusht, popt & jumpt es ne runde (keine ahnung warum genau).
Warscheinlich hat das auch was mit dem scheduler zutun.
Am ende jedenfalls gibt's n iret und dann is die funktion fertig :)

Ich hoffe ich hab's nicht zu unverständlich ausgedrückt..

APK
05.02.2005, 16:27
Okay ich gebe mich einfach damit zufrieden das es den laufenden Prozess wechselt.
Ich habe mal versucht einen Befehl zu schreiben der eine Liste mit den Laufenden Prozessen ausgibt, funktioniert zwar auch aber nur etwas "seltsam". Ich würde es für sinnvoll der struct process noch 2 elemente hinzuzufügen. Und zwar die id des Processes und den Namen.

Wenn die Funktion ordnungsgemäß funktioniert werde ich sie hochladen

Warum die Frage wegen Clonk ?

lizer
06.02.2005, 09:24
Hab mir das überlegt da in der string.h eine cmp-funktion eine andere aufruft, wäre dann wohl eine Optimierung in Richtung Programmgröße.Yup, außerdem hab ich noch meine Tipparbeit mini- und meine Faulheit somit opimiert ;)

tmint ist die Trap zum Scheduler. Wie Hansinator schon gesagt hat, zunächst werden die Register auf den Stack gepusht. Dann wird der Scheduler gestartet, welcher die Register vom Stack liest und in der Prozess-Struktur speichert. Dann sucht er den nächsten Prozess raus und liefert einen Pointer auf den Array mit den neuen Registern zurück bzw. NULL, wenn der Prozess nicht gewechselt wurde (z. B. weil nur einer da ist oder so). Wenn das der Fall ist poppt tmint einfach die Register wieder rein und führt den Prozess aus. Ansonsten werden die alten Register vom Stack entfernt und die aus dem Array gepusht, so dass sie mit popa wieder in die 'echten' Register geladen werden können. Anschließend gehts dann weiter mit dem Prozess. Hab in der neuen Version übrigens noch 'nen Counter eingebaut, sodass der Scheduler nicht bei jedem Timer Interrupt aufgerufen wird. So können wir später noch Prioritäten bei Prozessen setzen, also wie viel Zeit ein Prozess hat bis er gewechselt wird.
Der Teil mit dem Sichern der Register in der Struktur wird aber mit der Einführung von LDTs etc. obsolet. Wenn jeder Prozess seinen eigenen Stack hat, können wir die Register einfach pushen, den Stack wechseln und beim nächsten mal einfach wieder poppen.

Hansinator
06.02.2005, 15:31
Ah sehr gut, jetzt weiss ich auch wofür der restliceh asm salat gut is ;)
Ja ich werd mich mal um LDTs kümmern, bin grad nur zu faul mir den ganzen kram reinzulesen...
Aber das kommt noch :D

Wegen clonk hab ich gefragt, weil kaum leute clonk zocken.. ;)

Hansinator
11.03.2005, 20:30
Brr hab grad mal einen blick in die shell.c geworfen..
Die ist ja ... ähm ... grausig *g*
Wie wäre es wenn jemand (jemand der lsut & zeit hat) mal einen kleinen tokenizer schreibt?

Wer hat eigentlich nochmal die string lib geschrieben?
Wär ganz cool wenn da jemand noch eine hex/dezimal string nach int funktion einbaut.
Dann erkläre ich mich auch bereit outp und inp funktionen zu schreiben für die shell, die könnte ich nämlich gebrauchen :)

APK
02.04.2005, 17:12
Da vorher mal die Idee einer Kernelshell aufkam mit deren Hilfe man zur Laufzeit Kernelvariablen verändern kann. Wäre das nicht möglich wenn man weiß wo genau der Kernel die Variable im Ram gespeichert hat ?

lizer
02.04.2005, 18:03
@hansinator: Sorry, hab dein Post irgendwie verpasst ;)

Wie wäre es wenn jemand (jemand der lsut & zeit hat) mal einen kleinen tokenizer schreibt?Kein Problem, aber ohne funktionierendes MM (d.h. malloc, realloc & free) dürfte die Lösung extrem hässlich ausfallen. Mach mich aber trotzdem mal dran.

Wär ganz cool wenn da jemand noch eine hex/dezimal string nach int funktion einbaut.Ebenfalls machbar. In den nächsten Tagen.

Wäre das nicht möglich wenn man weiß wo genau der Kernel die Variable im Ram gespeichert hat ?Ließe sich auch einrichten, aber auch hier wär die Lösung eher hässlich.

APK
02.04.2005, 20:42
Ließe sich auch einrichten, aber auch hier wär die Lösung eher hässlich.
Hässlicher als mein Shellversuch kann es ja nicht sein ;)

lizer
03.04.2005, 02:14
Hässlich was die Implementierung betrifft - ohne MM ist jede verf*** Zeile statisch im Kernel. Whatever, der Tokenizer ist mittlerweile fertig, jetzt pass ich noch die Shell an etc... die nächsten 2-3 Tage wirds was geben.

MfG,
Lizer

Hansinator
11.04.2005, 13:12
Wollte nur mal posten das ich noch da bin und am segmentkram arbeite *g*
Ich hab nur hier und da n paar probleme mit dem gcc assembler, aber es geht eigentlich ganz gut vorran.

Hansinator
14.04.2005, 22:26
So, ich bin heute endlich dazu gekommen das ganze etwas zu testen..

Bisher hab' ich die segment.h fast komplett neu geschrieben und structs verwendet, das macht das ganze eleganter und einfacher (das problem mit den 4byte vielfachen structgrössen hab ich gelöst).

Ausserdem habe ich eine neue, einfachere funktion zum erstellen der segment descriptoren geschrieben.

Das erstellen von segmenten klappt auch ganz wunderbar, wobei ich mich nochmal ausgiebig über die attribute (nicht access flags) von einem segment descriptor informieren muss..
Ich hab' bisher nur die intel 386 documentation und beispiel source benutzt und da sind die infos recht spärlich.
Ich weiss z.b. nicht was der default operand size kram zu bedeuten hat..
Hast du da vielleicht infos zu @ lizer?

Aufjedenfall klappt das lidt, aber alles was danach kommt führt zum reboot :D
(also das laden von cs, ds, ss usw)
Da muss ich noch etwas debuggen und mir anschauen was da eigentlich wirklich passiert und was grub da vorher gemacht hat.

Ansonsten hab' ich die GDTsetup funktion etwas überarbeitet.

Da ich nicht weiss was sonst noch alles da reinsoll an funktionen, würd ich sagen das gdt/segment zeug ist zu 70% fertig, es müssen nur noch die bugs ausgemerzt werden und der source etwas aufgeräumt werden :D

Ich hoffe das ich am wochenende dazu komme die GDTsetup in einen funktionierenden zustand zu versetzen, da ich mich ab nächster woche etwas mehr auf meine fachabi prüfungen vorbereiten muss ....

Auf jeden fall poste ich sonntag mal den source, vielleicht fällt ja jemand anderem noch was dazu ein!

Das wär's soweit. :)

lizer
15.04.2005, 12:40
Ich weiss z.b. nicht was der default operand size kram zu bedeuten hat..
Wenn es das ist, was ich denke das du meinst, dann gibt das an, ob die Befehle (Binary/Maschinenenbefehle) 16 oder 32 Bit lang sind und betrifft die Code Segmente, evtl auch Stack Segmente (?). Das steht aber in der Intel Doku.


Aufjedenfall klappt das lidt, aber alles was danach kommt führt zum rebootWenn du wirklich LIDT meinst, wundert mich das nicht. LIDT lädt die Interrupt Descriptor Table, zum Laden der GDT nimmst du LGDT bzw. LLDT für die Local Discriptor Table.

Freu mich schon auf den Source :)

Lizer

Hansinator
15.04.2005, 13:47
Aaaah ich meine doch LGDT :D

Das laden von den datensegmenten klappt jetzt.
Du hattest da einen kleinen denkfehler...
Und zwar hat ein stack selector (wie er auch in einem segment register gespeichert ist) nicht die kompletten 16 bits für den index in der GDT..
Die ersten 2 bits geben irgendwelche rechte an und das 3. ob es ein GDT oder LDT selector ist.
Ich hab jetzt einfach 0x08, 0x10 und 0x18 für die ersten 3 selektoren als index eingetragen...

Dann gibts ein weiteres problem:
Das laden von CS scheint zu klappen...
Aber in dem intel doc hab ich grad gelesen:


The MOV, POP, and PUSH instructions also serve to load and store segment
registers. These variants operate similarly to their general-register
counterparts except that one operand can be a segment register. MOV cannot
move segment register to a segment register. Neither POP nor MOV can place a
value in the code-segment register CS; only the far control-transfer
instructions can change CS.


D.h. wir können die anderen register ruhig mit mov laden (das probier ich gleich mal).
Wie das mit dem CS funzt hab ich mir mal angeschaut in nem demo source:


_update_cs:
push bp
mov bp, sp
mov ax, [ss:bp+4] ; ax = new cs
push ax ; push segment
push word .1 ; push offset
retf ; we have a new cs now
.1:
pop bp
retn

Das funzt folgendermassen:
Erst holt er sich die parameter vom stack, schiebt den segment selector in den stack und danach das offset von .1

retf macht einen far return (das ist ne control transfer function und läd cs neu)
retf holt sich erst das offset (=eip) vom stack und danach den selector. (far pointer)
Netterweise haben wir die neuen cs und eip values ja auf den stack gepusht :D


Far RET. An intersegment RET restores the values of both CS and EIP which
were saved on the stack by the previous intersegment CALL instruction.


ich hoffe nur das das so funktioniert...

Naja aufjedenfall hab ich noch 2-3 probleme...
Wenn ich den stack selector nicht lade, dann funktioniert fast alles wie es soll.
Er returnt aus der GDTsetup funktion und geht in die memorysetup funktion zurück..
Dann macht er das printk("RAM: ");
Aber danach kommt nix... (edit: er bleibt stehen, wie ein halt)
Ich denke mal das hat mit den ungültigen ss/cs selektoren zutun.

Irgendwo in der nähe der detect funktion scheint er wohl zu versuchen einen der ungültigen selektoren zu laden und bleibt stehen.
Ich hab wenigstens auf ne exception gehofft, aber nix... (edit: bleibt auch stehen, kein reboot *g*)

Aber:
Der ss scheint sich auch ohne probleme neu beladen zu lassen..
Leider kommt er dann nicht über das return der GDTsetup funktion hinaus.

Ich glaube das liegt daran, dass der stack "kaputt geht" und die adressen für das return nicht mehr stimmen.

Nur kann ich das nicht genau sagen..
Ich werde mal versuchen den CS (richtig..) neu zu laden, und mal sehen ob er dann die weitere ausführung bis zum return schafft (mit und ohne ss) und wie weit er noch kommt.

Falls das mit dem stack wirklich so ist und das nicht nur am cs liegt, sollte es doch ausreichen die ret adresse vorher zu poppen und nach dem ss neuladen wieder zu pushen...

Oder was meinst du dazu?

lizer
15.04.2005, 15:33
Ups, hab ich das nicht irgendwo geschrieben? :rolleyes: Die GDT muss zu allererst ersetzt werden, d. h. bevor irgendwas auf'm Stack landet, was wir später wieder brauchen können (wie z. B. Return Adressen). GRUB initialisiert zwar alle Segment mit dem Bereich von 0 - 4G, in der Doku steht aber gleichzeitig, man solle die GDT so schnell wie möglich ersetzen und eigene Segmente anlegen, da in dieser Hinsicht GRUB wohl nicht verlässlich ist oder so und man nicht 100% sicher sein kann, was genau in der GDT von GRUB steht. D. h., GDT in der entry.s laden, noch bevor die MidgardMain aufgerufen wird. Dann muss man auch später nicht blöden Stack-Inhalt kopieren, um Abstürze zu vermeiden.

Hansinator
15.04.2005, 16:43
Hmm... dann müssten wir die gdt aber noch vor dem ersten function call ersetzen, bzw nicht in einer subroutine, sondern direkt in der midgard main.
Sonst gehen wieder stacksachen verloren..

Mir ist irgendwie nach dem posting aufgefallen, dass du das ja gemacht hast mit dem eip+cs auf den stack pushen, sodass das return das läd.
Aber das muss sofort geschehen, nicht erst beim return der funktion.

Ich hab den asm code umgeändert und ne funktion zum laden von cs gebaut.
Die funktioniert auch (glaub ich).
Ich hab lange gebraucht um rauszufinden das interrupts wärend dem cs laden aus sein müssen ... (sonst rebootet der käse)

Aufjedenfall gibts 2 cases:

case 1:
load_cs steht direkt nach lgdt.
wenn die nächste zeile kein while(1) ist, dann rebootet das sys sofort...
break; *g*

case 2:
load_cs steht nach dem laden von ds,es,gs,fs und ss
dann passiert garnixmehr nach dem load_cs
ich weiss nur nicht in welcher asm zeile die ganze geschichte stehen bleibt :/

In dem beispielcode den ich habe, wird CS aber als erstes direkt nach lgdt geladen.
Leider ist der beispielcode für n 16bit dos compiler und n passenden 16bit assembler..

Naja, schau einfach mal über meinen ASM code, vielleicht fällt dir ja was auf...
Das mov eax, 0x08 ist absicht, weil ich mir nicht sicher war ob ich den parameter korrekt bekommen habe.


; Helper functions for loading segments

global load_cs

; this function forces the cpu to reload CS and EIP using retf
load_cs:
push ebp
mov ebp, esp
mov eax, [ebp + 4] ; set eax to the new cs
mov ax, 0x08
push ax ; push segment
push dword .1 ; push offset
retf ; we have a new cs now

.1: pop ebp
retn


Gibt es denn hier keinen der sowas schonmal gemacht hat?

edit:
Der segment selector hat ja 16 bit, deswegen dachte ich es is ne gute idee nur ax auf den stack zu pushen. Ich werds morgen noch mal mit eax probieren, aber ich glaub nicht das das funktioniert.
Aber ich hab da noch ne andere vermutung:
dword = 32bit aber wenn die operand size nur 16 bit ist, glaub ich braucht der nur ein word (also 16 bit) für den 2. teil des far pointers.

Hansinator
18.04.2005, 22:20
Huhu!
Ich bin sonntag leider nicht dazu gekommen den code zu posten..
Ich poste den jetzt mal so wie er ist, zum basteln :)