Jump to content
  • Advertisement
Sign in to follow this  
Endar

Linux unfriendly assembly?

This topic is 4740 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working with a tutorial to create a basic text-based OS, and for some reason, this guy likes to use the command-line gcc on windows, and I'm starting to think that some of the assembly he gives might be windows specific (if that's possible). I'm using Fedora Core 3.
/**
 * Defines a GDT entry. We say 'packed' because it prevents the
 * compiler from doing things it thinks is best: Prevent
 * compiler 'optimization' by packing.
 */
struct gdt_entry
{
	unsigned short limit_low;
	unsigned short base_low;
	unsigned char base_middle;
	unsigned char access;
	unsigned char granularity;
	unsigned char base_high;
} __attribute__((packed));

/**
 * Special pointer that includes the limit: The max bytes
 * taken up by the GDT, minus 1. Again, this NEEDS to be packed.
 */
struct gdt_ptr
{
	unsigned short limit;
	unsigned int base;
} __attribute((packed));


struct gdt_entry gdt[3];	///< Our GDT, with 3 entries.
struct gdt_ptr gp;		///< Our special GDT pointer.

/**
 * This will be a new function in start.asm. We use this to properly
 * reload the new segment registers.
 */
extern void gdt_flush();
 
/**
 * Setup a descriptor in the Global Descriptor Table.
 */
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
	// Setup the descriptor base address
	gdt[num].base_low = (base & 0xffff);
	gdt[num].base_middle = (base >> 16) & 0xff;
	gdt[num].base_high = (base >> 24) & 0xff;

	// setup the descriptor limits
	gdt[num].limit_low = (limit & 0xffff);
	gdt[num].granularity = ((limit >> 16) & 0x0f);

	// finally setup the granularity and flags
	gdt[num].granularity |= (gran & 0xf0);
	gdt[num].access = access;
}

/**
 * Should be called by main. This will setup the special GDT
 * pointer, setup the first 3 entries in out GDT, and then finally
 * call gdt_flush() in out assembler file in order
 * to tell the processor where the new GDT is and update the
 * new segment registers.
 */
void gdt_install()
{
	// setup the GDT pointer and limit
	gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
	gp.base = (unsigned int)&gdt;

	// out NULL descriptor
	gdt_set_gate(0,0,0,0,0);

	/* The second entry is our code segment. The base address
	 * is 0, the limit is 4Gbytes, it uses 4Kbyte ganularity,
	 * uses 32-bit opcodes, and is a Code Segment descriptor.
	 * Please check the table above in the tutorial in order
	 * to see exactly what each value means.
	 */
	gdt_set_gate(1, 0, 0xffffffff, 0x9a, 0xcf);

	/* The third entry is our Data Segment. It's EXACTLY the
	 * same as our code segment, but the descriptor type in
	 * this entry's access byte says its a Data Segment
	 */
	gdt_set_gate(2, 0, 0xffffffff, 0x92, 0xcf);

	 // flush out the old GDT and install the new changes
	 gdt_flush();     /****** only call to gdt_flush ******/
}

; Loading the GDT right here!
; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global gdt_flush     ; Allows the C code to link to this
extern gp            ; Says that '_gp' is in another file
gdt_flush:
        lgdt [gp]        ; Load the GDT with our '_gp' which is a special pointer
        mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        jmp 0x08:flush2   ; 0x08 is the offset to our code segment: Far jump!
flush2:
        ret               ; Returns back to the C code!


I'm running the OS from a disk image, in bochs, and when I run it with the gdt installed, the OS keeps resetting. I've narrowed down the problem to the above assembly. Is there something in there that is unfriendly to linux? Or something that is just plain wrong?

Share this post


Link to post
Share on other sites
Advertisement
Seeing as the ASM in question is being run when neither windows or linux are loaded (well in the virtual machine BOCHS provides they're not loaded) you can rule out there being some incompatibility with linux, unless of course there's a linux specific bug in the assembler/compiler you are using which seems unlikely. So there's probably something wrong with the code itself.

How are you building it? You may have screwed something up in the build process. Also what do you mean by 'the OS keeps resetting'?

Share this post


Link to post
Share on other sites
Well after reading through the code you posted I can't see anything obviously wrong, my guess is you've screwed up the build process, probably in linking.

Share this post


Link to post
Share on other sites
That looks like Intel ASM Syntax, gas uses AT&T syntax, I think you want to assemble the second file with NASM instead of gcc.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I think your problem comes from the "ret" instruction.
Actually, this instruction is executed after the gdt is installed. So when the "ret" instruction tries to pop the registers' value from the stack, it find wrong values and your system crash.
You can't put the gdt loading code in a function.

Share this post


Link to post
Share on other sites
Seems reasonable - I don't think you can issue "ret" after rebuilding the memory map.

Instead, reset the stack pointer somewhere sensible (after making the seg registers are all sane), and JMP to your C code's entry point.

Oh yes - make sure interrupts are off before you even THINK about doing this :)

Mark

Share this post


Link to post
Share on other sites
Quote:
Original post by markr
Seems reasonable - I don't think you can issue "ret" after rebuilding the memory map.

Instead, reset the stack pointer somewhere sensible (after making the seg registers are all sane), and JMP to your C code's entry point.

Oh yes - make sure interrupts are off before you even THINK about doing this :)

Mark


Okay, well, here's my problem. When I started this, I was going to concentrate mostly on the C-code and not on the assembly, which is the reason for my next question: How do I do that?

Share this post


Link to post
Share on other sites
Well to reset the stack pointer you just do a mov sp, value. sti sets the interrupt flag, cli clears it (if the interrupt flag is not set then the cpu will not respond to interrupts. You'll also want to make sure your stack segment is setup as you want.

If you're writing an OS you'll need to take a look at the intel manuals here. The system programming guide contains a whole load of information that you'll need to know (e.g. how memory management works, task management, interrupts etc).

Share this post


Link to post
Share on other sites
When I have the section below of assembly instead of the section above, the OS doens't reset.


; Loading the GDT right here!
; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global gdt_flush ; Allows the C code to link to this
extern gp ; Says that '_gp' is in another file
gdt_flush:
;lgdt [gp] ; Load the GDT with our '_gp' which is a special pointer
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
;jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
jmp flush2
flush2:
ret ; Returns back to the C code!



So, granted, its not installing the new gdt, but, then its not resetting either. And, if I use the old jmp command: "jmp 0x08:flush2", bochs shuts down with a "fetch_raw_descriptor: LDTR.valid=0" message.

So, question: why would loading a new gdt make the computer reset?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!