Autor Thema: RPC geht unter bochs, aber nicht unter qemu  (Gelesen 2822 mal)

AcidIO

  • Beiträge: 11
    • Profil anzeigen
Gespeichert
« am: 30. September 2009, 19:38 »
Also ich hab ein Problem(mal wieder).....

Wenn ein Task einen RPC zum Kernel ausführt, funktioniert das ganze ohne probleme, wenn ein Task aber einen RPC zu einem anderen Task macht bekomm ich eine GPF in Qemu und auf einem echten PC, debuggen kann ich das leider schlecht da es unter bochs ohne probleme funktioniert.
Komischerweise tritt dieses Problem nur dann auf wenn die Stackaddresse des callee(so heißt doch der aufgerufene RPC Handler dann, oder?) != 0xF0000000 ist oder BASE_STACK SIZE >= 0x4000, ansonsten funktioniert es ganz normal(jedenfalls im Emulator auf einem echtem Computer bekomm ich auch ne GPF).

Hier mal meine make_rpc Funktion:

// führt einen rpc-aus(zu einem RING0-Task)
void make_rpc(UINT32 callee_pid, rpc_data *data)
{
    //den task suchen, dessen rpc-handler afgerufen werden soll
    Task *callee = get_task_from_pid(callee_pid);
    if(callee == 0)
    {
        Console_putf("make_rpc: callee %i existiert nicht.\n");
        return;
    }
    // wenn kein rpc-handler vorhanden ---> ende
    if(callee->rpc_handler == 0)
    {
        Console_putf("make_rpc: callee %i hat keinen rpc-handler.\n");
        return;
    }
    UINT32 callee_stack;
    if(callee->pid != 0)
    {
        int i;
        for (i = 0; i <= BASE_STACK_SIZE; i+=0x1000)
        {
            UINT32 va = (VIRT_STACK_ADDR + i);
            UINT32 pa = mem_get_phys_addr(VIRT_STACK_ADDR + i, (page*)callee->page_dir);
            mem_map(kernel_pd, va, pa);
            if (mem_get_phys_addr(va, kernel_pd) != pa)
            {
                Console_putf("Fehler beim mappen des callee_stacks(i = %x)", i);
            }
        }
        callee_stack = callee->stack;
    }
    else
    {
        // ok, den kernel stack brauchen wir icht mappen, da das kernel_pd schon geladen ist
        callee_stack = callee->stack;
    }
    // jetzt dort einen weiteren irq-stack hinzufügen
    UINT32 *stack = (UINT32 *) callee_stack;
    data->caller_pid = current_task->pid;
    // vor den daten wird noch der alte stackpointer gespeichert
    data->callee_old_esp = (UINT32) callee_stack;
    // jetzt die daten auf den stack
    *(--stack) = (UINT32)data;
    // die rücksprungaddresse des rpc-hadnler, welche diesen beendet
    *(--stack) = (UINT32)rpc_end_stub;
    *(--stack) = 0x0202;
// das codesegment(kernel-task = 0x08)
*(--stack) = 0x08;
// der einstiegspunkt(eip)
*(--stack) = callee->rpc_handler;
// WICHTIG: die IRQ-nummer und error-code(weil der timer-interrupt zuständig ist für multitasking)
*(--stack) = 0x00;
*(--stack) = 0x00;
// Allzweckregister
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
*(--stack) = 0x00;
// jetzt die datensegment(kernel-task = 0x10)
*(--stack) = 0x10;
*(--stack) = 0x10;
*(--stack) = 0x10;
*(--stack) = 0x10;


    // jetzt den stack verschieben
    callee->stack = (UINT32)stack;
    callee->makes_rpc = 1;
    // ok jetzt den caller blocken und zum callee wechseln
    current_task->blocked = 1;
    callee->last_rpc_data = data;
    // ok wenn der callee, schläft, ihn jetzt aufwecken
    callee->sleeping = 0;
    //while(current_task != callee)
    while(current_task->blocked == 1)
    {
     multitasking_taskswitch();
    }
}

Hier die verwendeten Konstanten:

#define BASE_STACK_SIZE     0x2000
#define VIRT_STACK_ADDR     0xF0000000

Hoffe ihr könnt mir helfen...
Wer nichts wagt kann nichts verlieren...

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 30. September 2009, 19:59 »
Naja, als erstes solltest du mal schauen, wo es genau passiert und was überhaupt passiert. Interessant wäre also, wo eip hinzeigt, wenn der GPF auftritt und vielleicht kannst du auch mit dem Fehlercode was anfangen. Wenn dein OS beides nicht ausgibt, kannst du qemu mit -d int starten, dann werden alle Interrupts jeweils mit einem CPU-Dump nach /tmp/qemu.log geloggt.

Dann noch zwei Sachen, die mir beim bloßen Überfliegen aufgefallen sind: Eine Funktion multitasking_taskswitch mitten im C-Code sieht unheimlich aus. Oder ist dein Kernel wirklich darauf ausgelegt, multithreaded zu laufen? Ob es korrekt ist, hängt natürlich davon ab, wie es implementiert ist.

Und dann noch zu der konkreten Art der Kommunikation. Du scheinst dasselbe wie tyndur zu machen, also quasi Unix-Signale mit Daten dran. Das Konzept hat das Problem, dass der RPC-Handler an jeder beliebigen Stelle im Programm aufgerufen werden kann und das Programm damit umgehen können muss. Wenn du ungefähr eine Vorstellung davon hast, was man in Signalhandlern machen darf, dann ist das ungefähr dasselbe, was man in deinen RPC-Handlern machen dürfen wird. Welche Variante du wählst, musst du am Ende selbst entscheiden, aber du solltest über die Probleme damit mal nachgedacht haben.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

