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.