Autor Thema: Rückkehr aus Task mit IRET geht nicht  (Gelesen 7384 mal)

supernicky

  • Beiträge: 7
    • Profil anzeigen
Gespeichert
« am: 08. January 2014, 21:18 »
Hallo Gemeinde,

ich versuche seit einiger Zeit den Taskswitch mittels IRET.
Mein Programm erstellt nur eine GDT, 2 TSS Segmente mit den Descriptoren.
Alles läuft außerdem in Ring 0.

Der Aufruf des Tasks mittels CALL funktioniert tadellos. Am Ende soll mit IRET wieder zurückgesprungen werden,
da streikt aber die CPU. (Prozessorfehler)

Beide Tasks nutzen die selben Segmente (CS, DS) nur ESP ist unterschiedlich.

Ich habe mich schon auf Fehlersuche begeben und die Stacks und die TSS Segmente untersucht.
Das Backlink Feld wird richtig gesetzt und das Busy-Bit wird auch gesetzt. Jedoch finde ich weder
auf den Stacks, im TSS noch im EFlag Register das gesetzte NT-Bit was dafür erforderlich ist.

Die CPU muss das doch irgendwo speichern oder?

Wenn ich anstatt Call, JMP benutze springt er fehlerfrei zwischen beiden Tasks hin und her.

ESP des ersten Task = 0x80000 beim zweiten ist ESP auf 0x80500

Ich weiß nicht mehr weiter

Nicky

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 09. January 2014, 12:08 »
Hallo supernicky
Willkommenen im Forum.

Interessant wäre natürlich welche Exception ausgelöst wird.
Und dann ist das passende Gegenstück zu einem CALL ein RET (und kein InterruptRETurn).

« Letzte Änderung: 09. January 2014, 12:11 von MNemo »
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 09. January 2014, 12:25 »
Früher oder später werden wir es wahrscheinlich sowieso brauchen, deswegen: Kannst du deinen Code gleich mal irgendwo hochladen?

In welcher Umgebung testest du denn? Echte Hardware oder ein Emulator?

Ich habe noch nie ein CALL in ein Task Gate gemacht, aber das IRET dürfte schon das passende Gegenstück sein. Kannst du mit einem Exceptionhandler (oder ggf. der Logdatei des Emulators) noch die genaue Exceptionnummer, Errorcode und einen Registerdump bekommen?

Was das NT-Flag angeht, das wird meines Wissens im "echten" eflags-Register gesetzt, nicht in der Kopie in einem TSS, die ja zum alten Zustand oder zum anderen Task gehört, je nachdem, welches TSS du anschaust. In eflags sollte es aber wirklich da sein. Wenn es fehlt, ist es nicht überraschend, wenn das iret anschließend schiefgeht. Der Backlink im TSS ist aber korrekt gesetzt?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

supernicky

  • Beiträge: 7
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 09. January 2014, 19:52 »
Hallo zusammen,

ich schreibe mit NASM und mein System ist Virtual PC.
Wenn man mit Call einen Taskwechsel auslöst

JMP 0x18:123456

sollte das NT Bit im EFLAG Register gesetzt werden und im Backlink Feld der neuen Task wird das TSS des aufrufenden Task's abgelegt.
Somit ist es möglich mittels IRET (nicht RET) zum alten Task zurück zukehren.

http://www.fh-zwickau.de/doc/prmo/pmtutor/text/p_spez2.htm ebenfalls im Buch von Klaus-Dieter Thies Seite 375.

Auf eine IDT habe ich der Einfachheit halber verzichtet, da ich den Sprung mit Call/IRET durchführen möchte.

Mein Code ist recht einfach aufgebaut:
Das A21 Bit ist hier im Code nicht aufgeführt wird aber gemacht :)
org 0x8000
cpu 486
[Bits 16]


start:
cli ;Interrupts aus
;alle Register löschen
in al, 0x60 ;Tastaturbuffer leeren
xor ax, ax
xor bx, bx
xor cx, cx
xor dx, dx
xor di, di
xor si, si

lgdt [GDT] ;GDT laden

mov eax, cr0
or ax, 1
mov cr0, eax ;In den P-Mode wechseln

jmp 0x8:pmode
[Bits 32]
pmode:
;Selectoren setzen
xor eax, eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

mov esp, 0x80000      ;Stack auf 512 KByte

mov ax, 32d
ltr ax ;OS Urtask erstellen

call getmemsize ;Speichergröße feststellen
mov [ram], eax ;und speichern


call createtables ;Paging aktivieren


call 0x28:0x123456 ;Userprogramm als Task aufrufen

push dword str_umbruch
call prints

jmp warteschleife
;######################################################
; ab hier nur ein Test ob alles geht!!!
;------------------------------------------------------------------------------------------------------------
%include "main.inc"



;#############################################
;------------------------------------------------------------------------------------------------------------
warteschleife:
nop
jmp warteschleife


meine GDT sieht so aus:
gdttabelle: ;0
dd 0
dd 0

CODE: ;8
dw 0xFFFF
dw 0                 
db 0                 
db 10011010b
db 11001111b ;DB und G Bit gesetzt
db 0


DATEN: ;16
dw 0xFFFF
dw 0
db 0                  
db 10010010b
db 11001111b ;DB und G Bit gesetzt
db 0
USER_TASK: ;24 ;Das USER TSS
dw 104d
dw TSS1
db 0
db 10001001b
db 10000000b
db 0
TMP_TSS: ;32 nur zur temporären Umschaltung des Tasks
dw 104d
dw tmptss
db 0
db 10001001b
db 10000000b
db 0
USER_GATE: ;40 Task Gate Userprogramm
dw 0
dw 24d
db 0
db 10000101b
dw 0

GDT:
gdt_limit dw $ - gdttabelle
gdt_base dd gdttabelle


Nachdem nun mit call 0x28:0x123456      ;Userprogramm als Task aufrufen in den neuen Task gewechselt wurde, werden ein paar Daten ausgegeben:
Das ist nichts großes..
user:

push dword crlf
call prints
push dword crlf
call prints
push dword crlf
call prints