AcidIO

  • Beiträge: 11
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 30. September 2009, 20:36 »
Also die funktion multitasking_taskswitch ist mein scheduler und wählt den task aus, der am ende des RPC syscalls laufen soll.

Der GPF kommt beim pop'en der segmentregister wo (in bei diesem beispiel) gs komischer mit 0x01 beladen wird.

Allerdings erklärt das alles nicht wieso es mit bochs ohne probleme läuft.

Was du mit signals meinst weiß ich leider nicht.
Wer nichts wagt kann nichts verlieren...

DerHartmut

  • Beiträge: 236
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #3 am: 01. October 2009, 06:10 »
Signale sind halt Signale, die an einen Prozess gesendet werden können und welche dieser verarbeiten kann. Bekannt sind z.B. unter Linux SIGTERM, SIGABRT und SIGKILL.
$_="krJhruaesrltre c a cnp,ohet";$_.=$1,print$2while s/(..)(.)//;
Nutze die Macht, nutze Perl ;-)

AcidIO

  • Beiträge: 11
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 01. October 2009, 14:53 »
Wenn ich meine Konstanten so setzte:

#define BASE_STACK_SIZE     0x8000
#define VIRT_STACK_ADDR     0xBADF0000

tritt der GPF bei 0x1050F0 auf, also also am Ende meiner Syscall-Behandlungsroutine. Meine Behandlungsroutine sieht momentan so aus:

%macro save_regs 0
  ; alle register sichern
pushad
;segmente sichern
push ds
push es
push fs
push gs
; den kernel daten deskriptor laden
cld
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; das kernel-pd laden
mov eax, [kernel_pd]
mov cr3, eax
; den stack des momentaten prozesses nach current_process
mov eax, [current_task]
mov [eax], esp
; alten stack nach ecx sichern
mov eax, esp
; jetzt den kernel_stack laden
; jetzt in den kernel_stack wechseln(wenn task nicht ring3, da der schon im kernel stack liegt)
mov ebx, [current_task]
    mov ecx, [ebx + 12]
    sub ecx, 3
    jz %%l1
    mov esp, [ebx + 4]
%%l1:
push eax
%endmacro

%macro rest_regs 0
pop eax
; in den stack des momentanen(evtl. auch nächsten) prozesses wechseln

mov eax, [current_task]
mov esp, [eax]
; das pd setzten
mov ebx, [current_task]
mov eax, [ebx + 8];
mov cr3, eax
;cli
;hlt
    ;  register zurückholen
pop gs
pop fs
pop es ; <--------- HIER KOMMT DER GPF(es wird mit 0x01 beladen)
pop ds

popad
%endmacro

; syscall(push ebenfalls int_no und error_code(0), damit man auch in ihm einen prozess wechseln kann kann)
global sc
sc:
        ; dummy werte auf den stack
push byte 0
push byte 0x77
save_regs
; jetzt den syscall hander aufrufen
mov eax, Syscall_handler
call eax
rest_regs
; die dummy paramter vom stack nehmen
add esp, 8
iret

; wird als rücksprungaddresse für einen rpc-call benutzt, damit der stack kontant bleibt, und beendet diesen
global rpc_end_stub
rpc_end_stub:
    mov eax, 0x02
    pop ebx
    int 0x77


Je nachdem wo ich den Stack platziere 0xE0000000 oder 0xBADF0000 kommt der GPF irgendwo beim pop'en der segment register, aber nur wenn der RPC_Handler eines Prozesses aufgerufen wird und nicht der des Kernels.

Achja der Platzt des Stacks ist reserviert, d.h keine funktion wie malloc o.ä alloziert die virtuellen speicher zwischen VIRT_STACK_ADDR und VIRT_STACK_ADDR + BASE_STACK_SIZE.

Ich hab mir auch in qemu mittels "info tlb" mal die zuordnung zwischen virtuellem und physischem Speicher angesehen, es zeigt auch keine andere virtuelle addresse auf den physischen platzt des Stacks, von da an kann ich mir diesen Fehler absolut nicht erklären. Wenn ich VIRT_STACK ADDR auf 0xF0000000 setzte kommt der GPF nur auf einer Realen Maschiene nicht in qemu/bochs.
Wer nichts wagt kann nichts verlieren...

AcidIO

  • Beiträge: 11
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 01. October 2009, 19:11 »
Also ich glaub ich werte verückt.....

Bei einem pop wird doch normalerweise der wert [esp] geholt und esp um vier erhöht.
Jetzt wird in meiner make_rpc funtion, der stack erniedrigt und dabei werte gesetzt(so wie beim erstellen eines Tasks) und dann die momentane addresse des (erniedrigten) stacks nach callee->stack gespeichert(ebanfalls genau so wie beim erstellen eines Tasks).
Danach liegt der Stack bei 0xBADF0F00. Am Ende meines syscall-handlers (um genau zu sein beim laden der segmentregister vom Stack), steht ESP(wenn der GPF kommt) komischerweise auf 0xBADF0EFC(also noch niedriger, weil ESP müsste ja dann > 0xBADF0F00 sein).

Entweder hab ich irgendwo einen großen Denkfehler oder das ist richtig so(ich tendiere zum Denkfehler).

Ich hoffe mal irgendwer weiß woran das liegt weil ich weiß echt nicht mehr weiter.
Wer nichts wagt kann nichts verlieren...

 

Einloggen