Page 1 of 1

calls missing in action

Posted: Thu Jul 26, 2007 1:49 am
by enrico_granata
I am trying to write a syscall() routine for my kernel and map it onto INT 0x80
This is the main handler:

Code: Select all

void syscall(struct regs *r)
{
	int opcode = r->eax;
	int operand1 = r->ebx;
	int operand2 = r->ecx;
	switch(opcode)
	{
	case SYSCALL_PUTCHAR: // 1
		putch(operand1);
		break;
	case SYSCALL_REBOOT:
		raise_signal(SIGNAL_DOREBOOT);
		break;
	case SYSCALL_MORE_MEMORY:
		{
			ptr_t *ret_addr = (ptr_t*)((void*)operand2);
			*ret_addr = (ptr_t)sbrk(operand1);
		}
		break;
	default:
		break;
	}
}
and I am calling it through this other standard library routine

Code: Select all

void make_syscall(int syscall_code, int param1, int param2)
{
	__asm__ __volatile__ ("movl %0,%%eax" : : "m" (syscall_code));
	__asm__ __volatile__ ("movl %0,%%ebx" : : "m" (param1));
	__asm__ __volatile__ ("movl %0,%%ecx" : : "m" (param2));
	__asm__ __volatile__ ("int $0x80");
}
In printf() I use the following routine to output single characters to the screen

Code: Select all

void putchar(char c)
{
	make_syscall(1,c,0);
}
The issue, indeed a mysterious one, is that when I use printf's format codes (%i,%s,...) and the resulting string is longer than 1 character, only the first character really goes on the screen (all the rest is silently ignored). However, if I change putchar() to

Code: Select all

void putchar(char c)
{
                putch(c);
	make_syscall(1,c,0);
}
where putch() is the routine used by the system call that directly writes on video memory, all characters are written *twice*

Does anyone have any idea about this problem?

Thanks a lot,
Enrico[/code]

Posted: Thu Jul 26, 2007 2:01 am
by JamesM
Your putch() routine could be/is of interest - could you post it?

Posted: Thu Jul 26, 2007 2:05 am
by enrico_granata
I kept Bran's routine as I didn't see anything wrong with it (maybe not yet)

Code: Select all

void scroll(void)
{
    unsigned blank, temp;

    /* A blank is defined as a space... we need to give it
    *  backcolor too */
    blank = 0x20 | (attrib << 8);

    /* Row 25 is the end, this means we need to scroll up */
    if(csr_y >= 25)
    {
        /* Move the current text chunk that makes up the screen
        *  back in the buffer by a line */
        temp = csr_y - 25 + 1;
        memcpy (textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);

        /* Finally, we set the chunk of memory that occupies
        *  the last line of text to our 'blank' character */
        memsetw (textmemptr + (25 - temp) * 80, blank, 80);
        csr_y = 25 - 1;
    }
}

/* Updates the hardware cursor: the little blinking line
*  on the screen under the last character pressed! */
void move_csr(void)
{
    unsigned temp;

    /* The equation for finding the index in a linear
    *  chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    temp = csr_y * 80 + csr_x;

    /* This sends a command to indicies 14 and 15 in the
    *  CRT Control Register of the VGA controller. These
    *  are the high and low bytes of the index that show
    *  where the hardware cursor is to be 'blinking'. To
    *  learn more, you should look up some VGA specific
    *  programming documents. A great start to graphics:
    *  http://www.brackeen.com/home/vga */
    outportb(0x3D4, 14);
    outportb(0x3D5, temp >> 8);
    outportb(0x3D4, 15);
    outportb(0x3D5, temp);
}

