James Molloy's Tutorial Known Bugs: Difference between revisions

Jump to navigation Jump to search
no edit summary
[unchecked revision][unchecked revision]
(Great. This hit me 4 years later.)
No edit summary
Line 94:
 
It is strongly recommended that you write your own implementation of this and disregard the tutorial. The tutorial attempts to implement forking kernel threads by searching for magic values on the stack, which is insanity. If you wish to create a new kernel thread, simply decide which registers it should have and point its stack pointer at its freshly allocated stack. It will then start executing at your desired entry point. The part where it disables paging is bad and you should just map the source and destination physical frames at appropriate virtual addresses and memcpy with paging on at all times. Section 9.3 in particular is insanity and has blown up at least one well-established hobby operating system.
 
=== Inline assembly optimiser problem with gcc 4.8 ===
As mentioned above, writing inline assembly can be tricky. The original inline assembly is this:
 
<nowiki>
asm volatile(" \
cli; \
mov %0, %%ecx; \
mov %1, %%esp; \
mov %2, %%ebp; \
mov %3, %%cr3; \
mov $0x12345, %%eax; \
sti; \
jmp *%%ecx "
: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr)); </nowiki>
 
Everything works fine when using gcc-4.2.4. However, the gcc-4.8.4 optimizer produces the following assembly (produced with '''objdump -d src/kernel'''):
<nowiki>
10387c: fa cli
10387d: 89 c1 mov %eax,%ecx
10387f: 89 d4 mov %edx,%esp
103881: 89 cd mov %ecx,%ebp
103883: 0f 22 db mov %ebx,%cr3
103886: b8 45 23 01 00 mov $0x12345,%eax
10388b: fb sti
10388c: ff e1 jmp *%ecx</nowiki>
 
Note how the eax register is assigned to the ecx register. However, later on the ecx register is assigned to ebp register. The reason for this is that the optimizer was using the eax register to store the eip variable and the ecx register to store the ebp variable. This results in the eip variable being assigned to the ecx register ''as well as'' the ebp register. This leads to a subsequent '''ret''' statement sending the cpu to some invalid memory location.
 
A way to fix this is to remove the inline assembly by, for example adding this to '''process.c''':
<nowiki>
; Here we:
; * Stop interrupts so we don't get interrupted.
; * Temporarily put the new EIP location in ECX.
; * Temporarily put the new page directory's physical address in EAX.
; * Set the base and stack pointers
; * Set the page directory
; * Put a dummy value (0x12345) in EAX so that above we can recognise that we've just
; switched task.
; * Restart interrupts. The STI instruction has a delay - it doesn't take effect until after
; the next instruction.
; * Jump to the location in ECX (remember we put the new EIP in there).
 
[GLOBAL perform_task_switch]
perform_task_switch:
cli;
mov ecx, [esp+4] ; eip
mov eax, [esp+8] ; physical address of current directory
mov ebp, [esp+12] ; ebp
mov esp, [esp+16] ; esp
mov cr3, eax ; set the page directory
mov eax, 0x12345 ; magic number to detect a task switch
sti;
jmp ecx</nowiki>
 
Then edit '''task.c''' and add this to the top of the file:
<nowiki>
extern void perform_task_switch(u32int, u32int, u32int, u32int);</nowiki>
and replace the inline assembly with:
<nowiki>
perform_task_switch(eip, current_directory->physicalAddr, ebp, esp);</nowiki>
 
== Conclusion ==
5

edits

Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu