c switch statement

Programming, for all ages and all languages.
Post Reply
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

c switch statement

Post 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
User avatar
b.zaar
Member
Member
Posts: 294
Joined: Wed May 21, 2008 4:33 am
Location: Mars MTC +6:00
Contact:

Re: c switch statement

Post 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.
"God! Not Unix" - Richard Stallman

Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

Re: c switch statement

Post by teodori »

Ok so that means the problem is here:

Code: Select all

intr_20:
	movq $0x20, %rdi
	call kintr
	iretq
User avatar
b.zaar
Member
Member
Posts: 294
Joined: Wed May 21, 2008 4:33 am
Location: Mars MTC +6:00
Contact:

Re: c switch statement

Post 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.
"God! Not Unix" - Richard Stallman

Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

Re: c switch statement

Post 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?
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: c switch statement

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: c switch statement

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: c switch statement

Post 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?
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

Re: c switch statement

Post 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
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: c switch statement

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

Re: c switch statement

Post 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?
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: c switch statement

Post 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.
If a trainstation is where trains stop, what is a workstation ?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: c switch statement

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
teodori
Member
Member
Posts: 103
Joined: Wed Nov 14, 2012 4:55 pm

Re: c switch statement

Post 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.
Post Reply