Page 1 of 1
c switch statement
Posted: Thu Dec 05, 2013 9:37 pm
by teodori
Hello, I cannot use
switch in my kernel. It causes a triple fault.
Code: Select all
switch(id){
case 0x20: // Timer
break;
case 0x21: // Keyboard
break;
case 0x22: // used internally by the two PICs, never raised
break;
case 0x23: // COM2
break;
case 0x24: // COM1
break;
case 0x25: // LPT2
break;
case 0x26: // Floppy Disk
break;
case 0x27: // LPT1 / Unreliable spurious interrupt
break;
case 0x28: // CMOS RTC
break;
case 0x29: // Free for peripherals / legacy SCSI / NIC
break;
case 0x2a: // Free for peripherals / SCSI / NIC
break;
case 0x2b: // Free for peripherals / SCSI / NIC
break;
case 0x2c: // Mouse
break;
case 0x2d: // FPU / Coprocessor / Inter-processor
break;
case 0x2e: // Primary ATA Channel
break;
case 0x2f: // Secondary ATA Channel
break;
default:
break;
}
Code: Select all
00005260957i[CPU0 ] 0x0000000000003027>> add byte ptr ds:[rbx], al : 0003
00005260957p[CPU0 ] >>PANIC<< exception(): 3rd (14) exception with no resolution
Re: c switch statement
Posted: Thu Dec 05, 2013 10:04 pm
by b.zaar
It probably wont be the switch statement but the code surrounding it. This looks like it's called from an interrupt so I'd check first that the interrupt was entering and returning correctly.
Re: c switch statement
Posted: Fri Dec 06, 2013 3:25 pm
by teodori
Ok so that means the problem is here:
Code: Select all
intr_20:
movq $0x20, %rdi
call kintr
iretq
Re: c switch statement
Posted: Fri Dec 06, 2013 6:53 pm
by b.zaar
teodori wrote:Ok so that means the problem is here:
Code: Select all
intr_20:
movq $0x20, %rdi
call kintr
iretq
It could be here, it could be kintr, or it could be that this isn't set properly in the IDT. There's a lot of things that happen before (if) you reach the switch statement.
Do some debugging.
1. Can you do a software interrupt?
2. Can you read the stack/parameters from a software interrupt?
3. Disable all IRQs except the keyboard. This is a device that you can trigger an interrupt on.
Re: c switch statement
Posted: Fri Dec 06, 2013 10:09 pm
by teodori
Ok I changed my code there:
Code: Select all
intr_20:
movq $0x20, %rdi
pushq %rdi
call kintr
popq %rdi
iretq
Fact is that if I use
if(),
else if() and
else, it works. What is the difference in gcc between
switch and
if?
Re: c switch statement
Posted: Sat Dec 07, 2013 5:10 am
by jnc100
When you call any C code you must assume that certain registers are trashed. Which these are depends on the particular ABI you are using. I would imagine that gcc encodes switch differently from if-else (probably using a jump table given that you use successive numbers in your case statement) and therefore trashes different registers. The fact that you are not preserving these registers anywhere within you interrupt handler means you will always run into problems.
In short, save all registers on entry to the interrupt handler and restore them in the opposite order at the end.
Regards,
John.
Re: c switch statement
Posted: Sat Dec 07, 2013 5:15 am
by Combuster
teodori wrote:Fact is that if I use if(), else if() and else, it works. What is the difference in gcc between switch and if?
The switch has exactly one expression to test whereas in if-elseif-elseif subsequent expressions are
not allowed to be evaluated if any of the previous one passes. This is a restriction dictated by the language, not gcc.
What GCC does add however is a specific optimisation to switch() statements. Instead of evaluating it once and writing out (shorter) elseif code for each case, it goes a step further and tries to put all the choices into one instruction so that almost every case is reached in the same amount of instructions: the jump table. It's a construct somewhat like the following:
Code: Select all
if (value < min || value > max)
{
default_case();
}
else
{
void (**switch_functions)(void) = {case_1, case_2, case_3, ...}; // note 1
switch_functions[value](); // note 2
}
...
default_case()
{
...
}
case_1()
{
...
}
The resulting assembly has two noticeable differences from the regular if-else code.
#1 (in this form) would suddenly add things to the .rodata or .data sections to store the preinitialized table, which in turn add relocations from data to code and vice versa while you might have lived with only one direction, and
#2 means that the code is no longer PIC in respect to the code section by default, because the position-independent version has additional overhead and has to be enabled explicitly.
This all gets you a huge additional correctness demand on your loading infrastructure thanks to one simple construct. Existing bugs suddenly get exposed that you might have gotten away with earlier. Which specific issue it is, is something you'll only properly figure out with a debugger.
Re: c switch statement
Posted: Sat Dec 07, 2013 3:42 pm
by Nable
teodori wrote:Ok I changed my code there:
Code: Select all
intr_20:
movq $0x20, %rdi
pushq %rdi
call kintr
popq %rdi
iretq
Fact is that if I use
if(),
else if() and
else, it works. What is the difference in gcc between
switch and
if?
Hey, your interrupt handler trashes RDI (and possibly other registers that are not saved by kintr), no applications expect this, so they can crash at any time.
It should be
Code: Select all
intr_20:
pushq %rdi
movq $0x20, %rdi
call kintr
popq %rdi
iretq
, shouldn't it?
Re: c switch statement
Posted: Sun Dec 08, 2013 7:47 am
by teodori
@Combuster:
You are right the .rodata gows when using switch(). And now I need to set the load address in my linker script? I got to look up in the binutils manpages how to do that correctly.
@Nable:
No C 64 calling convention says RDI contains the first argument and it must have a place on the stack.
http://wiki.osdev.org/Calling_Conventions
Re: c switch statement
Posted: Sun Dec 08, 2013 10:51 pm
by thepowersgang
Someone has hinted on this but nobody has actually addressed it yet.
YOU'RE TRASHING ALL REGISTERS
First thing you do in an interrupt handler is save state. This means pushing all registers that are modified within the handler (it's sometimes just as easy to push all GP regs). In this case, at least "rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11" should be saved (As these are defined as callee-modifiable in the calling convention)
This is VERY likely to be the root of your problem.
Re: c switch statement
Posted: Mon Dec 09, 2013 12:08 pm
by teodori
Ok I set the stack at 0x1ffffff to have 1MB of memory, then I pushed all registers before calling the c function and poped them back again.
Still not working, and RIP=0000000000003027 (is a part from my paging tables)! Could it be that the load address is incorrect?
Re: c switch statement
Posted: Mon Dec 09, 2013 1:55 pm
by gerryg400
teodori wrote:Ok I set the stack at 0x1ffffff to have 1MB of memory, then I pushed all registers before calling the c function and poped them back again.
Still not working, and RIP=0000000000003027 (is a part from my paging tables)! Could it be that the load address is incorrect?
How did you do the save. It can be tricky to save all regs without trashing anything at all.
Re: c switch statement
Posted: Mon Dec 09, 2013 2:04 pm
by Combuster
b.zaar wrote:Do some debugging
Combuster wrote:you'll only properly figure out with a debugger.
Debugger.
There, now it's been said three times. That should be enough.
Re: c switch statement
Posted: Tue Jan 14, 2014 5:36 pm
by teodori
Ok found out what caused the problem. It was during the linking process. I corrected my linker script, now it works.
Thank you.