/* Puts a single character on the screen */
void putch(strtype c)
{
    unsigned short *where;
    unsigned att = attrib << 8;

    /* Handle a backspace, by moving the cursor back one space */
    if(c == 0x08)
    {
        if(csr_x != 0) csr_x--;
    }
    /* Handles a tab by incrementing the cursor's x, but only
    *  to a point that will make it divisible by 8 */
    else if(c == 0x09)
    {
        csr_x = (csr_x + 8) & ~(8 - 1);
    }
    /* Handles a 'Carriage Return', which simply brings the
    *  cursor back to the margin */
    else if(c == '\r')
    {
        csr_x = 0;
    }
    /* We handle our newlines the way DOS and the BIOS do: we
    *  treat it as if a 'CR' was also there, so we bring the
    *  cursor to the margin and we increment the 'y' value */
    else if(c == '\n')
    {
        csr_x = 0;
        csr_y++;
    }
    /* Any character greater than and including a space, is a
    *  printable character. The equation for finding the index
    *  in a linear chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    else if(c >= ' ')
    {
        where = textmemptr + (csr_y * 80 + csr_x);
        *where = c | att;	/* Character AND attributes: color */
        csr_x++;
    }

    /* If the cursor has reached the edge of the screen's width, we
    *  insert a new line in there */
    if(csr_x >= 80)
    {
        csr_x = 0;
        csr_y++;
    }

    /* Scroll the screen if needed, and finally move the cursor */
    scroll();
    move_csr();
}

Posted: Thu Jul 26, 2007 2:11 am
by os64dev
and how about your printf code? as that is the one causing the problems

Posted: Thu Jul 26, 2007 2:17 am
by enrico_granata
This is used in a loop to output the characters inside a buffer:

Code: Select all

void printf_help( const char ch, char **string )
{
    UNUSED( string );

    // just output the character
    putchar(ch);

} // end printf_help
The reference to a char** is due to the fact that both printf() and sprintf() share a function pointer to (void)(char, char**)

Code: Select all

                    for ( i = 0; i < length; i++ )
                    {
                        // we printed another character
                        charsout++;

                        // print the string
                        func( string[i], &out_string );

                    } // end for
However, what really IS strange is that adding a putch() call inside putchar() instead of solely calling make_syscall() outputs *everything* properly twice

issue solved

Posted: Thu Jul 26, 2007 6:16 am
by enrico_granata
the problem was in make_syscall():

Code: Select all

void make_syscall(int syscall_code, int param1, int param2)
{
	__asm__ ("movl %0,%%eax" : : "m" (syscall_code));
	__asm__ ("movl %0,%%ebx" : : "m" (param1));
	__asm__ ("movl %0,%%ecx" : : "m" (param2));
	__asm__ ("int $0x80");
}
as GCC was using EBX as a counter inside a loop. Moving something else into it destroyed this counter and so the loop was never finished... Probably, calling putch() before making the syscall persuaded the compiler not to put the counter inside EBX...

This new version seems to work:

Code: Select all

void make_syscall(int syscall_code, int param1, int param2)
{

	int old_eax,old_ebx,old_ecx;

	/*
	* GCC may use the same registers that we use for its own motives, so save those
	* (failing to do so will cause all kind of mysterious bugs)
	*/
	__asm__ ("movl %%eax,%0" : "=m" (old_eax));
	__asm__ ("movl %%ebx,%0" : "=m" (old_ebx));
	__asm__ ("movl %%ecx,%0" : "=m" (old_ecx));

	/*
	* put parameters into registers and ask the kernel to do its deed
	* (we use INT 0x80 as Linux does)
	*/
	__asm__ ("movl %0,%%eax" : : "m" (syscall_code));
	__asm__ ("movl %0,%%ebx" : : "m" (param1));
	__asm__ ("movl %0,%%ecx" : : "m" (param2));
	__asm__ ("int $0x80");

	/*
	* restore previous registers values so GCC doesn't even imagine what we did
	* with 'em ;)
	*/
	__asm__ ("movl %0,%%eax" : : "m" (old_eax));
	__asm__ ("movl %0,%%ebx" : : "m" (old_ebx));
	__asm__ ("movl %0,%%ecx" : : "m" (old_ecx));

}
However, is there a way to tell GCC not to put local variables in registers?

Thanks,
Enrico

Posted: Thu Jul 26, 2007 6:30 am
by JamesM
No, you have no choice. You can do:

Code: Select all

