860 lines · 36.2 KB · C
RawDownload
/* * Procedures for drawing on the screen early on in the boot process. * * Benjamin Herrenschmidt <benh@kernel.crashing.org> */#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/utsrelease.h> #include <asm/sections.h>#include <asm/bootx.h>#include <asm/btext.h>#include <asm/prom.h>#include <asm/page.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/io.h>#include <asm/reg.h> #define NO_SCROLL #ifndef NO_SCROLLstatic void scrollscreen(void);#endif static void draw_byte(unsigned char c, long locX, long locY);static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb);static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb);static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb); static int g_loc_X;static int g_loc_Y;static int g_max_loc_X;static int g_max_loc_Y; unsigned long disp_BAT[2] __initdata = {0, 0}; #define cmapsz	(16*256) static unsigned char vga_font[cmapsz]; int boot_text_mapped;int force_printk_to_btext = 0; boot_infos_t disp_bi; extern char *klimit; /* * Powermac can use btext_* after boot for xmon, * chrp only uses it during early boot. */#ifdef CONFIG_XMON#define BTEXT#define BTDATA#else#define BTEXT	__init#define BTDATA	__initdata#endif /* CONFIG_XMON */ /* * This is called only when we are booted via BootX. */void __initbtext_init(boot_infos_t *bi){	g_loc_X = 0;	g_loc_Y = 0;	g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8;	g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16;	disp_bi = *bi;	boot_text_mapped = 1;} void __initbtext_welcome(void){	unsigned long flags;	unsigned long pvr;	boot_infos_t* bi = &disp_bi; 	btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n");	btext_drawstring("\nlinked at        : 0x");	btext_drawhex(KERNELBASE);	btext_drawstring("\nframe buffer at  : 0x");	btext_drawhex((unsigned long)bi->dispDeviceBase);	btext_drawstring(" (phys), 0x");	btext_drawhex((unsigned long)bi->logicalDisplayBase);	btext_drawstring(" (log)");	btext_drawstring("\nklimit           : 0x");	btext_drawhex((unsigned long)klimit);	btext_drawstring("\nMSR              : 0x");	__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));	btext_drawhex(flags);	__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));	pvr >>= 16;	if (pvr > 1) {	    btext_drawstring("\nHID0             : 0x");	    __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));	    btext_drawhex(flags);	}	if (pvr == 8 || pvr == 12 || pvr == 0x800c) {	    btext_drawstring("\nICTC             : 0x");	    __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));	    btext_drawhex(flags);	}	btext_drawstring("\n\n");} /* Calc BAT values for mapping the display and store them * in disp_BAT.  Those values are then used from head.S to map * the display during identify_machine() and MMU_Init() * * The display is mapped to virtual address 0xD0000000, rather * than 1:1, because some some CHRP machines put the frame buffer * in the region starting at 0xC0000000 (KERNELBASE). * This mapping is temporary and will disappear as soon as the * setup done by MMU_Init() is applied. * * For now, we align the BAT and then map 8Mb on 601 and 16Mb * on other PPCs. This may cause trouble if the framebuffer * is really badly aligned, but I didn't encounter this case * yet. */void __initbtext_prepare_BAT(void){	boot_infos_t* bi = &disp_bi;	unsigned long vaddr = KERNELBASE + 0x10000000;	unsigned long addr;	unsigned long lowbits; 	addr = (unsigned long)bi->dispDeviceBase;	if (!addr) {		boot_text_mapped = 0;		return;	}	if (PVR_VER(mfspr(SPRN_PVR)) != 1) {		/* 603, 604, G3, G4, ... */		lowbits = addr & ~0xFF000000UL;		addr &= 0xFF000000UL;		disp_BAT[0] = vaddr | (BL_16M<<2) | 2;		disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW);		} else {		/* 601 */		lowbits = addr & ~0xFF800000UL;		addr &= 0xFF800000UL;		disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4;		disp_BAT[1] = addr | BL_8M | 0x40;	}	bi->logicalDisplayBase = (void *) (vaddr + lowbits);} /* This function will enable the early boot text when doing OF booting. This * way, xmon output should work too */void __initbtext_setup_display(int width, int height, int depth, int pitch,		    unsigned long address){	boot_infos_t* bi = &disp_bi; 	g_loc_X = 0;	g_loc_Y = 0;	g_max_loc_X = width / 8;	g_max_loc_Y = height / 16;	bi->logicalDisplayBase = (unsigned char *)address;	bi->dispDeviceBase = (unsigned char *)address;	bi->dispDeviceRowBytes = pitch;	bi->dispDeviceDepth = depth;	bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;	bi->dispDeviceRect[2] = width;	bi->dispDeviceRect[3] = height;	boot_text_mapped = 1;} /* Here's a small text engine to use during early boot * or for debugging purposes * * todo: * *  - build some kind of vgacon with it to enable early printk *  - move to a separate file *  - add a few video driver hooks to keep in sync with display *    changes. */ voidmap_boot_text(void){	unsigned long base, offset, size;	boot_infos_t *bi = &disp_bi;	unsigned char *vbase; 	/* By default, we are no longer mapped */	boot_text_mapped = 0;	if (bi->dispDeviceBase == 0)		return;	base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL;	offset = ((unsigned long) bi->dispDeviceBase) - base;	size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset		+ bi->dispDeviceRect[0];	vbase = ioremap(base, size);	if (vbase == 0)		return;	bi->logicalDisplayBase = vbase + offset;	boot_text_mapped = 1;} /* Calc the base address of a given point (x,y) */static unsigned char * BTEXTcalc_base(boot_infos_t *bi, int x, int y){	unsigned char *base; 	base = bi->logicalDisplayBase;	if (base == 0)		base = bi->dispDeviceBase;	base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);	base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;	return base;} /* Adjust the display to a new resolution */voidbtext_update_display(unsigned long phys, int width, int height,		     int depth, int pitch){	boot_infos_t *bi = &disp_bi; 	if (bi->dispDeviceBase == 0)		return; 	/* check it's the same frame buffer (within 256MB) */	if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000)		return; 	bi->dispDeviceBase = (__u8 *) phys;	bi->dispDeviceRect[0] = 0;	bi->dispDeviceRect[1] = 0;	bi->dispDeviceRect[2] = width;	bi->dispDeviceRect[3] = height;	bi->dispDeviceDepth = depth;	bi->dispDeviceRowBytes = pitch;	if (boot_text_mapped) {		iounmap(bi->logicalDisplayBase);		boot_text_mapped = 0;	}	map_boot_text();	g_loc_X = 0;	g_loc_Y = 0;	g_max_loc_X = width / 8;	g_max_loc_Y = height / 16;} void BTEXT btext_clearscreen(void){	boot_infos_t* bi	= &disp_bi;	unsigned long *base	= (unsigned long *)calc_base(bi, 0, 0);	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *					(bi->dispDeviceDepth >> 3)) >> 2;	int i,j; 	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)	{		unsigned long *ptr = base;		for(j=width; j; --j)			*(ptr++) = 0;		base += (bi->dispDeviceRowBytes >> 2);	}} __inline__ void dcbst(const void* addr){	__asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));} void BTEXT btext_flushscreen(void){	boot_infos_t* bi	= &disp_bi;	unsigned long *base	= (unsigned long *)calc_base(bi, 0, 0);	unsigned long width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *					(bi->dispDeviceDepth >> 3)) >> 2;	int i,j; 	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)	{		unsigned long *ptr = base;		for(j=width; j>0; j-=8) {			dcbst(ptr);			ptr += 8;		}		base += (bi->dispDeviceRowBytes >> 2);	}} #ifndef NO_SCROLLstatic BTEXT voidscrollscreen(void){	boot_infos_t* bi		= &disp_bi;	unsigned long *src		= (unsigned long *)calc_base(bi,0,16);	unsigned long *dst		= (unsigned long *)calc_base(bi,0,0);	unsigned long width		= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *						(bi->dispDeviceDepth >> 3)) >> 2;	int i,j; #ifdef CONFIG_ADB_PMU	pmu_suspend();	/* PMU will not shut us down ! */#endif	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)	{		unsigned long *src_ptr = src;		unsigned long *dst_ptr = dst;		for(j=width; j; --j)			*(dst_ptr++) = *(src_ptr++);		src += (bi->dispDeviceRowBytes >> 2);		dst += (bi->dispDeviceRowBytes >> 2);	}	for (i=0; i<16; i++)	{		unsigned long *dst_ptr = dst;		for(j=width; j; --j)			*(dst_ptr++) = 0;		dst += (bi->dispDeviceRowBytes >> 2);	}#ifdef CONFIG_ADB_PMU	pmu_resume();	/* PMU will not shut us down ! */#endif}#endif /* ndef NO_SCROLL */ void BTEXT btext_drawchar(char c){	int cline = 0, x; 	if (!boot_text_mapped)		return; 	switch (c) {	case '\b':		if (g_loc_X > 0)			--g_loc_X;		break;	case '\t':		g_loc_X = (g_loc_X & -8) + 8;		break;	case '\r':		g_loc_X = 0;		break;	case '\n':		g_loc_X = 0;		g_loc_Y++;		cline = 1;		break;	default:		draw_byte(c, g_loc_X++, g_loc_Y);	}	if (g_loc_X >= g_max_loc_X) {		g_loc_X = 0;		g_loc_Y++;		cline = 1;	}#ifndef NO_SCROLL	while (g_loc_Y >= g_max_loc_Y) {		scrollscreen();		g_loc_Y--;	}#else	/* wrap around from bottom to top of screen so we don't	   waste time scrolling each line.  -- paulus. */	if (g_loc_Y >= g_max_loc_Y)		g_loc_Y = 0;	if (cline) {		for (x = 0; x < g_max_loc_X; ++x)			draw_byte(' ', x, g_loc_Y);	}#endif} void BTEXTbtext_drawstring(const char *c){	if (!boot_text_mapped)		return;	while (*c)		btext_drawchar(*c++);} void BTEXTbtext_drawhex(unsigned long v){	static char hex_table[] = "0123456789abcdef"; 	if (!boot_text_mapped)		return;	btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >>  8) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >>  4) & 0x0000000FUL]);	btext_drawchar(hex_table[(v >>  0) & 0x0000000FUL]);	btext_drawchar(' ');} static void BTEXTdraw_byte(unsigned char c, long locX, long locY){	boot_infos_t* bi	= &disp_bi;	unsigned char *base	= calc_base(bi, locX << 3, locY << 4);	unsigned char *font	= &vga_font[((unsigned long)c) * 16];	int rb			= bi->dispDeviceRowBytes; 	switch(bi->dispDeviceDepth) {	case 24:	case 32:		draw_byte_32(font, (unsigned long *)base, rb);		break;	case 15:	case 16:		draw_byte_16(font, (unsigned long *)base, rb);		break;	case 8:		draw_byte_8(font, (unsigned long *)base, rb);		break;	}} static unsigned long expand_bits_8[16] BTDATA = {	0x00000000,	0x000000ff,	0x0000ff00,	0x0000ffff,	0x00ff0000,	0x00ff00ff,	0x00ffff00,	0x00ffffff,	0xff000000,	0xff0000ff,	0xff00ff00,	0xff00ffff,	0xffff0000,	0xffff00ff,	0xffffff00,	0xffffffff}; static unsigned long expand_bits_16[4] BTDATA = {	0x00000000,	0x0000ffff,	0xffff0000,	0xffffffff};  static void BTEXTdraw_byte_32(unsigned char *font, unsigned long *base, int rb){	int l, bits;	int fg = 0xFFFFFFFFUL;	int bg = 0x00000000UL; 	for (l = 0; l < 16; ++l)	{		bits = *font++;		base[0] = (-(bits >> 7) & fg) ^ bg;		base[1] = (-((bits >> 6) & 1) & fg) ^ bg;		base[2] = (-((bits >> 5) & 1) & fg) ^ bg;		base[3] = (-((bits >> 4) & 1) & fg) ^ bg;		base[4] = (-((bits >> 3) & 1) & fg) ^ bg;		base[5] = (-((bits >> 2) & 1) & fg) ^ bg;		base[6] = (-((bits >> 1) & 1) & fg) ^ bg;		base[7] = (-(bits & 1) & fg) ^ bg;		base = (unsigned long *) ((char *)base + rb);	}} static void BTEXTdraw_byte_16(unsigned char *font, unsigned long *base, int rb){	int l, bits;	int fg = 0xFFFFFFFFUL;	int bg = 0x00000000UL;	unsigned long *eb = expand_bits_16; 	for (l = 0; l < 16; ++l)	{		bits = *font++;		base[0] = (eb[bits >> 6] & fg) ^ bg;		base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;		base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;		base[3] = (eb[bits & 3] & fg) ^ bg;		base = (unsigned long *) ((char *)base + rb);	}} static void BTEXT

Lines 1–500 of 860.