mov ebx, 0x804f0
mov eax, [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints

mov ebx, 0x804f4
mov eax, [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints

mov ebx, 0x804f8
mov eax, [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints

mov ebx, 0x804fc
mov eax, [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints

push dword crlf
call prints

mov ebx, USER_TASK
add ebx, 5d
push ebx
xor eax, eax
mov al, byte [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints
pop ebx
add ebx, 8d
xor eax, eax
mov al, byte [ebx]
push eax
call itoh
push eax
call prints
push dword crlf
call prints

;iret
;jmp 0x20:0x123456

Falls noch von Interesse hier noch die beiden TSS
TSS1 ist für das aufgerufene Programm und tmptss ist nur zur temporären Speicherung der Urtask. (Da wird später nicht mehr zurückgesprungen)
TSS1:
dw 0 ;Backlinkfeld
dw 0 ;NULL
dd 0x80000 ;ESP0
dw 0x10 ;SS0
dw 0 ;NULL
dd 0 ;ESP1
dw 0 ;SS1
dw 0 ;NULL
dd 0 ;ESP2
dw 0 ;SS2
dw 0 ;NULL
dd 0x21000 ;Cr3 = 0x21000 = 135 KByte
.eip dd user ;EIP
.eflag dd 0x2 ;EFlag
;----- allgemeine Register
dd 0 ;EAX
dd 0 ;ECX
dd 0 ;EDX
dd 0 ;EBX
dd 0x80500 ;ESP
dd 0 ;EBP
dd 0 ;ESI
dd 0 ;EDI
;-------
dw 0x10 ;ES
dw 0 ;
dw 0x8 ;CS
dw 0 ;
dw 0x10 ;SS
dw 0 ;
dw 0x10 ;DS
dw 0 ;
dw 0x10 ;FS
dw 0 ;
dw 0x10 ;GS
dw 0 ;
dw 0 ;LDT Selektor
dw 0 ;NULL
dd 0 ;
;########################################################
tmptss:
times 104 db 0

Beide Tasks benutzen die selben Segmente (Flat 4GB) aber unterschiedliche Stackzeiger (ESP). Beide laufen in Ring 0.
Das Bit 14 (0-15) wird nicht gesetzt nach Call und in beiden Stacks werden auch keine Daten abgelegt.

Falls noch was fehlt, einfach melden...

Danke für die Hilfe

Nicky

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 10. January 2014, 00:17 »
Auf eine IDT habe ich der Einfachheit halber verzichtet
Der Einfachheit halber hättest du das besser nicht getan. Ein einfacher Handler der dir die für die ersten 32 Interrupts seine Nummer, die Rücksprung Adresse(also etwa da wo der Fehler aufgetreten ist) und den alten Stackpointer ausgibt machen dir das Leben sehr viel leichter. Vor allem wenn dir dein Emulator das nicht liefert.

Und ein vollständiger Registerdump macht das Suchen nach dem Fehler dann evtl. noch ein ganzes Stück einfacher.

Bastel dir also einen Screen of Death, entnimm die Daten den logs von Virtual PC (falls der so etwas loggt) oder nutze qemu -d int -hda disk.img
(könnte sein das unter Windows die Parameter für Qemu nicht ganz reichen.)

Und lass uns wissen was du so für Werte hast wenn dein Kernel den Geist auf gibt. Sich so ganz ohne, durch Assemblercode durch zu denken, macht nicht sonderlich viel Spaß.
« Letzte Änderung: 10. January 2014, 00:19 von MNemo »
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

supernicky

  • Beiträge: 7
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 13. January 2014, 19:12 »
Hallo nochmal,

hat etwas gedauert da wir im Umzugsstreß sind...

Aufgerufen wird der IRQ13 (Allg. Schutzverletzung)

Im Anhang habe ich mal den Screenshot eingefügt.
Folgende Daten liegen auf dem Stack:

Fehlercode: 0x0
EIP: 0x8259
CS: 0x8
EFLAG: 0x46

Im Listing beginnt er bei Offset 0x0, es müssen aber 0x8000 addiert werden.

Was auch nicht funktioniert ist, wenn in der IDT ein TASK Gate verwendet wird.
Auch hier wird der IRQ 13 aufgerufen.

Gruß, Nicky

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 13. January 2014, 23:38 »
Ist es denn Tatsächlich das IRET an 0x8259?

Wie sieht den das ende deiner "prints" aus. Nutzt tu tatsächlich "ret 4" (eher Unüblich), oder machst du dir in deinem Task den ganzen Stack kaputt weil du
die Parameter nicht mehr runter holst? Die meisten folgen hier der C-Calling-Convention wonach der Caller den Stack aufräumt, das machst du aber offensichtlich nicht.

Der Fehler-Code bei einem #GP ist die Segmentselektor, die den Fehler verursacht hat. Ist das tatsächlich 0 oder ist die Angabe eventuell Fehlerhaft? (Wenn du einen Call ins Datensegment machst, sollte der data-segmentselektor angegeben werden)
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

supernicky

  • Beiträge: 7
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 17. January 2014, 18:22 »
Hallo nochmal,

ich habe mein Programm mal in VirtualBox (Oracle) ausprobiert...
Wenn ich ein Task Gate in der IDT anlege und den Interrupt mit "int 33h" aufrufe, wird dieser ausgeführt und auch das NT-Bit wird gesetzt  :-o
Sogar der Rücksprung über IRET funktioniert tadellos  :-D

Wenn ich das selbe Task Gate jedoch auf den IRQ 33 (Tastatur lege) bekomme ich die Fehlermeldung "undef. Opcode" sobald ich eine Taste drücke  :?

Alles seltsam

Nicky

PS: ich nutze am Ende jeder Funktion ret 4... ret 8 usw... damit hatte ich noch nie Probleme.

 

Einloggen