asm volatile ("push %%eax"
                   "push %%ebx"
                   "push %%ecx"
                   "push %%edx"
                   "movl %0,%%eax"
                   "movl %1,%%ebx"
                   "movl %2,%%ecx"
                   "int $0x80"
                   "pop %%edx"
                   "pop %%ecx"
                   "pop %%ebx"
                   "pop %%eax" :: "m" (syscall_code),"m"(param1),"m"(param2)  );
However, the more generally accepted method is:

Code: Select all

asm volatile("int $0x80" : "=r"(return_val" :
                                     "a" (syscall_code),
                                     "b"(param1),
                                     "c"(param2) );
which will accomplish the same thing.

JamesM

Posted: Thu Jul 26, 2007 6:38 am
by enrico_granata
how do I return a value from the system call back to the user, however? I am using Bran's tutorial ISR handler:

Code: Select all

syscalls_stub:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    mov eax, _syscall
    call eax
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
so it just seems to me that every change _syscall (the true C handler for system calls) does, is wiped out

Posted: Thu Jul 26, 2007 6:56 am
by JamesM
You have to modify that handler. What I do is when _syscall returns, it places a value in ecx - either 0 or 1. 1 means the handler should return the value in eax (clobbering whatever the user had there originally). This is used in syscalls.

0 means don't return anything, be invisible. This is used in irq handlers etc.

so, you'd do something like:

Code: Select all

u32int _syscall(struct regs *r)
{
  // do syscall
  if (shouldreturn) asm volatile("mov $1, %ecx"); else asm volatile("mov $0, %ecx");
  return valtoreturn;
}

syscalls_stub:
    push ds
    push es
    push fs
    push gs
    push edx
    push ecx
    push ebx
    push eax
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    call _syscall
    cmp ecx, $0x1   ; should we clobber?
    jne .noclobber    ; no?
    pop ebx           ; pop the value that WAS eax, into ebx.
                           ; we don't care about ebx. It will be
                           ; clobbered in a few lines time.
    jmp .after

.noclobber:
    pop eax             ; pop the original eax out

.after:
    pop ebx
    pop ecx
    pop edx
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret

You have to take out that 'pusha/popa' because then you can't change the value of eax. You'd also have to change the layout of your 'struct regs' so eax et al. come FIRST instead of LAST.

Is that understandable? I'll recomment it nicer otherwise ;)

JamesM

Posted: Thu Jul 26, 2007 7:19 am
by urxae
JamesM wrote:You have to modify that handler. What I do is when _syscall returns, it places a value in ecx - either 0 or 1. 1 means the handler should return the value in eax (clobbering whatever the user had there originally). This is used in syscalls.

0 means don't return anything, be invisible. This is used in irq handlers etc.

so, you'd do something like:

Code: Select all

u32int _syscall(struct regs *r)
{
  // do syscall
  if (shouldreturn) asm volatile("mov $1, %ecx"); else asm volatile("mov $0, %ecx");
  return valtoreturn;
}

syscalls_stub:
[snip]
    call _syscall
    cmp ecx, $0x1   ; should we clobber?
    jne .noclobber    ; no?
    pop ebx           ; pop the value that WAS eax, into ebx.
                           ; we don't care about ebx. It will be
                           ; clobbered in a few lines time.
    jmp .after

.noclobber:
    pop eax             ; pop the original eax out

.after:
    pop ebx
[snip]
    iret
You have to take out that 'pusha/popa' because then you can't change the value of eax. You'd also have to change the layout of your 'struct regs' so eax et al. come FIRST instead of LAST.
Or you could just do this:

Code: Select all

void _syscall(struct regs *r)
{
  // do syscall
  r->eax = valtoreturn;
}
which changes the register value saved on the stack and then lets the normal exit code pop the modified version. This requires no special handling or checking in your syscalls_stub, it can just restore the registers from the stack. No need to change your struct layout either...

Posted: Thu Jul 26, 2007 7:22 am
by JamesM
urxae:

Ha! ah yes, you could! Lol, what a retarded way I was doing it - I'm going to have to change my codebase tonight!

D'OH!

JamesM

Posted: Thu Jul 26, 2007 9:53 am
by enrico_granata
thanks a lot :D I'm gonna try right now :!: