添加printk

是时候有一个像样的打印工具了,在kernel\chr_drv目录下添加tty_io.c,将tty_table的定义,copy_to_cooked和do_tty_interrupt都移到这个文件中。

添加get_fs_byte函数是读取fs段内addr地址处(fs:[addr])一字节的数据,%0是返回的 寄存器变量_v;%1是内存地址addr,定义一个寄存器变量_v,该变量将被保存在一个寄存器中,以便提高访问和操作效率。

函数的主要作用是读取buf中的数据并put进tty->write_q队列,然后调用tty->write(tty)输出。


#include <linux/head.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/system.h>

struct tty_struct tty_table[] = {
	{
		con_write,
		{0,0,0,0,""},		/* console read-queue */
		{0,0,0,0,""},		/* console write-queue */
		{0,0,0,0,""}		/* console secondary queue */
	}
};
void copy_to_cooked(struct tty_struct * tty)
{
	signed char c;
	if(!EMPTY(tty->read_q) ){
		GETCH(tty->read_q,c);
		PUTCH(c,tty->write_q);
		tty->write(tty);
	}

}

void do_tty_interrupt(int tty)
{
	copy_to_cooked(tty_table+tty);
}

inline unsigned char get_fs_byte(const char * addr)
{
	unsigned register char _v;

	__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
	return _v;
}

int tty_write(unsigned channel, char * buf, int nr)
{
	static cr_flag=0;
	struct tty_struct * tty;
	char c, *b=buf;

	if (channel>2 || nr<0) return -1;
	tty = channel + tty_table;
	while(nr>0 && !FULL(tty->write_q)){	
		c=get_fs_byte(b);
		if(c=='\n')
			c=13;
		b++; nr--;
		PUTCH(c,tty->write_q);
	}

	tty->write(tty);


	return (b-buf);

}

头文件tty.h中添加

#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
#define FULL(a) (!LEFT(a))
void con_write(struct tty_struct * tty);


include中添加stdarg.h头文件
#ifndef STDARG_H
#define STDARG_H

typedef char *va_list;

#endif /* STDARG_H */



%4是base,eax中存放的是n,所以divl %4相当于%eax = n/base; %edx = n%base;

输出寄存器列表:"=a" (n),"=d" (__res),说明代码运行结束后将eax的值放到n中,将%ebx中的值放到__res中。

最后一行的__res;说明该表达式的输出值为__res

所以n中保存商,返回值为余数

C语言里规定:16bit程序中,返回值保存在ax寄存器中,32bit程序中,返回值保持在eax寄存器中,如果是64bit返回值,edx寄存器保存高32bit,eax寄存器保存低32bit

number是将整数num转换为相应的16进制数的字符串


#include <stdarg.h>

#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })
static char * number(char *str, int num, int base)
{
	char c,sign,tmp[36];
	int i=0;
	const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	

	if (num==0)
		tmp[i++]='0';
	else while (num!=0)
		tmp[i++]=digits[do_div(num,base)];

	while(i-->0)
		*str++ = tmp[i];

	return str;
}
typedef char *va_list;
int vsprintf(char *buf, const char *fmt, va_list args)
{
	int len;
	int i;
	char * str;
	
	for (str=buf ; *fmt ; ++fmt) {

		if (*fmt != '%') {
			*str++ = *fmt;
			continue;
		}

	*fmt++;


	switch (*fmt) {
		case 'x':
			//flags |= SMALL;
		case 'X':
			str=number(str,*((int *)args), 16);
			break;
		case 'd':
		case 'i':
			//flags |= SIGN;
		case 'u':
			str = number(str,*((int *)args), 10);
			break;
		}
	}

	*str = '\0';

	return str-buf;

}


在kernel文件夹下添加printk.c

C调用约定:函数后面的参数先入栈,并且由调用者清理堆栈。所以fmt最后入栈,&fmt+4为fmt后面一个参数的地址 。。。。。

#include <linux/kernel.h>
#include <stdarg.h>

static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...)
{
	
	int i;

	va_list args = (va_list)((char*)(&fmt)+4);
	i=vsprintf(buf,fmt,args);
	__asm__("push %%fs\n\t"
		"push %%ds\n\t"
		"pop %%fs\n\t"
		"pushl %0\n\t"
		"pushl $buf\n\t"
		"pushl $0\n\t"
		"call tty_write\n\t"
		"addl $8,%%esp\n\t"
		"popl %0\n\t"
		"pop %%fs"
		::"r" (i):"ax","cx","dx");
	return i;
}

kernel下的Makefile

# Makefile for the simple example kernel.
AS    =as
LD    =ld
LDFLAGS   = --oformat binary -N -e start -Ttext 0x0
CC    =gcc
CFLAGS	  = -I../include -fno-stack-protector

.c.s:
	$(CC) $(CFLAGS) \
	-S -o $*.s $<
.s.o:
	$(AS) -c -o $*.o $<
.c.o:
	$(CC) $(CFLAGS) \
	-c -o $*.o $<

OBJS  = asm.o kliba.o System_call.o chr_drv/chr_drv.o Traps.o sched.o printk.o vsprintf.o
kernel.o:$(OBJS)
	$(LD) -r -o kernel.o $(OBJS)
	sync

chr_drv/chr_drv.o:
	(cd chr_drv; make)

clean:
	rm -f  *.o *.s
	(cd chr_drv;make clean)


你可能感兴趣的:(添加printk)