How to Change CS Register in GCC Inline Assembly? [Solved]

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
blobmiester
Member
Member
Posts: 45
Joined: Fri Jul 16, 2010 9:49 am

How to Change CS Register in GCC Inline Assembly? [Solved]

Post by blobmiester »

While I was cleaning up my code, I noticed I was doing the follow for modifying the CS register (after installing the GDT):

Code: Select all

        __asm__ volatile ("movb %0, %%al\n"
                          "movb %%al, (do + 5)\n"
                          ""
                          "do:\n"
                          "ljmp $0x08, $flush\n"
                          ""
                          "flush:"
                          :: "r" (cs_register) 
                          : "%al");
I don't particularly like the self-modifying code but that is the only thing I could come up with at the time (to be able to set the CS register to a variable). It works. However, I'm wondering if anyone has any suggestions for improving it (or am I going about this the wrong way)?

Thanks for the help.

Oh, and I realize it might be better to use an external ASM function. But... I don't know. I would rather do this in inline assembly. If I can't come up anything else I'll move it to an external file. But I can hope, right? :)
Last edited by blobmiester on Sat Oct 16, 2010 4:52 pm, edited 1 time in total.
TylerH
Member
Member
Posts: 285
Joined: Tue Apr 13, 2010 8:00 pm
Contact:

Re: How to Change CS Register in GCC Inline Assembly?

Post by TylerH »

Code: Select all

__asm__ volatile ("ljmp %0, $flush\n"
                          "flush:"
                          :: "r" (cs_register)
                          : "%al");
Does that not work?
User avatar
blobmiester
Member
Member
Posts: 45
Joined: Fri Jul 16, 2010 9:49 am

Re: How to Change CS Register in GCC Inline Assembly?

Post by blobmiester »

Code: Select all

/tmp/ccpk8g0z.s: Assembler messages:
/tmp/ccpk8g0z.s:87: Error: suffix or operands invalid for `ljmp'
Sad Panda. When I add the immediate '$' in front of the '%0' it complains like the following:

Code: Select all

/tmp/ccZC5MeA.s: Assembler messages:
/tmp/ccZC5MeA.s:87: Error: illegal immediate register operand %dl
Apparently the ljmp segment needs to be a constant. Period. No "N" constraint. Nothing. (At least of what I tried). Meh.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: How to Change CS Register in GCC Inline Assembly?

Post by xenos »

TylerAnon wrote:

Code: Select all

__asm__ volatile ("ljmp %0, $flush\n"
                          "flush:"
                          :: "r" (cs_register)
                          : "%al");
This probably won't work because ljmp doesn't allow a general purpose register ("r") in here. But you could try an indirect far jump, i.e. store a far pointer in some variable and use it as a jump target. I'll try to figure out how to do that in inline assembly, but maybe you'll find out yourself in the meantime ;)

EDIT: Try this:

Code: Select all

struct {unsigned int offset; unsigned short segment;} dest;
dest.segment = 0x08; // whatever value you want in CS
asm volatile (
	"movl $1f, %0\n"
	"ljmp *%0\n"
	"1:" :: "m"(dest));
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to Change CS Register in GCC Inline Assembly?

Post by Owen »

Code: Select all

asm volatile(
        "push %[cs];"
        "push $1f;"
        "retf;"
        "1:"
    ::  [cs] "nr" (cs)
);
User avatar
blobmiester
Member
Member
Posts: 45
Joined: Fri Jul 16, 2010 9:49 am

Re: How to Change CS Register in GCC Inline Assembly?

Post by blobmiester »

Owen wrote:

Code: Select all

asm volatile(
        "push %[cs];"
        "push $1f;"
        "retf;"
        "1:"
    ::  [cs] "nr" (cs)
);
Damn. That looks awesome. I didn't even think of using retf. Focusing too much on trying to get ljmp to work.

Here is my final code, many thanks:

Code: Select all

        __asm__ volatile ("pushw %[cs]\n"
                          "pushl $1f\n"
                          "retf\n"
                          ""
                          "1:"
                          :: [cs] "nr" (cs_register));
XenOS: the indirect ljmp works too. Thanks. :)
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: How to Change CS Register in GCC Inline Assembly? [Solve

Post by xenos »

The retf method is probably the most elegant one ;) The only thing one needs to be careful about is the possibility of stack switches, so the old and new value of CS must have the same privilege level. Of course this is also true for the ljmp method, but in that case you simply get a #GP.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
qw
Member
Member
Posts: 792
Joined: Mon Jan 26, 2009 2:48 am

Re: How to Change CS Register in GCC Inline Assembly?

Post by qw »

blobmiester wrote:

Code: Select all

        __asm__ volatile ("pushw %[cs]\n"
                          "pushl $1f\n"
                          "retf\n"
                          ""
                          "1:"
                          :: [cs] "nr" (cs_register));
Don't use "pushw". Yes, CS is only 16 bits, but "retf" will pop 32 bits for the selector.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to Change CS Register in GCC Inline Assembly? [Solve

Post by Owen »

Yeah, you have to be careful about that. Pushw will make 16-bit stack slots that absolutely nothing wants, and will also cause alignment hell.
Post Reply