Eigenes OS?



  • PHPnerd schrieb:

    so
    int a = 5/0;
    a will be zero, instead of killing process.

    no, that keeps the error. the error creeps forward and will cause weird behaviour of the whole thing. instead, exceptions should be 'handled' in a controlled manner (like this):

    ...
    begin_guarded_section();
    {
      ...
      int a = x/0;
      ...
    }
    if (get_exception_code() == DIV_BY_ZERO)  // enter exception handler 
    {
      // deal with division by 0
    }
    end_guarded_section();  // stop guarding
    ...   // process will be killed at this point, if another exceptions occur
    

    ^^all can be done using c-functions. there's no need for a modfied compiler (like that is the case with structured exception handling under m$-windoofs).
    🙂



  • So, no kernelmode div by zero handler?

    Erhard: do not forget to invalidate the TLB. use INVLPG (see Intel manuals) or read CR3, and write it to it again.

    // PHPnerd



  • PHPnerd schrieb:

    So, no kernelmode div by zero handler?

    of course, kernel must handle that, but must not terminate the application if an exception occurs inside guarded code. after that, the application should ask the kernel for exception code and (if there is one), retry the operation, notify the user, abort the program or something like that.
    🙂



  • All right, understand. But that is something for later on to me 😛

    // PHPnerd


  • Mod

    Yes, exception handling has to be fine-tuned in PrettyOS. Currently, there is only a message and a big stop sign. Page faults are already analyzed deeper.

    I worked on the VFS and the RAM disk, because it is very important to bring data, specialized kernel modules or user code into the memory of the OS. The way how it works now is as follows:

    1. produce a binary image from various files (with "make_initrd.exe", source code: "make_initrd.c"). I renamed the resulting image to "file_data.dat". This is the target for incbin in process.asm.

    2a) include it in process.asm with incbin, global addresses:

    ; data for ramdisk
    global _file_data_start
    global _file_data_end
    _file_data_start:
    incbin "file_data.dat"
    _file_data_end:
    

    2b) Linker transfers it to the memory at &file_data_start

    1. The source code line
    k_memcpy((void*)ramdisk_start, &file_data_start, (ULONG)&file_data_end - (ULONG)&file_data_start);
    

    in ckernel.c transfers the data with the files to the RAM disk (this is the way we exchange data with PrettyOS at the time being)

    1. The Virtual File System (VFS) with individual file headers helps to access the files in the RAM disk

    Enjoy it! 🙂

    PrettyOS: http://www.henkessoft.de/OS_Dev/Downloads/36.zip
    Screenshot: http://www.henkessoft.de/OS_Dev/Downloads/ramdisk_test.png

    Currently the framework for booting, loading asm kernel, switch to PM and C-kernel, interrupts, write/read key queue, set system timer frequency (I use 100 Hz), paging, heap, virtual file system, RAM disk, multitasking, syscalls now basically work. 🙂



  • Erhard Henkes schrieb:

    Hier ein prinzipielles Beispiel mit Erzeugung mehrerer Tasks und Taskswitch:...

    Aha, jetzt wird's interessanter und praxisbezogener...


  • Mod

    Aha, jetzt wird's interessanter und praxisbezogener...

    Die letzten beiden Wochen habe ich mich - zumindest grundlegend - um die komplexen Themen Memory-, Task- und File-Management (VFS, RAM Disk) sowie Syscalls gekümmert, um zu sehen, wie man dies am besten aufbauen und darstellen könnte. Manchmal sucht man auch nur "ewig" nach einem Fehler im Sourcecode. Hierbei behebt man dann zunächst noch alle möglichen anderen Baustellen, die man bei genauerem Hinsehen als nicht korrekt aufspürt. Bei mir liegen die "üblen" Fehler immer im Zeiger-Bereich. Kleines Beispiel:

    Diese Zeile hatte ich schnell "hingehauen":

    k_memcpy((void*)ramdisk_start, &file_data_start, &file_data_end - &file_data_start);
    

    Mit k_memshow(...) sah ich zufrieden, dass die "eingelinkten" Daten gefunden und sauber an den Anfang der RAM Disk kopiert wurden.
    Das Schlimme ist, dass durch die Pointer-Arithmetik zu wenige Daten kopiert wurden. Über die Blödsinnigkeit dieses Fehlers (es gibt nur wenige geniale Fehler) möchte ich hier nicht reden, so etwas passiert eben ab und zu, wenn man den cast auf die Schnelle vergisst. Dieses Wechselspiel zwischen Speicheradresse und Darstellung als unsigned long integer (ULONG) ist eine der Schwächen des Systems. Zwei kleine Casts nach ULONG, und schon funktionierte es perfekt:

    k_memcpy((void*)ramdisk_start, &file_data_start, (ULONG)&file_data_end - (ULONG)&file_data_start);
    

    Das Schlimme ist, dass man den Fehler an völlig anderer Stelle - nämlich den neu eingebauten Mechanismen - sucht. Auslöser ist, dass man große Speicherbereiche nicht auf einfache Weise wie bei einem Hex-Editor durchscrollen kann. Mir ist der Fehler erst aufgefallen, als ich mich mit k_memshow(...) auf die konkrete Stelle im Speicher gesetzt und dort nur Nullen gefunden habe. 😃
    Solche Erfahrungen sind in einem Tutorial wichtig und sollten auch dargestellt werden, damit andere an solche Trivialitäten denken, wenn Sie Fehler suchen. Denn das "Aufgeben" kommt oft daher, dass man Fehler nicht findet, keine geeigneten Vorbilder hat und/oder in einem Forum (http://forum.osdev.org/ ist ein Musterbeispiel) arrogant behandelt wird.

    Momentan schreibe ich an dem Tutorial noch nicht weiter, weil die grundlegenden Mechanismen zwar funktionieren, ich aber mit der Visualisierung noch nicht zufrieden bin.

    Erste zaghafte Beispiele für Visualisierung sind:

    void k_memshow(void* start, size_t count)
    {
        const UCHAR* end = (const UCHAR*)(start+count);
        for(; count != 0; count--) printformat("%x ",*(end-count));
    }
    

    und die Paging-Analyse-Funktionen

    ULONG show_physical_address(ULONG virtual_address)
    {
        page_t* page = get_page(virtual_address, 0, kernel_directory);
        return( (page->frame_addr)*PAGESIZE + (virtual_address&0xFFF) );
    }
    
    void analyze_physical_addresses()
    {
       int i,j,k, k_old;
        for(i=0;i<(PHYSICAL_MEMORY/0x18000+1);++i)
        {
            for(j=i*0x18000; j<i*0x18000+0x18000; j+=0x1000)
            {
                if(show_physical_address(j)==0)
                {
                    settextcolor(4,0);
                    k_old=k; k=1;
                }
                else
                {
                    if(show_physical_address(j)-j)
                    {
                        settextcolor(3,0);
                        k_old=k; k=2;
                    }
                    else
                    {
                        settextcolor(2,0);
                        k_old=k; k=3;
                    }
                }
                if(k!=k_old)
                    printformat("%x %x\n", j, show_physical_address(j));
            }
        }
    }
    

    Im Tasking-Bereich ist mir bisher nur die Darstellung der PID als Farbe beim "Tippen" (Schreiben/Lesen KeyQueue) eingefallen.
    Vor dem Heap-Gebäude stehe ich noch etwas zögerlich bezüglich Darstellung der "blocks and holes". Man hat im Textmode mit 80*25 mit wenigen brauchbaren Farben auch nicht allzu viele Möglichkeiten.

    Didaktische Ideen sind daher immer willkommen. 🙂



  • Well done!

    I am still busy with kmalloc, almost finished it.

    How advanced is your multitasker now?

    And did you make your filesystem manager usable for programmers, who want to make a filesystem driver like Ext2, Ext3, FAT etc?

    // PHPnerd


  • Mod

    How advanced is your multitasker now?

    There is the function task_switch of PrettyOS: 1 -> 2 -> 3 -> ... -> N -> 1 ->...
    This could be triggered by the system timer according to allowed timeslices, but not done until now. I tried it out, but got General Protection Fault. You can't switch at each time. Thus, the question is how to avoid and switch on the right time?
    ..

    As mentioned above, I don't have a time-based scheduler. Here I found some easy code, which might be interesting: ..

    What do you think about it?
    How to prohibit general protection faults by the task switch?

    And did you make your filesystem manager usable for programmers, who want to make a filesystem driver like Ext2, Ext3, FAT etc?

    Not yet, but it should be possible by the VFS.
    ..

    I was very glad, when it worked. 🙂

    Looking at the path forward, I think the next milestones could be:
    - loading executables (file format?)
    - kernel modules
    - shell

    How do you / will you load executables? (one of the text files in the RAM disk of the last example could have been an executable file (elf, com, exe, ...) ). What's the best way to start it?


  • Mod

    Try to use cli(); at start of the handler.

    I did, but the GPF didn't leave. In between I was told that C might be not perfect for the task switch. 🙄 The above presented function for task_switch does not save all registers. That might be the real problem in some situations.


  • Mod

    Another point in the whole source code is the mixing of physical and virtual memory management in paging.h/paging.c. That has to be separated.



  • Sure it has to be separate, i do, the PMM, VMM and kmalloc, all apart.

    What is o_0:

    if(!(queue_empty(&roundrobin_prozesse))&&p->prozess_timetorun<1)
    

    the double && looks very bad to me

    I checked task switch code with my old, its the same (diff is what I added, like threads and states). I put a cli() just after very first statement. (if(!task)).

    And where exactly (line) does the #GP trigger? (just try putting chars like ! on as much as lines as possible, and count numbeer of printed when running)

    // PHPnerd


  • Mod

    just try putting chars like ! on as much as lines as possible, and count numbeer of printed when running

    Interesting trick. This I try at once:

    !!!!#!!!!#!!!!#General Protection Fault >>> Exception. System Halted! <<<

    Hence, I know that the GPF happens in the asm-code:
    ..
    because the last ! did not show up.



  • You really do? Dont be so sure. What does the ASM do? Yes, it switches task, it chnages page directory, and the instrcution pointer after that code, we continue the task, and not the code of the scheduler.

    Or am i wrong, and is the ! showed at the other switchs?

    // PHP



  • Erhard Henkes schrieb:

    In between I was told that C might be not perfect for the task switch. The above presented function for task_switch does not save all registers. That might be the real problem in some situations.

    that's true. even if most c-compilers have support for interrupt routines, you can't access cpu registers directly in C (only few compilers for embedded systems support that). you have to code this very low level stuff in assembly. all real life RTOS (although portable among many cpus), with preemptive schedulers, use asm for the heart of the task switcher.
    🙂


  • Mod

    Well, you think, the last ! cannot show up. OK, then I have to try a situation where it works correctly.

    You are right! If it works, then !!!!#!!!!#!!!!## is presented at the screen. Thus, the last # is given out twice instead of the ! 😕

    Now I am in a dead-end-street. 😞



  • Are you using Bochs? I hope so, do following for me:

    put cli, hlt, for(;;); before the big assembly part. Shutdown, and open the bochsout file. Put the register info in here. Btw: remove any calls to the scheduler and call it the way it will fault. So it will give us the right values.

    // PHPnerd


  • Mod

    /// TEST
          cli();
          asm volatile("hlt;");
          for(;;);
        /// TEST
    
        asm volatile("         \
        //...
    
    01368000000p[WGUI ] >>PANIC<< POWER button turned off.
    01368000000i[CPU0 ] CPU is in protected mode (halted)
    01368000000i[CPU0 ] CS.d_b = 32 bit
    01368000000i[CPU0 ] SS.d_b = 32 bit
    01368000000i[CPU0 ] EFER   = 0x00000000
    01368000000i[CPU0 ] | RAX=000000000000008f  RBX=000000000000d502
    01368000000i[CPU0 ] | RCX=00000000000b8000  RDX=00000000000003d5
    01368000000i[CPU0 ] | RSP=000000000018ff1c  RBP=000000000018ff44
    01368000000i[CPU0 ] | RSI=000000000018ffd8  RDI=000000000018fff0
    01368000000i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
    01368000000i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
    01368000000i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
    01368000000i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
    01368000000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf
    01368000000i[CPU0 ] | SEG selector     base    limit G D
    01368000000i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
    01368000000i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
    01368000000i[CPU0 ] |  MSR_FS_BASE:0000000000000000
    01368000000i[CPU0 ] |  MSR_GS_BASE:0000000000000000
    01368000000i[CPU0 ] | RIP=000000000000d3f9 (000000000000d3f9)
    01368000000i[CPU0 ] | CR0=0xe0000011 CR1=0x0 CR2=0x0000000000000000
    01368000000i[CPU0 ] | CR3=0x00306000 CR4=0x00000000
    01368000000i[CPU0 ] >> add esp, 0x00000010 : 83C410
    01368000000i[CMOS ] Last time is 1240781579 (Sun Apr 26 23:32:59 2009)
    01368000000i[     ] restoring default signal behavior
    01368000000i[CTRL ] quit_sim called with exit code 1
    

    This I did not understand:

    remove any calls to the scheduler and call it the way it will fault. So it will give us the right values.

    The timer_handler:

    void timer_handler(struct regs* r)
    {
        ++timer_ticks;
        if (eticks)
            --eticks;
    
        //TEST
        static ULONG c = 0;
        ++c;
        if(c>200)
        {
          task_switch();
          settextcolor(getpid(),0);
          c=0;
        }
        //TEST
    }
    


  • You see that task_switch() will never be called? you declare a variable, does 1 time incrmeent, and checks for 200?

    ///////////

    Ok, good.

    Try the same, but than in the assbly part itself, each time one line, until the GPF occurs. then we know which opcode makes the fault. (Maybe it is just the jump).

    If that fails, try getting all values of the registers when GPF triggers (print out all registers etc from the Registers given at the handler and post them, and maybe a disassembly of your schedule() (so we can exactly see when it trigger according to RIP's. We cant put cli hlt etc into the assembly part

    // PHPnerd


  • Mod

    PHPnerd schrieb:

    You see that task_switch() will never be called? you declare a variable, does 1 time incrmeent, and checks for 200?

    c is declared static, therefore it works:
    ..


Anmelden zum Antworten