diff options
Diffstat (limited to 'arch/i386')
77 files changed, 4730 insertions, 4144 deletions
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 8770a5d0b14..b1b2b30b1b8 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -441,8 +441,8 @@ config X86_REBOOTFIXUPS this config is intended, is when reboot ends with a stalled/hung system. - Currently, the only fixup is for the Geode GX1/CS5530A/TROM2.1. - combination. + Currently, the only fixup is for the Geode machines using + CS5530A and CS5536 chipsets. Say Y if you want to enable the fixup. Currently, it's safe to enable this option even if you don't need it. @@ -541,7 +541,7 @@ config HIGHMEM4G config HIGHMEM64G bool "64GB" - depends on X86_CMPXCHG64 + depends on !M386 && !M486 help Select this if you have a 32-bit processor and more than 4 gigabytes of physical RAM. diff --git a/arch/i386/Kconfig.cpu b/arch/i386/Kconfig.cpu index d7f6fb0b30f..9cbe76c3aa3 100644 --- a/arch/i386/Kconfig.cpu +++ b/arch/i386/Kconfig.cpu @@ -299,7 +299,7 @@ config X86_POPAD_OK config X86_CMPXCHG64 bool - depends on !M386 && !M486 + depends on X86_PAE default y config X86_ALIGNMENT_16 @@ -344,8 +344,8 @@ config X86_CMOV depends on (MK7 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7) default y -config X86_MINIMUM_CPU_MODEL +config X86_MINIMUM_CPU_FAMILY int - default "4" if X86_XADD || X86_CMPXCHG || X86_BSWAP - default "0" + default "4" if X86_XADD || X86_CMPXCHG || X86_BSWAP || X86_WP_WORKS_OK + default "3" diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index bfbc32098a4..08678a0a3d1 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -25,27 +25,56 @@ SVGA_MODE := -DSVGA_MODE=NORMAL_VGA #RAMDISK := -DRAMDISK=512 -targets := vmlinux.bin bootsect bootsect.o \ - setup setup.o zImage bzImage +targets := vmlinux.bin setup.bin setup.elf zImage bzImage subdir- := compressed +setup-y += a20.o apm.o cmdline.o copy.o cpu.o cpucheck.o edd.o +setup-y += header.o main.o mca.o memory.o pm.o pmjump.o +setup-y += printf.o string.o tty.o video.o version.o voyager.o + +# The link order of the video-*.o modules can matter. In particular, +# video-vga.o *must* be listed first, followed by video-vesa.o. +# Hardware-specific drivers should follow in the order they should be +# probed, and video-bios.o should typically be last. +setup-y += video-vga.o +setup-y += video-vesa.o +setup-y += video-bios.o + hostprogs-y := tools/build HOSTCFLAGS_build.o := $(LINUXINCLUDE) # --------------------------------------------------------------------------- +# How to compile the 16-bit code. Note we always compile for -march=i386, +# that way we can complain to the user if the CPU is insufficient. +cflags-i386 := +cflags-x86_64 := -m32 +CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \ + $(cflags-$(ARCH)) \ + -Wall -Wstrict-prototypes \ + -march=i386 -mregparm=3 \ + -include $(srctree)/$(src)/code16gcc.h \ + -fno-strict-aliasing -fomit-frame-pointer \ + $(call cc-option, -ffreestanding) \ + $(call cc-option, -fno-toplevel-reorder,\ + $(call cc-option, -fno-unit-at-a-time)) \ + $(call cc-option, -fno-stack-protector) \ + $(call cc-option, -mpreferred-stack-boundary=2) +AFLAGS := $(CFLAGS) -D__ASSEMBLY__ + $(obj)/zImage: IMAGE_OFFSET := 0x1000 $(obj)/zImage: EXTRA_AFLAGS := $(SVGA_MODE) $(RAMDISK) $(obj)/bzImage: IMAGE_OFFSET := 0x100000 +$(obj)/bzImage: EXTRA_CFLAGS := -D__BIG_KERNEL__ $(obj)/bzImage: EXTRA_AFLAGS := $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__ $(obj)/bzImage: BUILDFLAGS := -b quiet_cmd_image = BUILD $@ -cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/bootsect $(obj)/setup \ +cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/setup.bin \ $(obj)/vmlinux.bin $(ROOT_DEV) > $@ -$(obj)/zImage $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \ +$(obj)/zImage $(obj)/bzImage: $(obj)/setup.bin \ $(obj)/vmlinux.bin $(obj)/tools/build FORCE $(call if_changed,image) @echo 'Kernel: $@ is ready' ' (#'`cat .version`')' @@ -53,12 +82,17 @@ $(obj)/zImage $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy) -LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary -LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext +SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) -$(obj)/setup $(obj)/bootsect: %: %.o FORCE +LDFLAGS_setup.elf := -T +$(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE $(call if_changed,ld) +OBJCOPYFLAGS_setup.bin := -O binary + +$(obj)/setup.bin: $(obj)/setup.elf FORCE + $(call if_changed,objcopy) + $(obj)/compressed/vmlinux: FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ diff --git a/arch/i386/boot/a20.c b/arch/i386/boot/a20.c new file mode 100644 index 00000000000..31348d054fc --- /dev/null +++ b/arch/i386/boot/a20.c @@ -0,0 +1,161 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/a20.c + * + * Enable A20 gate (return -1 on failure) + */ + +#include "boot.h" + +#define MAX_8042_LOOPS 100000 + +static int empty_8042(void) +{ + u8 status; + int loops = MAX_8042_LOOPS; + + while (loops--) { + io_delay(); + + status = inb(0x64); + if (status & 1) { + /* Read and discard input data */ + io_delay(); + (void)inb(0x60); + } else if (!(status & 2)) { + /* Buffers empty, finished! */ + return 0; + } + } + + return -1; +} + +/* Returns nonzero if the A20 line is enabled. The memory address + used as a test is the int $0x80 vector, which should be safe. */ + +#define A20_TEST_ADDR (4*0x80) +#define A20_TEST_SHORT 32 +#define A20_TEST_LONG 2097152 /* 2^21 */ + +static int a20_test(int loops) +{ + int ok = 0; + int saved, ctr; + + set_fs(0x0000); + set_gs(0xffff); + + saved = ctr = rdfs32(A20_TEST_ADDR); + + while (loops--) { + wrfs32(++ctr, A20_TEST_ADDR); + io_delay(); /* Serialize and make delay constant */ + ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr; + if (ok) + break; + } + + wrfs32(saved, A20_TEST_ADDR); + return ok; +} + +/* Quick test to see if A20 is already enabled */ +static int a20_test_short(void) +{ + return a20_test(A20_TEST_SHORT); +} + +/* Longer test that actually waits for A20 to come on line; this + is useful when dealing with the KBC or other slow external circuitry. */ +static int a20_test_long(void) +{ + return a20_test(A20_TEST_LONG); +} + +static void enable_a20_bios(void) +{ + asm volatile("pushfl; int $0x15; popfl" + : : "a" ((u16)0x2401)); +} + +static void enable_a20_kbc(void) +{ + empty_8042(); + + outb(0xd1, 0x64); /* Command write */ + empty_8042(); + + outb(0xdf, 0x60); /* A20 on */ + empty_8042(); +} + +static void enable_a20_fast(void) +{ + u8 port_a; + + port_a = inb(0x92); /* Configuration port A */ + port_a |= 0x02; /* Enable A20 */ + port_a &= ~0x01; /* Do not reset machine */ + outb(port_a, 0x92); +} + +/* + * Actual routine to enable A20; return 0 on ok, -1 on failure + */ + +#define A20_ENABLE_LOOPS 255 /* Number of times to try */ + +int enable_a20(void) +{ + int loops = A20_ENABLE_LOOPS; + +#if defined(CONFIG_X86_ELAN) + /* Elan croaks if we try to touch the KBC */ + enable_a20_fast(); + while (!a20_test_long()) + ; + return 0; +#elif defined(CONFIG_X86_VOYAGER) + /* On Voyager, a20_test() is unsafe? */ + enable_a20_kbc(); + return 0; +#else + while (loops--) { + /* First, check to see if A20 is already enabled + (legacy free, etc.) */ + if (a20_test_short()) + return 0; + + /* Next, try the BIOS (INT 0x15, AX=0x2401) */ + enable_a20_bios(); + if (a20_test_short()) + return 0; + + /* Try enabling A20 through the keyboard controller */ + empty_8042(); + if (a20_test_short()) + return 0; /* BIOS worked, but with delayed reaction */ + + enable_a20_kbc(); + if (a20_test_long()) + return 0; + + /* Finally, try enabling the "fast A20 gate" */ + enable_a20_fast(); + if (a20_test_long()) + return 0; + } + + return -1; +#endif +} diff --git a/arch/i386/boot/apm.c b/arch/i386/boot/apm.c new file mode 100644 index 00000000000..a34087c370c --- /dev/null +++ b/arch/i386/boot/apm.c @@ -0,0 +1,97 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * Original APM BIOS checking by Stephen Rothwell, May 1994 + * (sfr@canb.auug.org.au) + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/apm.c + * + * Get APM BIOS information + */ + +#include "boot.h" + +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) + +int query_apm_bios(void) +{ + u16 ax, bx, cx, dx, di; + u32 ebx, esi; + u8 err; + + /* APM BIOS installation check */ + ax = 0x5300; + bx = cx = 0; + asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0" + : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx) + : : "esi", "edi"); + + if (err) + return -1; /* No APM BIOS */ + + if (bx != 0x504d) /* "PM" signature */ + return -1; + + if (cx & 0x02) /* 32 bits supported? */ + return -1; + + /* Disconnect first, just in case */ + ax = 0x5304; + asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp" + : "+a" (ax) + : : "ebx", "ecx", "edx", "esi", "edi"); + + /* Paranoia */ + ebx = esi = 0; + cx = dx = di = 0; + + /* 32-bit connect */ + asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %6" + : "=a" (ax), "+b" (ebx), "+c" (cx), "+d" (dx), + "+S" (esi), "+D" (di), "=m" (err) + : "a" (0x5303)); + + boot_params.apm_bios_info.cseg = ax; + boot_params.apm_bios_info.offset = ebx; + boot_params.apm_bios_info.cseg_16 = cx; + boot_params.apm_bios_info.dseg = dx; + boot_params.apm_bios_info.cseg_len = (u16)esi; + boot_params.apm_bios_info.cseg_16_len = esi >> 16; + boot_params.apm_bios_info.dseg_len = di; + + if (err) + return -1; + + /* Redo the installation check as the 32-bit connect; + some BIOSes return different flags this way... */ + + ax = 0x5300; + bx = cx = 0; + asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0" + : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx) + : : "esi", "edi"); + + if (err || bx != 0x504d) { + /* Failure with 32-bit connect, try to disconect and ignore */ + ax = 0x5304; + bx = 0; + asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp" + : "+a" (ax), "+b" (bx) + : : "ecx", "edx", "esi", "edi"); + return -1; + } + + boot_params.apm_bios_info.version = ax; + boot_params.apm_bios_info.flags = cx; + return 0; +} + +#endif diff --git a/arch/i386/boot/bitops.h b/arch/i386/boot/bitops.h new file mode 100644 index 00000000000..8dcc8dc7db8 --- /dev/null +++ b/arch/i386/boot/bitops.h @@ -0,0 +1,45 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/bitops.h + * + * Very simple bitops for the boot code. + */ + +#ifndef BOOT_BITOPS_H +#define BOOT_BITOPS_H +#define _LINUX_BITOPS_H /* Inhibit inclusion of <linux/bitops.h> */ + +static inline int constant_test_bit(int nr, const void *addr) +{ + const u32 *p = (const u32 *)addr; + return ((1UL << (nr & 31)) & (p[nr >> 5])) != 0; +} +static inline int variable_test_bit(int nr, const void *addr) +{ + u8 v; + const u32 *p = (const u32 *)addr; + + asm("btl %2,%1; setc %0" : "=qm" (v) : "m" (*p), "Ir" (nr)); + return v; +} + +#define test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + constant_test_bit((nr),(addr)) : \ + variable_test_bit((nr),(addr))) + +static inline void set_bit(int nr, void *addr) +{ + asm("btsl %1,%0" : "+m" (*(u32 *)addr) : "Ir" (nr)); +} + +#endif /* BOOT_BITOPS_H */ diff --git a/arch/i386/boot/boot.h b/arch/i386/boot/boot.h new file mode 100644 index 00000000000..0329c4fe4f8 --- /dev/null +++ b/arch/i386/boot/boot.h @@ -0,0 +1,296 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/boot.h + * + * Header file for the real-mode kernel code + */ + +#ifndef BOOT_BOOT_H +#define BOOT_BOOT_H + +#ifndef __ASSEMBLY__ + +#include <stdarg.h> +#include <linux/types.h> +#include <linux/edd.h> +#include <asm/boot.h> +#include <asm/bootparam.h> + +/* Useful macros */ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +extern struct setup_header hdr; +extern struct boot_params boot_params; + +/* Basic port I/O */ +static inline void outb(u8 v, u16 port) +{ + asm volatile("outb %0,%1" : : "a" (v), "dN" (port)); +} +static inline u8 inb(u16 port) +{ + u8 v; + asm volatile("inb %1,%0" : "=a" (v) : "dN" (port)); + return v; +} + +static inline void outw(u16 v, u16 port) +{ + asm volatile("outw %0,%1" : : "a" (v), "dN" (port)); +} +static inline u16 inw(u16 port) +{ + u16 v; + asm volatile("inw %1,%0" : "=a" (v) : "dN" (port)); + return v; +} + +static inline void outl(u32 v, u16 port) +{ + asm volatile("outl %0,%1" : : "a" (v), "dn" (port)); +} +static inline u32 inl(u32 port) +{ + u32 v; + asm volatile("inl %1,%0" : "=a" (v) : "dN" (port)); + return v; +} + +static inline void io_delay(void) +{ + const u16 DELAY_PORT = 0x80; + asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT)); +} + +/* These functions are used to reference data in other segments. */ + +static inline u16 ds(void) +{ + u16 seg; + asm("movw %%ds,%0" : "=rm" (seg)); + return seg; +} + +static inline void set_fs(u16 seg) +{ + asm volatile("movw %0,%%fs" : : "rm" (seg)); +} +static inline u16 fs(void) +{ + u16 seg; + asm("movw %%fs,%0" : "=rm" (seg)); + return seg; +} + +static inline void set_gs(u16 seg) +{ + asm volatile("movw %0,%%gs" : : "rm" (seg)); +} +static inline u16 gs(void) +{ + u16 seg; + asm("movw %%gs,%0" : "=rm" (seg)); + return seg; +} + +typedef unsigned int addr_t; + +static inline u8 rdfs8(addr_t addr) +{ + u8 v; + asm("movb %%fs:%1,%0" : "=r" (v) : "m" (*(u8 *)addr)); + return v; +} +static inline u16 rdfs16(addr_t addr) +{ + u16 v; + asm("movw %%fs:%1,%0" : "=r" (v) : "m" (*(u16 *)addr)); + return v; +} +static inline u32 rdfs32(addr_t addr) +{ + u32 v; + asm("movl %%fs:%1,%0" : "=r" (v) : "m" (*(u32 *)addr)); + return v; +} + +static inline void wrfs8(u8 v, addr_t addr) +{ + asm volatile("movb %1,%%fs:%0" : "+m" (*(u8 *)addr) : "r" (v)); +} +static inline void wrfs16(u16 v, addr_t addr) +{ + asm volatile("movw %1,%%fs:%0" : "+m" (*(u16 *)addr) : "r" (v)); +} +static inline void wrfs32(u32 v, addr_t addr) +{ + asm volatile("movl %1,%%fs:%0" : "+m" (*(u32 *)addr) : "r" (v)); +} + +static inline u8 rdgs8(addr_t addr) +{ + u8 v; + asm("movb %%gs:%1,%0" : "=r" (v) : "m" (*(u8 *)addr)); + return v; +} +static inline u16 rdgs16(addr_t addr) +{ + u16 v; + asm("movw %%gs:%1,%0" : "=r" (v) : "m" (*(u16 *)addr)); + return v; +} +static inline u32 rdgs32(addr_t addr) +{ + u32 v; + asm("movl %%gs:%1,%0" : "=r" (v) : "m" (*(u32 *)addr)); + return v; +} + +static inline void wrgs8(u8 v, addr_t addr) +{ + asm volatile("movb %1,%%gs:%0" : "+m" (*(u8 *)addr) : "r" (v)); +} +static inline void wrgs16(u16 v, addr_t addr) +{ + asm volatile("movw %1,%%gs:%0" : "+m" (*(u16 *)addr) : "r" (v)); +} +static inline void wrgs32(u32 v, addr_t addr) +{ + asm volatile("movl %1,%%gs:%0" : "+m" (*(u32 *)addr) : "r" (v)); +} + +/* Note: these only return true/false, not a signed return value! */ +static inline int memcmp(const void *s1, const void *s2, size_t len) +{ + u8 diff; + asm("repe; cmpsb; setnz %0" + : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + return diff; +} + +static inline int memcmp_fs(const void *s1, addr_t s2, size_t len) +{ + u8 diff; + asm("fs; repe; cmpsb; setnz %0" + : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + return diff; +} +static inline int memcmp_gs(const void *s1, addr_t s2, size_t len) +{ + u8 diff; + asm("gs; repe; cmpsb; setnz %0" + : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + return diff; +} + +static inline int isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +/* Heap -- available for dynamic lists. */ +#define STACK_SIZE 512 /* Minimum number of bytes for stack */ + +extern char _end[]; +extern char *HEAP; +extern char *heap_end; +#define RESET_HEAP() ((void *)( HEAP = _end )) +static inline char *__get_heap(size_t s, size_t a, size_t n) +{ + char *tmp; + + HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); + tmp = HEAP; + HEAP += s*n; + return tmp; +} +#define GET_HEAP(type, n) \ + ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) + +static inline int heap_free(void) +{ + return heap_end-HEAP; +} + +/* copy.S */ + +void copy_to_fs(addr_t dst, void *src, size_t len); +void *copy_from_fs(void *dst, addr_t src, size_t len); +void copy_to_gs(addr_t dst, void *src, size_t len); +void *copy_from_gs(void *dst, addr_t src, size_t len); +void *memcpy(void *dst, void *src, size_t len); +void *memset(void *dst, int c, size_t len); + +#define memcpy(d,s,l) __builtin_memcpy(d,s,l) +#define memset(d,c,l) __builtin_memset(d,c,l) + +/* a20.c */ +int enable_a20(void); + +/* apm.c */ +int query_apm_bios(void); + +/* cmdline.c */ +int cmdline_find_option(const char *option, char *buffer, int bufsize); + +/* cpu.c, cpucheck.c */ +int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr); +int validate_cpu(void); + +/* edd.c */ +void query_edd(void); + +/* header.S */ +void __attribute__((noreturn)) die(void); + +/* mca.c */ +int query_mca(void); + +/* memory.c */ +int detect_memory(void); + +/* pm.c */ +void __attribute__((noreturn)) go_to_protected_mode(void); + +/* pmjump.S */ +void __attribute__((noreturn)) + protected_mode_jump(u32 entrypoint, u32 bootparams); + +/* printf.c */ +int sprintf(char *buf, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list args); +int printf(const char *fmt, ...); + +/* string.c */ +int strcmp(const char *str1, const char *str2); +size_t strnlen(const char *s, size_t maxlen); +unsigned int atou(const char *s); + +/* tty.c */ +void puts(const char *); +void putchar(int); +int getchar(void); +void kbd_flush(void); +int getchar_timeout(void); + +/* video.c */ +void set_video(void); + +/* video-vesa.c */ +void vesa_store_edid(void); + +/* voyager.c */ +int query_voyager(void); + +#endif /* __ASSEMBLY__ */ + +#endif /* BOOT_BOOT_H */ diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S deleted file mode 100644 index 011b7a4993d..00000000000 --- a/arch/i386/boot/bootsect.S +++ /dev/null @@ -1,98 +0,0 @@ -/* - * bootsect.S Copyright (C) 1991, 1992 Linus Torvalds - * - * modified by Drew Eckhardt - * modified by Bruce Evans (bde) - * modified by Chris Noe (May 1999) (as86 -> gas) - * gutted by H. Peter Anvin (Jan 2003) - * - * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment - * addresses must be multiplied by 16 to obtain their respective linear - * addresses. To avoid confusion, linear addresses are written using leading - * hex while segment addresses are written as segment:offset. - * - */ - -#include <asm/boot.h> - -SETUPSECTS = 4 /* default nr of setup-sectors */ -BOOTSEG = 0x07C0 /* original address of boot-sector */ -INITSEG = DEF_INITSEG /* we move boot here - out of the way */ -SETUPSEG = DEF_SETUPSEG /* setup starts here */ -SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ -SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ - /* to be loaded */ -ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */ -SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */ - -#ifndef SVGA_MODE -#define SVGA_MODE ASK_VGA -#endif - -#ifndef RAMDISK -#define RAMDISK 0 -#endif - -#ifndef ROOT_RDONLY -#define ROOT_RDONLY 1 -#endif - -.code16 -.text - -.global _start -_start: - - # Normalize the start address - jmpl $BOOTSEG, $start2 - -start2: - movw %cs, %ax - movw %ax, %ds - movw %ax, %es - movw %ax, %ss - movw $0x7c00, %sp - sti - cld - - movw $bugger_off_msg, %si - -msg_loop: - lodsb - andb %al, %al - jz die - movb $0xe, %ah - movw $7, %bx - int $0x10 - jmp msg_loop - -die: - # Allow the user to press a key, then reboot - xorw %ax, %ax - int $0x16 - int $0x19 - - # int 0x19 should never return. In case it does anyway, - # invoke the BIOS reset code... - ljmp $0xf000,$0xfff0 - - -bugger_off_msg: - .ascii "Direct booting from floppy is no longer supported.\r\n" - .ascii "Please use a boot loader program instead.\r\n" - .ascii "\n" - .ascii "Remove disk and press any key to reboot . . .\r\n" - .byte 0 - - - # Kernel attributes; used by setup - - .org 497 -setup_sects: .byte SETUPSECTS -root_flags: .word ROOT_RDONLY -syssize: .word SYSSIZE -swap_dev: .word SWAP_DEV -ram_size: .word RAMDISK -vid_mode: .word SVGA_MODE -root_dev: .word ROOT_DEV -boot_flag: .word 0xAA55 diff --git a/arch/i386/boot/cmdline.c b/arch/i386/boot/cmdline.c new file mode 100644 index 00000000000..34bb778c435 --- /dev/null +++ b/arch/i386/boot/cmdline.c @@ -0,0 +1,97 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/cmdline.c + * + * Simple command-line parser for early boot. + */ + +#include "boot.h" + +static inline int myisspace(u8 c) +{ + return c <= ' '; /* Close enough approximation */ +} + +/* + * Find a non-boolean option, that is, "option=argument". In accordance + * with standard Linux practice, if this option is repeated, this returns + * the last instance on the command line. + * + * Returns the length of the argument (regardless of if it was + * truncated to fit in the buffer), or -1 on not found. + */ +int cmdline_find_option(const char *option, char *buffer, int bufsize) +{ + u32 cmdline_ptr = boot_params.hdr.cmd_line_ptr; + addr_t cptr; + char c; + int len = -1; + const char *opptr = NULL; + char *bufptr = buffer; + enum { + st_wordstart, /* Start of word/after whitespace */ + st_wordcmp, /* Comparing this word */ + st_wordskip, /* Miscompare, skip */ + st_bufcpy /* Copying this to buffer */ + } state = st_wordstart; + + if (!cmdline_ptr || cmdline_ptr >= 0x100000) + return -1; /* No command line, or inaccessible */ + + cptr = cmdline_ptr & 0xf; + set_fs(cmdline_ptr >> 4); + + while (cptr < 0x10000 && (c = rdfs8(cptr++))) { + switch (state) { + case st_wordstart: + if (myisspace(c)) + break; + + /* else */ + state = st_wordcmp; + opptr = option; + /* fall through */ + + case st_wordcmp: + if (c == '=' && !*opptr) { + len = 0; + bufptr = buffer; + state = st_bufcpy; + } else if (myisspace(c)) { + state = st_wordstart; + } else if (c != *opptr++) { + state = st_wordskip; + } + break; + + case st_wordskip: + if (myisspace(c)) + state = st_wordstart; + break; + + case st_bufcpy: + if (myisspace(c)) { + state = st_wordstart; + } else { + if (len < bufsize-1) + *bufptr++ = c; + len++; + } + break; + } + } + + if (bufsize) + *bufptr = '\0'; + + return len; +} diff --git a/arch/i386/boot/code16gcc.h b/arch/i386/boot/code16gcc.h new file mode 100644 index 00000000000..3bd848093b9 --- /dev/null +++ b/arch/i386/boot/code16gcc.h @@ -0,0 +1,15 @@ +/* + * code16gcc.h + * + * This file is -include'd when compiling 16-bit C code. + * Note: this asm() needs to be emitted before gcc omits any code. + * Depending on gcc version, this requires -fno-unit-at-a-time or + * -fno-toplevel-reorder. + * + * Hopefully gcc will eventually have a real -m16 option so we can + * drop this hack long term. + */ + +#ifndef __ASSEMBLY__ +asm(".code16gcc"); +#endif diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index a661217f33e..189fa1dbefc 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -9,9 +9,14 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o \ EXTRA_AFLAGS := -traditional LDFLAGS_vmlinux := -T -CFLAGS_misc.o += -fPIC hostprogs-y := relocs +CFLAGS := -m32 -D__KERNEL__ $(LINUX_INCLUDE) -O2 \ + -fno-strict-aliasing -fPIC \ + $(call cc-option,-ffreestanding) \ + $(call cc-option,-fno-stack-protector) +LDFLAGS := -m elf_i386 + $(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE $(call if_changed,ld) @: diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S index 3517a32aaf4..f35ea223752 100644 --- a/arch/i386/boot/compressed/head.S +++ b/arch/i386/boot/compressed/head.S @@ -45,10 +45,10 @@ startup_32: * at and where we were actually loaded at. This can only be done * with a short local call on x86. Nothing else will tell us what * address we are running at. The reserved chunk of the real-mode - * data at 0x34-0x3f are used as the stack for this calculation. - * Only 4 bytes are needed. + * data at 0x1e4 (defined as a scratch field) are used as the stack + * for this calculation. Only 4 bytes are needed. */ - leal 0x40(%esi), %esp + leal (0x1e4+4)(%esi), %esp call 1f 1: popl %ebp subl $1b, %ebp diff --git a/arch/i386/boot/copy.S b/arch/i386/boot/copy.S new file mode 100644 index 00000000000..ef127e56a3c --- /dev/null +++ b/arch/i386/boot/copy.S @@ -0,0 +1,101 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/copy.S + * + * Memory copy routines + */ + + .code16gcc + .text + + .globl memcpy + .type memcpy, @function +memcpy: + pushw %si + pushw %di + movw %ax, %di + movw %dx, %si + pushw %cx + shrw $2, %cx + rep; movsl + popw %cx + andw $3, %cx + rep; movsb + popw %di + popw %si + ret + .size memcpy, .-memcpy + + .globl memset + .type memset, @function +memset: + pushw %di + movw %ax, %di + movzbl %dl, %eax + imull $0x01010101,%eax + pushw %cx + shrw $2, %cx + rep; stosl + popw %cx + andw $3, %cx + rep; stosb + popw %di + ret + .size memset, .-memset + + .globl copy_from_fs + .type copy_from_fs, @function +copy_from_fs: + pushw %ds + pushw %fs + popw %ds + call memcpy + popw %ds + ret + .size copy_from_fs, .-copy_from_fs + + .globl copy_to_fs + .type copy_to_fs, @function +copy_to_fs: + pushw %es + pushw %fs + popw %es + call memcpy + popw %es + ret + .size copy_to_fs, .-copy_to_fs + +#if 0 /* Not currently used, but can be enabled as needed */ + + .globl copy_from_gs + .type copy_from_gs, @function +copy_from_gs: + pushw %ds + pushw %gs + popw %ds + call memcpy + popw %ds + ret + .size copy_from_gs, .-copy_from_gs + .globl copy_to_gs + + .type copy_to_gs, @function +copy_to_gs: + pushw %es + pushw %gs + popw %es + call memcpy + popw %es + ret + .size copy_to_gs, .-copy_to_gs + +#endif diff --git a/arch/i386/boot/cpu.c b/arch/i386/boot/cpu.c new file mode 100644 index 00000000000..2a5c32da585 --- /dev/null +++ b/arch/i386/boot/cpu.c @@ -0,0 +1,69 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/cpu.c + * + * Check for obligatory CPU features and abort if the features are not + * present. + */ + +#include "boot.h" +#include "bitops.h" +#include <asm/cpufeature.h> + +static char *cpu_name(int level) +{ + static char buf[6]; + + if (level == 64) { + return "x86-64"; + } else { + sprintf(buf, "i%d86", level); + return buf; + } +} + +int validate_cpu(void) +{ + u32 *err_flags; + int cpu_level, req_level; + + check_cpu(&cpu_level, &req_level, &err_flags); + + if (cpu_level < req_level) { + printf("This kernel requires an %s CPU, ", + cpu_name(req_level)); + printf("but only detected an %s CPU.\n", + cpu_name(cpu_level)); + return -1; + } + + if (err_flags) { + int i, j; + puts("This kernel requires the following features " + "not present on the CPU:\n"); + + for (i = 0; i < NCAPINTS; i++) { + u32 e = err_flags[i]; + + for (j = 0; j < 32; j++) { + if (e & 1) + printf("%d:%d ", i, j); + + e >>= 1; + } + } + putchar('\n'); + return -1; + } else { + return 0; + } +} diff --git a/arch/i386/boot/cpucheck.c b/arch/i386/boot/cpucheck.c new file mode 100644 index 00000000000..8b0f4473b08 --- /dev/null +++ b/arch/i386/boot/cpucheck.c @@ -0,0 +1,267 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/cpucheck.c + * + * Check for obligatory CPU features and abort if the features are not + * present. This code should be compilable as 16-, 32- or 64-bit + * code, so be very careful with types and inline assembly. + * + * This code should not contain any messages; that requires an + * additional wrapper. + * + * As written, this code is not safe for inclusion into the kernel + * proper (after FPU initialization, in particular). + */ + +#ifdef _SETUP +# include "boot.h" +# include "bitops.h" +#endif +#include <linux/types.h> +#include <asm/cpufeature.h> +#include <asm/processor-flags.h> +#include <asm/required-features.h> +#include <asm/msr-index.h> + +struct cpu_features { + int level; /* Family, or 64 for x86-64 */ + int model; + u32 flags[NCAPINTS]; +}; + +static struct cpu_features cpu; +static u32 cpu_vendor[3]; +static u32 err_flags[NCAPINTS]; + +#ifdef CONFIG_X86_64 +static const int req_level = 64; +#elif defined(CONFIG_X86_MINIMUM_CPU_FAMILY) +static const int req_level = CONFIG_X86_MINIMUM_CPU_FAMILY; +#else +static const int req_level = 3; +#endif + +static const u32 req_flags[NCAPINTS] = +{ + REQUIRED_MASK0, + REQUIRED_MASK1, + REQUIRED_MASK2, + REQUIRED_MASK3, + REQUIRED_MASK4, + REQUIRED_MASK5, + REQUIRED_MASK6, + REQUIRED_MASK7, +}; + +#define A32(a,b,c,d) (((d) << 24)+((c) << 16)+((b) << 8)+(a)) + +static int is_amd(void) +{ + return cpu_vendor[0] == A32('A','u','t','h') && + cpu_vendor[1] == A32('e','n','t','i') && + cpu_vendor[2] == A32('c','A','M','D'); +} + +static int is_centaur(void) +{ + return cpu_vendor[0] == A32('C','e','n','t') && + cpu_vendor[1] == A32('a','u','r','H') && + cpu_vendor[2] == A32('a','u','l','s'); +} + +static int is_transmeta(void) +{ + return cpu_vendor[0] == A32('G','e','n','u') && + cpu_vendor[1] == A32('i','n','e','T') && + cpu_vendor[2] == A32('M','x','8','6'); +} + +static int has_fpu(void) +{ + u16 fcw = -1, fsw = -1; + u32 cr0; + + asm("movl %%cr0,%0" : "=r" (cr0)); + if (cr0 & (X86_CR0_EM|X86_CR0_TS)) { + cr0 &= ~(X86_CR0_EM|X86_CR0_TS); + asm volatile("movl %0,%%cr0" : : "r" (cr0)); + } + + asm("fninit ; fnstsw %0 ; fnstcw %1" : "+m" (fsw), "+m" (fcw)); + + return fsw == 0 && (fcw & 0x103f) == 0x003f; +} + +static int has_eflag(u32 mask) +{ + u32 f0, f1; + + asm("pushfl ; " + "pushfl ; " + "popl %0 ; " + "movl %0,%1 ; " + "xorl %2,%1 ; " + "pushl %1 ; " + "popfl ; " + "pushfl ; " + "popl %1 ; " + "popfl" + : "=r" (f0), "=r" (f1) + : "g" (mask)); + + return !!((f0^f1) & mask); +} + +static void get_flags(void) +{ + u32 max_intel_level, max_amd_level; + u32 tfms; + + if (has_fpu()) + set_bit(X86_FEATURE_FPU, cpu.flags); + + if (has_eflag(X86_EFLAGS_ID)) { + asm("cpuid" + : "=a" (max_intel_level), + "=b" (cpu_vendor[0]), + "=d" (cpu_vendor[1]), + "=c" (cpu_vendor[2]) + : "a" (0)); + + if (max_intel_level >= 0x00000001 && + max_intel_level <= 0x0000ffff) { + asm("cpuid" + : "=a" (tfms), + "=c" (cpu.flags[4]), + "=d" (cpu.flags[0]) + : "a" (0x00000001) + : "ebx"); + cpu.level = (tfms >> 8) & 15; + cpu.model = (tfms >> 4) & 15; + if (cpu.level >= 6) + cpu.model += ((tfms >> 16) & 0xf) << 4; + } + + asm("cpuid" + : "=a" (max_amd_level) + : "a" (0x80000000) + : "ebx", "ecx", "edx"); + + if (max_amd_level >= 0x80000001 && + max_amd_level <= 0x8000ffff) { + u32 eax = 0x80000001; + asm("cpuid" + : "+a" (eax), + "=c" (cpu.flags[6]), + "=d" (cpu.flags[1]) + : : "ebx"); + } + } +} + +/* Returns a bitmask of which words we have error bits in */ +static int check_flags(void) +{ + u32 err; + int i; + + err = 0; + for (i = 0; i < NCAPINTS; i++) { + err_flags[i] = req_flags[i] & ~cpu.flags[i]; + if (err_flags[i]) + err |= 1 << i; + } + + return err; +} + +/* + * Returns -1 on error. + * + * *cpu_level is set to the current CPU level; *req_level to the required + * level. x86-64 is considered level 64 for this purpose. + * + * *err_flags_ptr is set to the flags error array if there are flags missing. + */ +int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr) +{ + int err; + + memset(&cpu.flags, 0, sizeof cpu.flags); + cpu.level = 3; + + if (has_eflag(X86_EFLAGS_AC)) + cpu.level = 4; + + get_flags(); + err = check_flags(); + + if (test_bit(X86_FEATURE_LM, cpu.flags)) + cpu.level = 64; + + if (err == 0x01 && + !(err_flags[0] & + ~((1 << X86_FEATURE_XMM)|(1 << X86_FEATURE_XMM2))) && + is_amd()) { + /* If this is an AMD and we're only missing SSE+SSE2, try to + turn them on */ + + u32 ecx = MSR_K7_HWCR; + u32 eax, edx; + + asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); + eax &= ~(1 << 15); + asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); + + get_flags(); /* Make sure it really did something */ + err = check_flags(); + } else if (err == 0x01 && + !(err_flags[0] & ~(1 << X86_FEATURE_CX8)) && + is_centaur() && cpu.model >= 6) { + /* If this is a VIA C3, we might have to enable CX8 + explicitly */ + + u32 ecx = MSR_VIA_FCR; + u32 eax, edx; + + asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); + eax |= (1<<1)|(1<<7); + asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); + + set_bit(X86_FEATURE_CX8, cpu.flags); + err = check_flags(); + } else if (err == 0x01 && is_transmeta()) { + /* Transmeta might have masked feature bits in word 0 */ + + u32 ecx = 0x80860004; + u32 eax, edx; + u32 level = 1; + + asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); + asm("wrmsr" : : "a" (~0), "d" (edx), "c" (ecx)); + asm("cpuid" + : "+a" (level), "=d" (cpu.flags[0]) + : : "ecx", "ebx"); + asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); + + err = check_flags(); + } + + if (err_flags_ptr) + *err_flags_ptr = err ? err_flags : NULL; + if (cpu_level_ptr) + *cpu_level_ptr = cpu.level; + if (req_level_ptr) + *req_level_ptr = req_level; + + return (cpu.level < req_level || err) ? -1 : 0; +} diff --git a/arch/i386/boot/edd.S b/arch/i386/boot/edd.S deleted file mode 100644 index 34321368011..00000000000 --- a/arch/i386/boot/edd.S +++ /dev/null @@ -1,231 +0,0 @@ -/* - * BIOS Enhanced Disk Drive support - * Copyright (C) 2002, 2003, 2004 Dell, Inc. - * by Matt Domsch <Matt_Domsch@dell.com> October 2002 - * conformant to T13 Committee www.t13.org - * projects 1572D, 1484D, 1386D, 1226DT - * disk signature read by Matt Domsch <Matt_Domsch@dell.com> - * and Andrew Wilks <Andrew_Wilks@dell.com> September 2003, June 2004 - * legacy CHS retrieval by Patrick J. LoPresti <patl@users.sourceforge.net> - * March 2004 - * Command line option parsing, Matt Domsch, November 2004 - */ - -#include <linux/edd.h> -#include <asm/setup.h> - -#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) - -# It is assumed that %ds == INITSEG here - - movb $0, (EDD_MBR_SIG_NR_BUF) - movb $0, (EDDNR) - -# Check the command line for options: -# edd=of disables EDD completely (edd=off) -# edd=sk skips the MBR test (edd=skipmbr) -# edd=on re-enables EDD (edd=on) - - pushl %esi - movw $edd_mbr_sig_start, %di # Default to edd=on - - movl %cs:(cmd_line_ptr), %esi - andl %esi, %esi - jz old_cl # Old boot protocol? - -# Convert to a real-mode pointer in fs:si - movl %esi, %eax - shrl $4, %eax - movw %ax, %fs - andw $0xf, %si - jmp have_cl_pointer - -# Old-style boot protocol? -old_cl: - push %ds # aka INITSEG - pop %fs - - cmpw $0xa33f, (0x20) - jne done_cl # No command line at all? - movw (0x22), %si # Pointer relative to INITSEG - -# fs:si has the pointer to the command line now -have_cl_pointer: - -# Loop through kernel command line one byte at a time. Just in -# case the loader is buggy and failed to null-terminate the command line -# terminate if we get close enough to the end of the segment that we -# cannot fit "edd=XX"... -cl_atspace: - cmpw $-5, %si # Watch for segment wraparound - jae done_cl - movl %fs:(%si), %eax - andb %al, %al # End of line? - jz done_cl - cmpl $EDD_CL_EQUALS, %eax - jz found_edd_equals - cmpb $0x20, %al # <= space consider whitespace - ja cl_skipword - incw %si - jmp cl_atspace - -cl_skipword: - cmpw $-5, %si # Watch for segment wraparound - jae done_cl - movb %fs:(%si), %al # End of string? - andb %al, %al - jz done_cl - cmpb $0x20, %al - jbe cl_atspace - incw %si - jmp cl_skipword - -found_edd_equals: -# only looking at first two characters after equals -# late overrides early on the command line, so keep going after finding something - movw %fs:4(%si), %ax - cmpw $EDD_CL_OFF, %ax # edd=of - je do_edd_off - cmpw $EDD_CL_SKIP, %ax # edd=sk - je do_edd_skipmbr - cmpw $EDD_CL_ON, %ax # edd=on - je do_edd_on - jmp cl_skipword -do_edd_skipmbr: - movw $edd_start, %di - jmp cl_skipword -do_edd_off: - movw $edd_done, %di - jmp cl_skipword -do_edd_on: - movw $edd_mbr_sig_start, %di - jmp cl_skipword - -done_cl: - popl %esi - jmpw *%di - -# Read the first sector of each BIOS disk device and store the 4-byte signature -edd_mbr_sig_start: - movb $0x80, %dl # from device 80 - movw $EDD_MBR_SIG_BUF, %bx # store buffer ptr in bx -edd_mbr_sig_read: - movl $0xFFFFFFFF, %eax - movl %eax, (%bx) # assume failure - pushw %bx - movb $READ_SECTORS, %ah - movb $1, %al # read 1 sector - movb $0, %dh # at head 0 - movw $1, %cx # cylinder 0, sector 0 - pushw %es - pushw %ds - popw %es - movw $EDDBUF, %bx # disk's data goes into EDDBUF - pushw %dx # work around buggy BIOSes - stc # work around buggy BIOSes - int $0x13 - sti # work around buggy BIOSes - popw %dx - popw %es - popw %bx - jc edd_mbr_sig_done # on failure, we're done. - cmpb $0, %ah # some BIOSes do not set CF - jne edd_mbr_sig_done # on failure, we're done. - movl (EDDBUF+EDD_MBR_SIG_OFFSET), %eax # read sig out of the MBR - movl %eax, (%bx) # store success - incb (EDD_MBR_SIG_NR_BUF) # note that we stored something - incb %dl # increment to next device - addw $4, %bx # increment sig buffer ptr - cmpb $EDD_MBR_SIG_MAX, (EDD_MBR_SIG_NR_BUF) # Out of space? - jb edd_mbr_sig_read # keep looping -edd_mbr_sig_done: - -# Do the BIOS Enhanced Disk Drive calls -# This consists of two calls: -# int 13h ah=41h "Check Extensions Present" -# int 13h ah=48h "Get Device Parameters" -# int 13h ah=08h "Legacy Get Device Parameters" -# -# A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use -# in the boot_params at EDDBUF. The first four bytes of which are -# used to store the device number, interface support map and version -# results from fn41. The next four bytes are used to store the legacy -# cylinders, heads, and sectors from fn08. The following 74 bytes are used to -# store the results from fn48. Starting from device 80h, fn41, then fn48 -# are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE). -# Then the pointer is incremented to store the data for the next call. -# This repeats until either a device doesn't exist, or until EDDMAXNR -# devices have been stored. -# The one tricky part is that ds:si always points EDDEXTSIZE bytes into -# the structure, and the fn41 and fn08 results are stored at offsets -# from there. This removes the need to increment the pointer for -# every store, and leaves it ready for the fn48 call. -# A second one-byte buffer, EDDNR, in the boot_params stores -# the number of BIOS devices which exist, up to EDDMAXNR. -# In setup.c, copy_edd() stores both boot_params buffers away -# for later use, as they would get overwritten otherwise. -# This code is sensitive to the size of the structs in edd.h -edd_start: - # %ds points to the bootsector - # result buffer for fn48 - movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results - # kept just before that - movb $0x80, %dl # BIOS device 0x80 - -edd_check_ext: - movb $CHECKEXTENSIONSPRESENT, %ah # Function 41 - movw $EDDMAGIC1, %bx # magic - int $0x13 # make the call - jc edd_done # no more BIOS devices - - cmpw $EDDMAGIC2, %bx # is magic right? - jne edd_next # nope, next... - - movb %dl, %ds:-8(%si) # store device number - movb %ah, %ds:-7(%si) # store version - movw %cx, %ds:-6(%si) # store extensions - incb (EDDNR) # note that we stored something - -edd_get_device_params: - movw $EDDPARMSIZE, %ds:(%si) # put size - movw $0x0, %ds:2(%si) # work around buggy BIOSes - movb $GETDEVICEPARAMETERS, %ah # Function 48 - int $0x13 # make the call - # Don't check for fail return - # it doesn't matter. -edd_get_legacy_chs: - xorw %ax, %ax - movw %ax, %ds:-4(%si) - movw %ax, %ds:-2(%si) - # Ralf Brown's Interrupt List says to set ES:DI to - # 0000h:0000h "to guard against BIOS bugs" - pushw %es - movw %ax, %es - movw %ax, %di - pushw %dx # legacy call clobbers %dl - movb $LEGACYGETDEVICEPARAMETERS, %ah # Function 08 - int $0x13 # make the call - jc edd_legacy_done # failed - movb %cl, %al # Low 6 bits are max - andb $0x3F, %al # sector number - movb %al, %ds:-1(%si) # Record max sect - movb %dh, %ds:-2(%si) # Record max head number - movb %ch, %al # Low 8 bits of max cyl - shr $6, %cl - movb %cl, %ah # High 2 bits of max cyl - movw %ax, %ds:-4(%si) - -edd_legacy_done: - popw %dx - popw %es - movw %si, %ax # increment si - addw $EDDPARMSIZE+EDDEXTSIZE, %ax - movw %ax, %si - -edd_next: - incb %dl # increment to next device - cmpb $EDDMAXNR, (EDDNR) # Out of space? - jb edd_check_ext # keep looping - -edd_done: -#endif diff --git a/arch/i386/boot/edd.c b/arch/i386/boot/edd.c new file mode 100644 index 00000000000..25a282494f4 --- /dev/null +++ b/arch/i386/boot/edd.c @@ -0,0 +1,196 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/edd.c + * + * Get EDD BIOS disk information + */ + +#include "boot.h" +#include <linux/edd.h> + +#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) + +struct edd_dapa { + u8 pkt_size; + u8 rsvd; + u16 sector_cnt; + u16 buf_off, buf_seg; + u64 lba; + u64 buf_lin_addr; +}; + +/* + * Read the MBR (first sector) from a specific device. + */ +static int read_mbr(u8 devno, void *buf) +{ + struct edd_dapa dapa; + u16 ax, bx, cx, dx, si; + + memset(&dapa, 0, sizeof dapa); + dapa.pkt_size = sizeof(dapa); + dapa.sector_cnt = 1; + dapa.buf_off = (size_t)buf; + dapa.buf_seg = ds(); + /* dapa.lba = 0; */ + + ax = 0x4200; /* Extended Read */ + si = (size_t)&dapa; + dx = devno; + asm("pushfl; stc; int $0x13; setc %%al; popfl" + : "+a" (ax), "+S" (si), "+d" (dx) + : "m" (dapa) + : "ebx", "ecx", "edi", "memory"); + + if (!(u8)ax) + return 0; /* OK */ + + ax = 0x0201; /* Legacy Read, one sector */ + cx = 0x0001; /* Sector 0-0-1 */ + dx = devno; + bx = (size_t)buf; + asm("pushfl; stc; int $0x13; setc %%al; popfl" + : "+a" (ax), "+c" (cx), "+d" (dx), "+b" (bx) + : : "esi", "edi", "memory"); + + return -(u8)ax; /* 0 or -1 */ +} + +static u32 read_mbr_sig(u8 devno, struct edd_info *ei) +{ + int sector_size; + char *mbrbuf_ptr, *mbrbuf_end; + u32 mbrsig; + u32 buf_base, mbr_base; + extern char _end[]; + static char mbr_buf[1024]; + + sector_size = ei->params.bytes_per_sector; + if (!sector_size) + sector_size = 512; /* Best available guess */ + + buf_base = (ds() << 4) + (u32)&_end; + mbr_base = (buf_base+sector_size-1) & ~(sector_size-1); + mbrbuf_ptr = mbr_buf + (mbr_base-buf_base); + mbrbuf_end = mbrbuf_ptr + sector_size; + + if (!(boot_params.hdr.loadflags & CAN_USE_HEAP)) + return 0; + if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) + return 0; + + if (read_mbr(devno, mbrbuf_ptr)) + return 0; + + mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; + return mbrsig; +} + +static int get_edd_info(u8 devno, struct edd_info *ei) +{ + u16 ax, bx, cx, dx, di; + + memset(ei, 0, sizeof *ei); + + /* Check Extensions Present */ + + ax = 0x4100; + bx = EDDMAGIC1; + dx = devno; + asm("pushfl; stc; int $0x13; setc %%al; popfl" + : "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx) + : : "esi", "edi"); + + if ((u8)ax) + return -1; /* No extended information */ + + if (bx != EDDMAGIC2) + return -1; + + ei->device = devno; + ei->version = ax >> 8; /* EDD version number */ + ei->interface_support = cx; /* EDD functionality subsets */ + + /* Extended Get Device Parameters */ + + ei->params.length = sizeof(ei->params); + ax = 0x4800; + dx = devno; + asm("pushfl; int $0x13; popfl" + : "+a" (ax), "+d" (dx) + : "S" (&ei->params) + : "ebx", "ecx", "edi"); + + /* Get legacy CHS parameters */ + + /* Ralf Brown recommends setting ES:DI to 0:0 */ + ax = 0x0800; + dx = devno; + di = 0; + asm("pushw %%es; " + "movw %%di,%%es; " + "pushfl; stc; int $0x13; setc %%al; popfl; " + "popw %%es" + : "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di) + : : "esi"); + + if ((u8)ax == 0) { + ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2); + ei->legacy_max_head = dx >> 8; + ei->legacy_sectors_per_track = cx & 0x3f; + } + + return 0; +} + +void query_edd(void) +{ + char eddarg[8]; + int do_mbr = 1; + int do_edd = 1; + int devno; + struct edd_info ei, *edp; + + if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) { + if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) + do_mbr = 0; + else if (!strcmp(eddarg, "off")) + do_edd = 0; + } + + edp = (struct edd_info *)boot_params.eddbuf; + + if (!do_edd) + return; + + for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { + /* + * Scan the BIOS-supported hard disks and query EDD + * information... + */ + get_edd_info(devno, &ei); + + if (boot_params.eddbuf_entries < EDDMAXNR) { + memcpy(edp, &ei, sizeof ei); + edp++; + boot_params.eddbuf_entries++; + } + + if (do_mbr) { + u32 mbr_sig; + mbr_sig = read_mbr_sig(devno, &ei); + boot_params.edd_mbr_sig_buffer[devno-0x80] = mbr_sig; + } + } +} + +#endif diff --git a/arch/i386/boot/header.S b/arch/i386/boot/header.S new file mode 100644 index 00000000000..6b9923fb6ea --- /dev/null +++ b/arch/i386/boot/header.S @@ -0,0 +1,283 @@ +/* + * header.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Based on bootsect.S and setup.S + * modified by more people than can be counted + * + * Rewritten as a common file by H. Peter Anvin (Apr 2007) + * + * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment + * addresses must be multiplied by 16 to obtain their respective linear + * addresses. To avoid confusion, linear addresses are written using leading + * hex while segment addresses are written as segment:offset. + * + */ + +#include <asm/segment.h> +#include <linux/utsrelease.h> +#include <asm/boot.h> +#include <asm/e820.h> +#include <asm/page.h> +#include <asm/setup.h> +#include "boot.h" + +SETUPSECTS = 4 /* default nr of setup-sectors */ +BOOTSEG = 0x07C0 /* original address of boot-sector */ +SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ +SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ + /* to be loaded */ +ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */ +SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */ + +#ifndef SVGA_MODE +#define SVGA_MODE ASK_VGA +#endif + +#ifndef RAMDISK +#define RAMDISK 0 +#endif + +#ifndef ROOT_RDONLY +#define ROOT_RDONLY 1 +#endif + + .code16 + .section ".bstext", "ax" + + .global bootsect_start +bootsect_start: + + # Normalize the start address + ljmp $BOOTSEG, $start2 + +start2: + movw %cs, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + xorw %sp, %sp + sti + cld + + movw $bugger_off_msg, %si + +msg_loop: + lodsb + andb %al, %al + jz bs_die + movb $0xe, %ah + movw $7, %bx + int $0x10 + jmp msg_loop + +bs_die: + # Allow the user to press a key, then reboot + xorw %ax, %ax + int $0x16 + int $0x19 + + # int 0x19 should never return. In case it does anyway, + # invoke the BIOS reset code... + ljmp $0xf000,$0xfff0 + + .section ".bsdata", "a" +bugger_off_msg: + .ascii "Direct booting from floppy is no longer supported.\r\n" + .ascii "Please use a boot loader program instead.\r\n" + .ascii "\n" + .ascii "Remove disk and press any key to reboot . . .\r\n" + .byte 0 + + + # Kernel attributes; used by setup. This is part 1 of the + # header, from the old boot sector. + + .section ".header", "a" + .globl hdr +hdr: +setup_sects: .byte SETUPSECTS +root_flags: .word ROOT_RDONLY +syssize: .long SYSSIZE +ram_size: .word RAMDISK +vid_mode: .word SVGA_MODE +root_dev: .word ROOT_DEV +boot_flag: .word 0xAA55 + + # offset 512, entry point + + .globl _start +_start: + # Explicitly enter this as bytes, or the assembler + # tries to generate a 3-byte jump here, which causes + # everything else to push off to the wrong offset. + .byte 0xeb # short (2-byte) jump + .byte start_of_setup-1f +1: + + # Part 2 of the header, from the old setup.S + + .ascii "HdrS" # header signature + .word 0x0206 # header version number (>= 0x0105) + # or else old loadlin-1.5 will fail) + .globl realmode_swtch +realmode_swtch: .word 0, 0 # default_switch, SETUPSEG +start_sys_seg: .word SYSSEG + .word kernel_version-512 # pointing to kernel version string + # above section of header is compatible + # with loadlin-1.5 (header v1.5). Don't + # change it. + +type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, + # Bootlin, SYSLX, bootsect...) + # See Documentation/i386/boot.txt for + # assigned ids + +# flags, unused bits must be zero (RFU) bit within loadflags +loadflags: +LOADED_HIGH = 1 # If set, the kernel is loaded high +CAN_USE_HEAP = 0x80 # If set, the loader also has set + # heap_end_ptr to tell how much + # space behind setup.S can be used for + # heap purposes. + # Only the loader knows what is free +#ifndef __BIG_KERNEL__ + .byte 0 +#else + .byte LOADED_HIGH +#endif + +setup_move_size: .word 0x8000 # size to move, when setup is not + # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping + # into the kernel. However, only the + # loader knows how much data behind + # us also needs to be loaded. + +code32_start: # here loaders can put a different + # start address for 32-bit code. +#ifndef __BIG_KERNEL__ + .long 0x1000 # 0x1000 = default for zImage +#else + .long 0x100000 # 0x100000 = default for big kernel +#endif + +ramdisk_image: .long 0 # address of loaded ramdisk image + # Here the loader puts the 32-bit + # address where it loaded the image. + # This only will be read by the kernel. + +ramdisk_size: .long 0 # its size in bytes + +bootsect_kludge: + .long 0 # obsolete + +heap_end_ptr: .word _end+1024 # (Header version 0x0201 or later) + # space from here (exclusive) down to + # end of setup code can be used by setup + # for local heap purposes. + +pad1: .word 0 +cmd_line_ptr: .long 0 # (Header version 0x0202 or later) + # If nonzero, a 32-bit pointer + # to the kernel command line. + # The command line should be + # located between the start of + # setup and the end of low + # memory (0xa0000), or it may + # get overwritten before it + # gets read. If this field is + # used, there is no longer + # anything magical about the + # 0x90000 segment; the setup + # can be located anywhere in + # low memory 0x10000 or higher. + +ramdisk_max: .long (-__PAGE_OFFSET-(512 << 20)-1) & 0x7fffffff + # (Header version 0x0203 or later) + # The highest safe address for + # the contents of an initrd + +kernel_alignment: .long CONFIG_PHYSICAL_ALIGN #physical addr alignment + #required for protected mode + #kernel +#ifdef CONFIG_RELOCATABLE +relocatable_kernel: .byte 1 +#else +relocatable_kernel: .byte 0 +#endif +pad2: .byte 0 +pad3: .word 0 + +cmdline_size: .long COMMAND_LINE_SIZE-1 #length of the command line, + #added with boot protocol + #version 2.06 + +# End of setup header ##################################################### + + .section ".inittext", "ax" +start_of_setup: +#ifdef SAFE_RESET_DISK_CONTROLLER +# Reset the disk controller. + movw $0x0000, %ax # Reset disk controller + movb $0x80, %dl # All disks + int $0x13 +#endif + +# We will have entired with %cs = %ds+0x20, normalize %cs so +# it is on par with the other segments. + pushw %ds + pushw $setup2 + lretw + +setup2: +# Force %es = %ds + movw %ds, %ax + movw %ax, %es + cld + +# Stack paranoia: align the stack and make sure it is good +# for both 16- and 32-bit references. In particular, if we +# were meant to have been using the full 16-bit segment, the +# caller might have set %sp to zero, which breaks %esp-based +# references. + andw $~3, %sp # dword align (might as well...) + jnz 1f + movw $0xfffc, %sp # Make sure we're not zero +1: movzwl %sp, %esp # Clear upper half of %esp + sti + +# Check signature at end of setup + cmpl $0x5a5aaa55, setup_sig + jne setup_bad + +# Zero the bss + movw $__bss_start, %di + movw $_end+3, %cx + xorl %eax, %eax + subw %di, %cx + shrw $2, %cx + rep; stosl + +# Jump to C code (should not return) + calll main + +# Setup corrupt somehow... +setup_bad: + movl $setup_corrupt, %eax + calll puts + # Fall through... + + .globl die + .type die, @function +die: + hlt + jmp die + + .size die, .-due + + .section ".initdata", "a" +setup_corrupt: + .byte 7 + .string "No setup signature found..." diff --git a/arch/i386/boot/main.c b/arch/i386/boot/main.c new file mode 100644 index 00000000000..7f01f96c4fb --- /dev/null +++ b/arch/i386/boot/main.c @@ -0,0 +1,161 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/main.c + * + * Main module for the real-mode kernel code + */ + +#include "boot.h" + +struct boot_params boot_params __attribute__((aligned(16))); + +char *HEAP = _end; +char *heap_end = _end; /* Default end of heap = no heap */ + +/* + * Copy the header into the boot parameter block. Since this + * screws up the old-style command line protocol, adjust by + * filling in the new-style command line pointer instead. + */ +#define OLD_CL_MAGIC 0xA33F +#define OLD_CL_ADDRESS 0x20 + +static void copy_boot_params(void) +{ + struct old_cmdline { + u16 cl_magic; + u16 cl_offset; + }; + const struct old_cmdline * const oldcmd = + (const struct old_cmdline *)OLD_CL_ADDRESS; + + BUILD_BUG_ON(sizeof boot_params != 4096); + memcpy(&boot_params.hdr, &hdr, sizeof hdr); + + if (!boot_params.hdr.cmd_line_ptr && + oldcmd->cl_magic == OLD_CL_MAGIC) { + /* Old-style command line protocol. */ + u16 cmdline_seg; + + /* Figure out if the command line falls in the region + of memory that an old kernel would have copied up + to 0x90000... */ + if (oldcmd->cl_offset < boot_params.hdr.setup_move_size) + cmdline_seg = ds(); + else + cmdline_seg = 0x9000; + + boot_params.hdr.cmd_line_ptr = + (cmdline_seg << 4) + oldcmd->cl_offset; + } +} + +/* + * Set the keyboard repeat rate to maximum. Unclear why this + * is done here; this might be possible to kill off as stale code. + */ +static void keyboard_set_repeat(void) +{ + u16 ax = 0x0305; + u16 bx = 0; + asm volatile("int $0x16" + : "+a" (ax), "+b" (bx) + : : "ecx", "edx", "esi", "edi"); +} + +/* + * Get Intel SpeedStep IST information. + */ +static void query_speedstep_ist(void) +{ + asm("int $0x15" + : "=a" (boot_params.speedstep_info[0]), + "=b" (boot_params.speedstep_info[1]), + "=c" (boot_params.speedstep_info[2]), + "=d" (boot_params.speedstep_info[3]) + : "a" (0x0000e980), /* IST Support */ + "d" (0x47534943)); /* Request value */ +} + +/* + * Tell the BIOS what CPU mode we intend to run in. + */ +static void set_bios_mode(void) +{ +#ifdef CONFIG_X86_64 + u32 eax, ebx; + + eax = 0xec00; + ebx = 2; + asm volatile("int $0x15" + : "+a" (eax), "+b" (ebx) + : : "ecx", "edx", "esi", "edi"); +#endif +} + +void main(void) +{ + /* First, copy the boot header into the "zeropage" */ + copy_boot_params(); + + /* End of heap check */ + if (boot_params.hdr.loadflags & CAN_USE_HEAP) { + heap_end = (char *)(boot_params.hdr.heap_end_ptr + +0x200-STACK_SIZE); + } else { + /* Boot protocol 2.00 only, no heap available */ + puts("WARNING: Ancient bootloader, some functionality " + "may be limited!\n"); + } + + /* Make sure we have all the proper CPU support */ + if (validate_cpu()) { + puts("Unable to boot - please use a kernel appropriate " + "for your CPU.\n"); + die(); + } + + /* Tell the BIOS what CPU mode we intend to run in. */ + set_bios_mode(); + + /* Detect memory layout */ + detect_memory(); + + /* Set keyboard repeat rate (why?) */ + keyboard_set_repeat(); + + /* Set the video mode */ + set_video(); + + /* Query MCA information */ + query_mca(); + + /* Voyager */ +#ifdef CONFIG_X86_VOYAGER + query_voyager(); +#endif + + /* Query SpeedStep IST information */ + query_speedstep_ist(); + + /* Query APM information */ +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) + query_apm_bios(); +#endif + + /* Query EDD information */ +#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) + query_edd(); +#endif + /* Do the last things and invoke protected mode */ + go_to_protected_mode(); +} diff --git a/arch/i386/boot/mca.c b/arch/i386/boot/mca.c new file mode 100644 index 00000000000..9b68bd1aef1 --- /dev/null +++ b/arch/i386/boot/mca.c @@ -0,0 +1,43 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/mca.c + * + * Get the MCA system description table + */ + +#include "boot.h" + +int query_mca(void) +{ + u8 err; + u16 es, bx, len; + + asm("pushw %%es ; " + "int $0x15 ; " + "setc %0 ; " + "movw %%es, %1 ; " + "popw %%es" + : "=acdSDm" (err), "=acdSDm" (es), "=b" (bx) + : "a" (0xc000)); + + if (err) + return -1; /* No MCA present */ + + set_fs(es); + len = rdfs16(bx); + + if (len > sizeof(boot_params.sys_desc_table)) + len = sizeof(boot_params.sys_desc_table); + + copy_from_fs(&boot_params.sys_desc_table, bx, len); + return 0; +} diff --git a/arch/i386/boot/memory.c b/arch/i386/boot/memory.c new file mode 100644 index 00000000000..1a2e62db8be --- /dev/null +++ b/arch/i386/boot/memory.c @@ -0,0 +1,99 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/memory.c + * + * Memory detection code + */ + +#include "boot.h" + +#define SMAP 0x534d4150 /* ASCII "SMAP" */ + +static int detect_memory_e820(void) +{ + u32 next = 0; + u32 size, id; + u8 err; + struct e820entry *desc = boot_params.e820_map; + + do { + size = sizeof(struct e820entry); + id = SMAP; + asm("int $0x15; setc %0" + : "=am" (err), "+b" (next), "+d" (id), "+c" (size), + "=m" (*desc) + : "D" (desc), "a" (0xe820)); + + if (err || id != SMAP) + break; + + boot_params.e820_entries++; + desc++; + } while (next && boot_params.e820_entries < E820MAX); + + return boot_params.e820_entries; +} + +static int detect_memory_e801(void) +{ + u16 ax, bx, cx, dx; + u8 err; + + bx = cx = dx = 0; + ax = 0xe801; + asm("stc; int $0x15; setc %0" + : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); + + if (err) + return -1; + + /* Do we really need to do this? */ + if (cx || dx) { + ax = cx; + bx = dx; + } + + if (ax > 15*1024) + return -1; /* Bogus! */ + + /* This ignores memory above 16MB if we have a memory hole + there. If someone actually finds a machine with a memory + hole at 16MB and no support for 0E820h they should probably + generate a fake e820 map. */ + boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax; + + return 0; +} + +static int detect_memory_88(void) +{ + u16 ax; + u8 err; + + ax = 0x8800; + asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); + + boot_params.screen_info.ext_mem_k = ax; + + return -err; +} + +int detect_memory(void) +{ + if (detect_memory_e820() > 0) + return 0; + + if (!detect_memory_e801()) + return 0; + + return detect_memory_88(); +} diff --git a/arch/i386/boot/pm.c b/arch/i386/boot/pm.c new file mode 100644 index 00000000000..3fa53e15ed7 --- /dev/null +++ b/arch/i386/boot/pm.c @@ -0,0 +1,170 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/pm.c + * + * Prepare the machine for transition to protected mode. + */ + +#include "boot.h" +#include <asm/segment.h> + +/* + * Invoke the realmode switch hook if present; otherwise + * disable all interrupts. + */ +static void realmode_switch_hook(void) +{ + if (boot_params.hdr.realmode_swtch) { + asm volatile("lcallw *%0" + : : "m" (boot_params.hdr.realmode_swtch) + : "eax", "ebx", "ecx", "edx"); + } else { + asm volatile("cli"); + outb(0x80, 0x70); /* Disable NMI */ + io_delay(); + } +} + +/* + * A zImage kernel is loaded at 0x10000 but wants to run at 0x1000. + * A bzImage kernel is loaded and runs at 0x100000. + */ +static void move_kernel_around(void) +{ + /* Note: rely on the compile-time option here rather than + the LOADED_HIGH flag. The Qemu kernel loader unconditionally + sets the loadflags to zero. */ +#ifndef __BIG_KERNEL__ + u16 dst_seg, src_seg; + u32 syssize; + + dst_seg = 0x1000 >> 4; + src_seg = 0x10000 >> 4; + syssize = boot_params.hdr.syssize; /* Size in 16-byte paragraphs */ + + while (syssize) { + int paras = (syssize >= 0x1000) ? 0x1000 : syssize; + int dwords = paras << 2; + + asm volatile("pushw %%es ; " + "pushw %%ds ; " + "movw %1,%%es ; " + "movw %2,%%ds ; " + "xorw %%di,%%di ; " + "xorw %%si,%%si ; " + "rep;movsl ; " + "popw %%ds ; " + "popw %%es" + : "+c" (dwords) + : "rm" (dst_seg), "rm" (src_seg) + : "esi", "edi"); + + syssize -= paras; + dst_seg += paras; + src_seg += paras; + } +#endif +} + +/* + * Disable all interrupts at the legacy PIC. + */ +static void mask_all_interrupts(void) +{ + outb(0xff, 0xa1); /* Mask all interrupts on the seconday PIC */ + io_delay(); + outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */ + io_delay(); +} + +/* + * Reset IGNNE# if asserted in the FPU. + */ +static void reset_coprocessor(void) +{ + outb(0, 0xf0); + io_delay(); + outb(0, 0xf1); + io_delay(); +} + +/* + * Set up the GDT + */ +#define GDT_ENTRY(flags,base,limit) \ + (((u64)(base & 0xff000000) << 32) | \ + ((u64)flags << 40) | \ + ((u64)(limit & 0x00ff0000) << 32) | \ + ((u64)(base & 0x00ffff00) << 16) | \ + ((u64)(limit & 0x0000ffff))) + +struct gdt_ptr { + u16 len; + u32 ptr; +} __attribute__((packed)); + +static void setup_gdt(void) +{ + /* There are machines which are known to not boot with the GDT + being 8-byte unaligned. Intel recommends 16 byte alignment. */ + static const u64 boot_gdt[] __attribute__((aligned(16))) = { + /* CS: code, read/execute, 4 GB, base 0 */ + [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), + /* DS: data, read/write, 4 GB, base 0 */ + [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), + }; + struct gdt_ptr gdt; + + gdt.len = sizeof(boot_gdt)-1; + gdt.ptr = (u32)&boot_gdt + (ds() << 4); + + asm volatile("lgdtl %0" : : "m" (gdt)); +} + +/* + * Set up the IDT + */ +static void setup_idt(void) +{ + static const struct gdt_ptr null_idt = {0, 0}; + asm volatile("lidtl %0" : : "m" (null_idt)); +} + +/* + * Actual invocation sequence + */ +void go_to_protected_mode(void) +{ + /* Hook before leaving real mode, also disables interrupts */ + realmode_switch_hook(); + + /* Move the kernel/setup to their final resting places */ + move_kernel_around(); + + /* Enable the A20 gate */ + if (enable_a20()) { + puts("A20 gate not responding, unable to boot...\n"); + die(); + } + + /* Reset coprocessor (IGNNE#) */ + reset_coprocessor(); + + /* Mask all interrupts in the PIC */ + mask_all_interrupts(); + + /* Actual transition to protected mode... */ + setup_idt(); + setup_gdt(); + protected_mode_jump(boot_params.hdr.code32_start, + (u32)&boot_params + (ds() << 4)); +} diff --git a/arch/i386/boot/pmjump.S b/arch/i386/boot/pmjump.S new file mode 100644 index 00000000000..2e559233725 --- /dev/null +++ b/arch/i386/boot/pmjump.S @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/pmjump.S + * + * The actual transition into protected mode + */ + +#include <asm/boot.h> +#include <asm/segment.h> + + .text + + .globl protected_mode_jump + .type protected_mode_jump, @function + + .code16 + +/* + * void protected_mode_jump(u32 entrypoint, u32 bootparams); + */ +protected_mode_jump: + xorl %ebx, %ebx # Flag to indicate this is a boot + movl %edx, %esi # Pointer to boot_params table + movl %eax, 2f # Patch ljmpl instruction + jmp 1f # Short jump to flush instruction q. + +1: + movw $__BOOT_DS, %cx + + movl %cr0, %edx + orb $1, %dl # Protected mode (PE) bit + movl %edx, %cr0 + + movw %cx, %ds + movw %cx, %es + movw %cx, %fs + movw %cx, %gs + movw %cx, %ss + + # Jump to the 32-bit entrypoint + .byte 0x66, 0xea # ljmpl opcode +2: .long 0 # offset + .word __BOOT_CS # segment + + .size protected_mode_jump, .-protected_mode_jump diff --git a/arch/i386/boot/printf.c b/arch/i386/boot/printf.c new file mode 100644 index 00000000000..1a09f9309d3 --- /dev/null +++ b/arch/i386/boot/printf.c @@ -0,0 +1,307 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/printf.c + * + * Oh, it's a waste of space, but oh-so-yummy for debugging. This + * version of printf() does not include 64-bit support. "Live with + * it." + * + */ + +#include "boot.h" + +static int skip_atoi(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char *number(char *str, long num, int base, int size, int precision, + int type) +{ + char c, sign, tmp[66]; + const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++] = '0'; + else + while (num != 0) + tmp[i++] = digits[do_div(num, base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type & (ZEROPAD + LEFT))) + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char *str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str = buf; *fmt; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= SPECIAL; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char)va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long)va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int *ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short)va_arg(args, int); + if (flags & SIGN) + num = (short)num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str - buf; +} + +int sprintf(char *buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + return i; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S deleted file mode 100644 index 6dbcc95b212..00000000000 --- a/arch/i386/boot/setup.S +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * setup.S Copyright (C) 1991, 1992 Linus Torvalds - * - * setup.s is responsible for getting the system data from the BIOS, - * and putting them into the appropriate places in system memory. - * both setup.s and system has been loaded by the bootblock. - * - * This code asks the bios for memory/disk/other parameters, and - * puts them in a "safe" place: 0x90000-0x901FF, ie where the - * boot-block used to be. It is then up to the protected mode - * system to read them from there before the area is overwritten - * for buffer-blocks. - * - * Move PS/2 aux init code to psaux.c - * (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 - * - * some changes and additional features by Christoph Niemann, - * March 1993/June 1994 (Christoph.Niemann@linux.org) - * - * add APM BIOS checking by Stephen Rothwell, May 1994 - * (sfr@canb.auug.org.au) - * - * High load stuff, initrd support and position independency - * by Hans Lermen & Werner Almesberger, February 1996 - * <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch> - * - * Video handling moved to video.S by Martin Mares, March 1996 - * <mj@k332.feld.cvut.cz> - * - * Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david - * parsons) to avoid loadlin confusion, July 1997 - * - * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999. - * <stiker@northlink.com> - * - * Fix to work around buggy BIOSes which don't use carry bit correctly - * and/or report extended memory in CX/DX for e801h memory size detection - * call. As a result the kernel got wrong figures. The int15/e801h docs - * from Ralf Brown interrupt list seem to indicate AX/BX should be used - * anyway. So to avoid breaking many machines (presumably there was a reason - * to orginally use CX/DX instead of AX/BX), we do a kludge to see - * if CX/DX have been changed in the e801 call and if so use AX/BX . - * Michael Miller, April 2001 <michaelm@mjmm.org> - * - * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes - * by Robert Schwebel, December 2001 <robert@schwebel.de> - */ - -#include <asm/segment.h> -#include <linux/utsrelease.h> -#include <linux/compile.h> -#include <asm/boot.h> -#include <asm/e820.h> -#include <asm/page.h> -#include <asm/setup.h> - -/* Signature words to ensure LILO loaded us right */ -#define SIG1 0xAA55 -#define SIG2 0x5A5A - -INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way -SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536). -SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment - # ... and the former contents of CS - -DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020 - -.code16 -.globl begtext, begdata, begbss, endtext, enddata, endbss - -.text -begtext: -.data -begdata: -.bss -begbss: -.text - -start: - jmp trampoline - -# This is the setup header, and it must start at %cs:2 (old 0x9020:2) - - .ascii "HdrS" # header signature - .word 0x0206 # header version number (>= 0x0105) - # or else old loadlin-1.5 will fail) -realmode_swtch: .word 0, 0 # default_switch, SETUPSEG -start_sys_seg: .word SYSSEG - .word kernel_version # pointing to kernel version string - # above section of header is compatible - # with loadlin-1.5 (header v1.5). Don't - # change it. - -type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, - # Bootlin, SYSLX, bootsect...) - # See Documentation/i386/boot.txt for - # assigned ids - -# flags, unused bits must be zero (RFU) bit within loadflags -loadflags: -LOADED_HIGH = 1 # If set, the kernel is loaded high -CAN_USE_HEAP = 0x80 # If set, the loader also has set - # heap_end_ptr to tell how much - # space behind setup.S can be used for - # heap purposes. - # Only the loader knows what is free -#ifndef __BIG_KERNEL__ - .byte 0 -#else - .byte LOADED_HIGH -#endif - -setup_move_size: .word 0x8000 # size to move, when setup is not - # loaded at 0x90000. We will move setup - # to 0x90000 then just before jumping - # into the kernel. However, only the - # loader knows how much data behind - # us also needs to be loaded. - -code32_start: # here loaders can put a different - # start address for 32-bit code. -#ifndef __BIG_KERNEL__ - .long 0x1000 # 0x1000 = default for zImage -#else - .long 0x100000 # 0x100000 = default for big kernel -#endif - -ramdisk_image: .long 0 # address of loaded ramdisk image - # Here the loader puts the 32-bit - # address where it loaded the image. - # This only will be read by the kernel. - -ramdisk_size: .long 0 # its size in bytes - -bootsect_kludge: - .long 0 # obsolete - -heap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later) - # space from here (exclusive) down to - # end of setup code can be used by setup - # for local heap purposes. - -pad1: .word 0 -cmd_line_ptr: .long 0 # (Header version 0x0202 or later) - # If nonzero, a 32-bit pointer - # to the kernel command line. - # The command line should be - # located between the start of - # setup and the end of low - # memory (0xa0000), or it may - # get overwritten before it - # gets read. If this field is - # used, there is no longer - # anything magical about the - # 0x90000 segment; the setup - # can be located anywhere in - # low memory 0x10000 or higher. - -ramdisk_max: .long (-__PAGE_OFFSET-(512 << 20)-1) & 0x7fffffff - # (Header version 0x0203 or later) - # The highest safe address for - # the contents of an initrd - -kernel_alignment: .long CONFIG_PHYSICAL_ALIGN #physical addr alignment - #required for protected mode - #kernel -#ifdef CONFIG_RELOCATABLE -relocatable_kernel: .byte 1 -#else -relocatable_kernel: .byte 0 -#endif -pad2: .byte 0 -pad3: .word 0 - -cmdline_size: .long COMMAND_LINE_SIZE-1 #length of the command line, - #added with boot protocol - #version 2.06 - -trampoline: call start_of_setup - .align 16 - # The offset at this point is 0x240 - .space (0xeff-0x240+1) # E820 & EDD space (ending at 0xeff) -# End of setup header ##################################################### - -start_of_setup: -# Bootlin depends on this being done early - movw $0x01500, %ax - movb $0x81, %dl - int $0x13 - -#ifdef SAFE_RESET_DISK_CONTROLLER -# Reset the disk controller. - movw $0x0000, %ax - movb $0x80, %dl - int $0x13 -#endif - -# Set %ds = %cs, we know that SETUPSEG = %cs at this point - movw %cs, %ax # aka SETUPSEG - movw %ax, %ds -# Check signature at end of setup - cmpw $SIG1, setup_sig1 - jne bad_sig - - cmpw $SIG2, setup_sig2 - jne bad_sig - - jmp good_sig1 - -# Routine to print asciiz string at ds:si -prtstr: - lodsb - andb %al, %al - jz fin - - call prtchr - jmp prtstr - -fin: ret - -# Space printing -prtsp2: call prtspc # Print double space -prtspc: movb $0x20, %al # Print single space (note: fall-thru) - -# Part of above routine, this one just prints ascii al -prtchr: pushw %ax - pushw %cx - movw $7,%bx - movw $0x01, %cx - movb $0x0e, %ah - int $0x10 - popw %cx - popw %ax - ret - -beep: movb $0x07, %al - jmp prtchr - -no_sig_mess: .string "No setup signature found ..." - -good_sig1: - jmp good_sig - -# We now have to find the rest of the setup code/data -bad_sig: - movw %cs, %ax # SETUPSEG - subw $DELTA_INITSEG, %ax # INITSEG - movw %ax, %ds - xorb %bh, %bh - movb (497), %bl # get setup sect from bootsect - subw $4, %bx # LILO loads 4 sectors of setup - shlw $8, %bx # convert to words (1sect=2^8 words) - movw %bx, %cx - shrw $3, %bx # convert to segment - addw $SYSSEG, %bx - movw %bx, %cs:start_sys_seg -# Move rest of setup code/data to here - movw $2048, %di # four sectors loaded by LILO - subw %si, %si - pushw %cs - popw %es - movw $SYSSEG, %ax - movw %ax, %ds - rep - movsw - movw %cs, %ax # aka SETUPSEG - movw %ax, %ds - cmpw $SIG1, setup_sig1 - jne no_sig - - cmpw $SIG2, setup_sig2 - jne no_sig - - jmp good_sig - -no_sig: - lea no_sig_mess, %si - call prtstr - -no_sig_loop: - hlt - jmp no_sig_loop - -good_sig: - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ax, %ds -# Check if an old loader tries to load a big-kernel - testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel? - jz loader_ok # No, no danger for old loaders. - - cmpb $0, %cs:type_of_loader # Do we have a loader that - # can deal with us? - jnz loader_ok # Yes, continue. - - pushw %cs # No, we have an old loader, - popw %ds # die. - lea loader_panic_mess, %si - call prtstr - - jmp no_sig_loop - -loader_panic_mess: .string "Wrong loader, giving up..." - -# check minimum cpuid -# we do this here because it is the last place we can actually -# show a user visible error message. Later the video modus -# might be already messed up. -loader_ok: - call verify_cpu - testl %eax,%eax - jz cpu_ok - movw %cs,%ax # aka SETUPSEG - movw %ax,%ds - lea cpu_panic_mess,%si - call prtstr -1: jmp 1b - -cpu_panic_mess: - .asciz "PANIC: CPU too old for this kernel." - -#include "../kernel/verify_cpu.S" - -cpu_ok: -# Get memory size (extended mem, kB) - - xorl %eax, %eax - movl %eax, (0x1e0) -#ifndef STANDARD_MEMORY_BIOS_CALL - movb %al, (E820NR) -# Try three different memory detection schemes. First, try -# e820h, which lets us assemble a memory map, then try e801h, -# which returns a 32-bit memory size, and finally 88h, which -# returns 0-64m - -# method E820H: -# the memory map from hell. e820h returns memory classified into -# a whole bunch of different types, and allows memory holes and -# everything. We scan through this memory map and build a list -# of the first 32 memory areas, which we return at [E820MAP]. -# This is documented at http://www.acpi.info/, in the ACPI 2.0 specification. - -#define SMAP 0x534d4150 - -meme820: - xorl %ebx, %ebx # continuation counter - movw $E820MAP, %di # point into the whitelist - # so we can have the bios - # directly write into it. - -jmpe820: - movl $0x0000e820, %eax # e820, upper word zeroed - movl $SMAP, %edx # ascii 'SMAP' - movl $20, %ecx # size of the e820rec - pushw %ds # data record. - popw %es - int $0x15 # make the call - jc bail820 # fall to e801 if it fails - - cmpl $SMAP, %eax # check the return is `SMAP' - jne bail820 # fall to e801 if it fails - -# cmpl $1, 16(%di) # is this usable memory? -# jne again820 - - # If this is usable memory, we save it by simply advancing %di by - # sizeof(e820rec). - # -good820: - movb (E820NR), %al # up to 128 entries - cmpb $E820MAX, %al - jae bail820 - - incb (E820NR) - movw %di, %ax - addw $20, %ax - movw %ax, %di -again820: - cmpl $0, %ebx # check to see if - jne jmpe820 # %ebx is set to EOF -bail820: - - -# method E801H: -# memory size is in 1k chunksizes, to avoid confusing loadlin. -# we store the 0xe801 memory size in a completely different place, -# because it will most likely be longer than 16 bits. -# (use 1e0 because that's what Larry Augustine uses in his -# alternative new memory detection scheme, and it's sensible -# to write everything into the same place.) - -meme801: - stc # fix to work around buggy - xorw %cx,%cx # BIOSes which don't clear/set - xorw %dx,%dx # carry on pass/error of - # e801h memory size call - # or merely pass cx,dx though - # without changing them. - movw $0xe801, %ax - int $0x15 - jc mem88 - - cmpw $0x0, %cx # Kludge to handle BIOSes - jne e801usecxdx # which report their extended - cmpw $0x0, %dx # memory in AX/BX rather than - jne e801usecxdx # CX/DX. The spec I have read - movw %ax, %cx # seems to indicate AX/BX - movw %bx, %dx # are more reasonable anyway... - -e801usecxdx: - andl $0xffff, %edx # clear sign extend - shll $6, %edx # and go from 64k to 1k chunks - movl %edx, (0x1e0) # store extended memory size - andl $0xffff, %ecx # clear sign extend - addl %ecx, (0x1e0) # and add lower memory into - # total size. - -# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or -# 64mb, depending on the bios) in ax. -mem88: - -#endif - movb $0x88, %ah - int $0x15 - movw %ax, (2) - -# Set the keyboard repeat rate to the max - movw $0x0305, %ax - xorw %bx, %bx - int $0x16 - -# Check for video adapter and its parameters and allow the -# user to browse video modes. - call video # NOTE: we need %ds pointing - # to bootsector - -# Get hd0 data... - xorw %ax, %ax - movw %ax, %ds - ldsw (4 * 0x41), %si - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - pushw %ax - movw %ax, %es - movw $0x0080, %di - movw $0x10, %cx - pushw %cx - cld - rep - movsb -# Get hd1 data... - xorw %ax, %ax - movw %ax, %ds - ldsw (4 * 0x46), %si - popw %cx - popw %es - movw $0x0090, %di - rep - movsb -# Check that there IS a hd1 :-) - movw $0x01500, %ax - movb $0x81, %dl - int $0x13 - jc no_disk1 - - cmpb $3, %ah - je is_disk1 - -no_disk1: - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ax, %es - movw $0x0090, %di - movw $0x10, %cx - xorw %ax, %ax - cld - rep - stosb -is_disk1: -# check for Micro Channel (MCA) bus - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ax, %ds - xorw %ax, %ax - movw %ax, (0xa0) # set table length to 0 - movb $0xc0, %ah - stc - int $0x15 # moves feature table to es:bx - jc no_mca - - pushw %ds - movw %es, %ax - movw %ax, %ds - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ax, %es - movw %bx, %si - movw $0xa0, %di - movw (%si), %cx - addw $2, %cx # table length is a short - cmpw $0x10, %cx - jc sysdesc_ok - - movw $0x10, %cx # we keep only first 16 bytes -sysdesc_ok: - rep - movsb - popw %ds -no_mca: -#ifdef CONFIG_X86_VOYAGER - movb $0xff, 0x40 # flag on config found - movb $0xc0, %al - mov $0xff, %ah - int $0x15 # put voyager config info at es:di - jc no_voyager - movw $0x40, %si # place voyager info in apm table - cld - movw $7, %cx -voyager_rep: - movb %es:(%di), %al - movb %al,(%si) - incw %di - incw %si - decw %cx - jnz voyager_rep -no_voyager: -#endif -# Check for PS/2 pointing device - movw %cs, %ax # aka SETUPSEG - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ax, %ds - movb $0, (0x1ff) # default is no pointing device - int $0x11 # int 0x11: equipment list - testb $0x04, %al # check if mouse installed - jz no_psmouse - - movb $0xAA, (0x1ff) # device present -no_psmouse: - -#if defined(CONFIG_X86_SPEEDSTEP_SMI) || defined(CONFIG_X86_SPEEDSTEP_SMI_MODULE) - movl $0x0000E980, %eax # IST Support - movl $0x47534943, %edx # Request value - int $0x15 - - movl %eax, (96) - movl %ebx, (100) - movl %ecx, (104) - movl %edx, (108) -#endif - -#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) -# Then check for an APM BIOS... - # %ds points to the bootsector - movw $0, 0x40 # version = 0 means no APM BIOS - movw $0x05300, %ax # APM BIOS installation check - xorw %bx, %bx - int $0x15 - jc done_apm_bios # Nope, no APM BIOS - - cmpw $0x0504d, %bx # Check for "PM" signature - jne done_apm_bios # No signature, no APM BIOS - - andw $0x02, %cx # Is 32 bit supported? - je done_apm_bios # No 32-bit, no (good) APM BIOS - - movw $0x05304, %ax # Disconnect first just in case - xorw %bx, %bx - int $0x15 # ignore return code - movw $0x05303, %ax # 32 bit connect - xorl %ebx, %ebx - xorw %cx, %cx # paranoia :-) - xorw %dx, %dx # ... - xorl %esi, %esi # ... - xorw %di, %di # ... - int $0x15 - jc no_32_apm_bios # Ack, error. - - movw %ax, (66) # BIOS code segment - movl %ebx, (68) # BIOS entry point offset - movw %cx, (72) # BIOS 16 bit code segment - movw %dx, (74) # BIOS data segment - movl %esi, (78) # BIOS code segment lengths - movw %di, (82) # BIOS data segment length -# Redo the installation check as the 32 bit connect -# modifies the flags returned on some BIOSs - movw $0x05300, %ax # APM BIOS installation check - xorw %bx, %bx - xorw %cx, %cx # paranoia - int $0x15 - jc apm_disconnect # error -> shouldn't happen - - cmpw $0x0504d, %bx # check for "PM" signature - jne apm_disconnect # no sig -> shouldn't happen - - movw %ax, (64) # record the APM BIOS version - movw %cx, (76) # and flags - jmp done_apm_bios - -apm_disconnect: # Tidy up - movw $0x05304, %ax # Disconnect - xorw %bx, %bx - int $0x15 # ignore return code - - jmp done_apm_bios - -no_32_apm_bios: - andw $0xfffd, (76) # remove 32 bit support bit -done_apm_bios: -#endif - -#include "edd.S" - -# Now we want to move to protected mode ... - cmpw $0, %cs:realmode_swtch - jz rmodeswtch_normal - - lcall *%cs:realmode_swtch - - jmp rmodeswtch_end - -rmodeswtch_normal: - pushw %cs - call default_switch - -rmodeswtch_end: -# Now we move the system to its rightful place ... but we check if we have a -# big-kernel. In that case we *must* not move it ... - testb $LOADED_HIGH, %cs:loadflags - jz do_move0 # .. then we have a normal low - # loaded zImage - # .. or else we have a high - # loaded bzImage - jmp end_move # ... and we skip moving - -do_move0: - movw $0x100, %ax # start of destination segment - movw %cs, %bp # aka SETUPSEG - subw $DELTA_INITSEG, %bp # aka INITSEG - movw %cs:start_sys_seg, %bx # start of source segment - cld -do_move: - movw %ax, %es # destination segment - incb %ah # instead of add ax,#0x100 - movw %bx, %ds # source segment - addw $0x100, %bx - subw %di, %di - subw %si, %si - movw $0x800, %cx - rep - movsw - cmpw %bp, %bx # assume start_sys_seg > 0x200, - # so we will perhaps read one - # page more than needed, but - # never overwrite INITSEG - # because destination is a - # minimum one page below source - jb do_move - -end_move: -# then we load the segment descriptors - movw %cs, %ax # aka SETUPSEG - movw %ax, %ds - -# Check whether we need to be downward compatible with version <=201 - cmpl $0, cmd_line_ptr - jne end_move_self # loader uses version >=202 features - cmpb $0x20, type_of_loader - je end_move_self # bootsect loader, we know of it - -# Boot loader doesnt support boot protocol version 2.02. -# If we have our code not at 0x90000, we need to move it there now. -# We also then need to move the params behind it (commandline) -# Because we would overwrite the code on the current IP, we move -# it in two steps, jumping high after the first one. - movw %cs, %ax - cmpw $SETUPSEG, %ax - je end_move_self - - cli # make sure we really have - # interrupts disabled ! - # because after this the stack - # should not be used - subw $DELTA_INITSEG, %ax # aka INITSEG - movw %ss, %dx - cmpw %ax, %dx - jb move_self_1 - - addw $INITSEG, %dx - subw %ax, %dx # this will go into %ss after - # the move -move_self_1: - movw %ax, %ds - movw $INITSEG, %ax # real INITSEG - movw %ax, %es - movw %cs:setup_move_size, %cx - std # we have to move up, so we use - # direction down because the - # areas may overlap - movw %cx, %di - decw %di - movw %di, %si - subw $move_self_here+0x200, %cx - rep - movsb - ljmp $SETUPSEG, $move_self_here - -move_self_here: - movw $move_self_here+0x200, %cx - rep - movsb - movw $SETUPSEG, %ax - movw %ax, %ds - movw %dx, %ss -end_move_self: # now we are at the right place - -# -# Enable A20. This is at the very best an annoying procedure. -# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. -# AMD Elan bug fix by Robert Schwebel. -# - -#if defined(CONFIG_X86_ELAN) - movb $0x02, %al # alternate A20 gate - outb %al, $0x92 # this works on SC410/SC520 -a20_elan_wait: - call a20_test - jz a20_elan_wait - jmp a20_done -#endif - - -A20_TEST_LOOPS = 32 # Iterations per wait -A20_ENABLE_LOOPS = 255 # Total loops to try - - -#ifndef CONFIG_X86_VOYAGER -a20_try_loop: - - # First, see if we are on a system with no A20 gate. -a20_none: - call a20_test - jnz a20_done - - # Next, try the BIOS (INT 0x15, AX=0x2401) -a20_bios: - movw $0x2401, %ax - pushfl # Be paranoid about flags - int $0x15 - popfl - - call a20_test - jnz a20_done - - # Try enabling A20 through the keyboard controller -#endif /* CONFIG_X86_VOYAGER */ -a20_kbc: - call empty_8042 - -#ifndef CONFIG_X86_VOYAGER - call a20_test # Just in case the BIOS worked - jnz a20_done # but had a delayed reaction. -#endif - - movb $0xD1, %al # command write - outb %al, $0x64 - call empty_8042 - - movb $0xDF, %al # A20 on - outb %al, $0x60 - call empty_8042 - -#ifndef CONFIG_X86_VOYAGER - # Wait until a20 really *is* enabled; it can take a fair amount of - # time on certain systems; Toshiba Tecras are known to have this - # problem. -a20_kbc_wait: - xorw %cx, %cx -a20_kbc_wait_loop: - call a20_test - jnz a20_done - loop a20_kbc_wait_loop - - # Final attempt: use "configuration port A" -a20_fast: - inb $0x92, %al # Configuration Port A - orb $0x02, %al # "fast A20" version - andb $0xFE, %al # don't accidentally reset - outb %al, $0x92 - - # Wait for configuration port A to take effect -a20_fast_wait: - xorw %cx, %cx -a20_fast_wait_loop: - call a20_test - jnz a20_done - loop a20_fast_wait_loop - - # A20 is still not responding. Try frobbing it again. - # - decb (a20_tries) - jnz a20_try_loop - - movw $a20_err_msg, %si - call prtstr - -a20_die: - hlt - jmp a20_die - -a20_tries: - .byte A20_ENABLE_LOOPS - -a20_err_msg: - .ascii "linux: fatal error: A20 gate not responding!" - .byte 13, 10, 0 - - # If we get here, all is good -a20_done: - -#endif /* CONFIG_X86_VOYAGER */ -# set up gdt and idt and 32bit start address - lidt idt_48 # load idt with 0,0 - xorl %eax, %eax # Compute gdt_base - movw %ds, %ax # (Convert %ds:gdt to a linear ptr) - shll $4, %eax - addl %eax, code32 - addl $gdt, %eax - movl %eax, (gdt_48+2) - lgdt gdt_48 # load gdt with whatever is - # appropriate - -# make sure any possible coprocessor is properly reset.. - xorw %ax, %ax - outb %al, $0xf0 - call delay - - outb %al, $0xf1 - call delay - -# well, that went ok, I hope. Now we mask all interrupts - the rest -# is done in init_IRQ(). - movb $0xFF, %al # mask all interrupts for now - outb %al, $0xA1 - call delay - - movb $0xFB, %al # mask all irq's but irq2 which - outb %al, $0x21 # is cascaded - -# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't -# need no steenking BIOS anyway (except for the initial loading :-). -# The BIOS-routine wants lots of unnecessary data, and it's less -# "interesting" anyway. This is how REAL programmers do it. -# -# Well, now's the time to actually move into protected mode. To make -# things as simple as possible, we do no register set-up or anything, -# we let the gnu-compiled 32-bit programs do that. We just jump to -# absolute address 0x1000 (or the loader supplied one), -# in 32-bit protected mode. -# -# Note that the short jump isn't strictly needed, although there are -# reasons why it might be a good idea. It won't hurt in any case. - movw $1, %ax # protected mode (PE) bit - lmsw %ax # This is it! - jmp flush_instr - -flush_instr: - xorw %bx, %bx # Flag to indicate a boot - xorl %esi, %esi # Pointer to real-mode code - movw %cs, %si - subw $DELTA_INITSEG, %si - shll $4, %esi # Convert to 32-bit pointer - -# jump to startup_32 in arch/i386/boot/compressed/head.S -# -# NOTE: For high loaded big kernels we need a -# jmpi 0x100000,__BOOT_CS -# -# but we yet haven't reloaded the CS register, so the default size -# of the target offset still is 16 bit. -# However, using an operand prefix (0x66), the CPU will properly -# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference -# Manual, Mixing 16-bit and 32-bit code, page 16-6) - - .byte 0x66, 0xea # prefix + jmpi-opcode -code32: .long startup_32 # will be set to %cs+startup_32 - .word __BOOT_CS -.code32 -startup_32: - movl $(__BOOT_DS), %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - movl %eax, %ss - - xorl %eax, %eax -1: incl %eax # check that A20 really IS enabled - movl %eax, 0x00000000 # loop forever if it isn't - cmpl %eax, 0x00100000 - je 1b - - # Jump to the 32bit entry point - jmpl *(code32_start - start + (DELTA_INITSEG << 4))(%esi) -.code16 - -# Here's a bunch of information about your current kernel.. -kernel_version: .ascii UTS_RELEASE - .ascii " (" - .ascii LINUX_COMPILE_BY - .ascii "@" - .ascii LINUX_COMPILE_HOST - .ascii ") " - .ascii UTS_VERSION - .byte 0 - -# This is the default real mode switch routine. -# to be called just before protected mode transition -default_switch: - cli # no interrupts allowed ! - movb $0x80, %al # disable NMI for bootup - # sequence - outb %al, $0x70 - lret - - -#ifndef CONFIG_X86_VOYAGER -# This routine tests whether or not A20 is enabled. If so, it -# exits with zf = 0. -# -# The memory address used, 0x200, is the int $0x80 vector, which -# should be safe. - -A20_TEST_ADDR = 4*0x80 - -a20_test: - pushw %cx - pushw %ax - xorw %cx, %cx - movw %cx, %fs # Low memory - decw %cx - movw %cx, %gs # High memory area - movw $A20_TEST_LOOPS, %cx - movw %fs:(A20_TEST_ADDR), %ax - pushw %ax -a20_test_wait: - incw %ax - movw %ax, %fs:(A20_TEST_ADDR) - call delay # Serialize and make delay constant - cmpw %gs:(A20_TEST_ADDR+0x10), %ax - loope a20_test_wait - - popw %fs:(A20_TEST_ADDR) - popw %ax - popw %cx - ret - -#endif /* CONFIG_X86_VOYAGER */ - -# This routine checks that the keyboard command queue is empty -# (after emptying the output buffers) -# -# Some machines have delusions that the keyboard buffer is always full -# with no keyboard attached... -# -# If there is no keyboard controller, we will usually get 0xff -# to all the reads. With each IO taking a microsecond and -# a timeout of 100,000 iterations, this can take about half a -# second ("delay" == outb to port 0x80). That should be ok, -# and should also be plenty of time for a real keyboard controller -# to empty. -# - -empty_8042: - pushl %ecx - movl $100000, %ecx - -empty_8042_loop: - decl %ecx - jz empty_8042_end_loop - - call delay - - inb $0x64, %al # 8042 status port - testb $1, %al # output buffer? - jz no_output - - call delay - inb $0x60, %al # read it - jmp empty_8042_loop - -no_output: - testb $2, %al # is input buffer full? - jnz empty_8042_loop # yes - loop -empty_8042_end_loop: - popl %ecx - ret - -# Read the cmos clock. Return the seconds in al -gettime: - pushw %cx - movb $0x02, %ah - int $0x1a - movb %dh, %al # %dh contains the seconds - andb $0x0f, %al - movb %dh, %ah - movb $0x04, %cl - shrb %cl, %ah - aad - popw %cx - ret - -# Delay is needed after doing I/O -delay: - outb %al,$0x80 - ret - -# Descriptor tables -# -# NOTE: The intel manual says gdt should be sixteen bytes aligned for -# efficiency reasons. However, there are machines which are known not -# to boot with misaligned GDTs, so alter this at your peril! If you alter -# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two -# empty GDT entries (one for NULL and one reserved). -# -# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is -# true for the Voyager Quad CPU card which will not boot without -# This directive. 16 byte aligment is recommended by intel. -# - .align 16 -gdt: - .fill GDT_ENTRY_BOOT_CS,8,0 - - .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) - .word 0 # base address = 0 - .word 0x9A00 # code read/exec - .word 0x00CF # granularity = 4096, 386 - # (+5th nibble of limit) - - .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) - .word 0 # base address = 0 - .word 0x9200 # data read/write - .word 0x00CF # granularity = 4096, 386 - # (+5th nibble of limit) -gdt_end: - .align 4 - - .word 0 # alignment byte -idt_48: - .word 0 # idt limit = 0 - .word 0, 0 # idt base = 0L - - .word 0 # alignment byte -gdt_48: - .word gdt_end - gdt - 1 # gdt limit - .word 0, 0 # gdt base (filled in later) - -# Include video setup & detection code - -#include "video.S" - -# Setup signature -- must be last -setup_sig1: .word SIG1 -setup_sig2: .word SIG2 - -# After this point, there is some free space which is used by the video mode -# handling code to store the temporary mode table (not used by the kernel). - -modelist: - -.text -endtext: -.data -enddata: -.bss -endbss: diff --git a/arch/i386/boot/setup.ld b/arch/i386/boot/setup.ld new file mode 100644 index 00000000000..df9234b3a5e --- /dev/null +++ b/arch/i386/boot/setup.ld @@ -0,0 +1,54 @@ +/* + * setup.ld + * + * Linker script for the i386 setup code + */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0; + .bstext : { *(.bstext) } + .bsdata : { *(.bsdata) } + + . = 497; + .header : { *(.header) } + .inittext : { *(.inittext) } + .initdata : { *(.initdata) } + .text : { *(.text*) } + + . = ALIGN(16); + .rodata : { *(.rodata*) } + + .videocards : { + video_cards = .; + *(.videocards) + video_cards_end = .; + } + + . = ALIGN(16); + .data : { *(.data*) } + + .signature : { + setup_sig = .; + LONG(0x5a5aaa55) + } + + + . = ALIGN(16); + .bss : + { + __bss_start = .; + *(.bss) + __bss_end = .; + } + . = ALIGN(16); + _end = .; + + /DISCARD/ : { *(.note*) } + + . = ASSERT(_end <= 0x8000, "Setup too big!"); + . = ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!"); +} diff --git a/arch/i386/boot/string.c b/arch/i386/boot/string.c new file mode 100644 index 00000000000..481a2209778 --- /dev/null +++ b/arch/i386/boot/string.c @@ -0,0 +1,52 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/string.c + * + * Very basic string functions + */ + +#include "boot.h" + +int strcmp(const char *str1, const char *str2) +{ + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)str2; + int delta = 0; + + while (*s1 || *s2) { + delta = *s2 - *s1; + if (delta) + return delta; + s1++; + s2++; + } + return 0; +} + +size_t strnlen(const char *s, size_t maxlen) +{ + const char *es = s; + while (*es && maxlen) { + es++; + maxlen--; + } + + return (es - s); +} + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i * 10 + (*s++ - '0'); + return i; +} diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c index 05798419a6a..886f47d8a48 100644 --- a/arch/i386/boot/tools/build.c +++ b/arch/i386/boot/tools/build.c @@ -1,13 +1,12 @@ /* * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1997 Martin Mares + * Copyright (C) 2007 H. Peter Anvin */ /* * This file builds a disk-image from three different files: * - * - bootsect: compatibility mbr which prints an error message if - * someone tries to boot the kernel directly. * - setup: 8086 machine code, sets up system parm * - system: 80386 code for actual system * @@ -21,6 +20,7 @@ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 * Cross compiling fixes by Gertjan van Wingerde, July 1996 * Rewritten by Martin Mares, April 1997 + * Substantially overhauled by H. Peter Anvin, April 2007 */ #include <stdio.h> @@ -32,23 +32,25 @@ #include <sys/sysmacros.h> #include <unistd.h> #include <fcntl.h> +#include <sys/mman.h> #include <asm/boot.h> -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long u32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; #define DEFAULT_MAJOR_ROOT 0 #define DEFAULT_MINOR_ROOT 0 -/* Minimal number of setup sectors (see also bootsect.S) */ -#define SETUP_SECTS 4 +/* Minimal number of setup sectors */ +#define SETUP_SECT_MIN 5 +#define SETUP_SECT_MAX 64 -byte buf[1024]; -int fd; +/* This must be large enough to hold the entire setup */ +u8 buf[SETUP_SECT_MAX*512]; int is_big_kernel; -void die(const char * str, ...) +static void die(const char * str, ...) { va_list args; va_start(args, str); @@ -57,15 +59,9 @@ void die(const char * str, ...) exit(1); } -void file_open(const char *name) +static void usage(void) { - if ((fd = open(name, O_RDONLY, 0)) < 0) - die("Unable to open `%s': %m", name); -} - -void usage(void) -{ - die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); + die("Usage: build [-b] setup system [rootdev] [> image]"); } int main(int argc, char ** argv) @@ -73,27 +69,30 @@ int main(int argc, char ** argv) unsigned int i, sz, setup_sectors; int c; u32 sys_size; - byte major_root, minor_root; + u8 major_root, minor_root; struct stat sb; + FILE *file; + int fd; + void *kernel; if (argc > 2 && !strcmp(argv[1], "-b")) { is_big_kernel = 1; argc--, argv++; } - if ((argc < 4) || (argc > 5)) + if ((argc < 3) || (argc > 4)) usage(); - if (argc > 4) { - if (!strcmp(argv[4], "CURRENT")) { + if (argc > 3) { + if (!strcmp(argv[3], "CURRENT")) { if (stat("/", &sb)) { perror("/"); die("Couldn't stat /"); } major_root = major(sb.st_dev); minor_root = minor(sb.st_dev); - } else if (strcmp(argv[4], "FLOPPY")) { - if (stat(argv[4], &sb)) { - perror(argv[4]); + } else if (strcmp(argv[3], "FLOPPY")) { + if (stat(argv[3], &sb)) { + perror(argv[3]); die("Couldn't stat root device."); } major_root = major(sb.st_rdev); @@ -108,79 +107,62 @@ int main(int argc, char ** argv) } fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); - file_open(argv[1]); - i = read(fd, buf, sizeof(buf)); - fprintf(stderr,"Boot sector %d bytes.\n",i); - if (i != 512) - die("Boot block must be exactly 512 bytes"); + /* Copy the setup code */ + file = fopen(argv[1], "r"); + if (!file) + die("Unable to open `%s': %m", argv[1]); + c = fread(buf, 1, sizeof(buf), file); + if (ferror(file)) + die("read-error on `setup'"); + if (c < 1024) + die("The setup must be at least 1024 bytes"); if (buf[510] != 0x55 || buf[511] != 0xaa) die("Boot block hasn't got boot flag (0xAA55)"); + fclose(file); + + /* Pad unused space with zeros */ + setup_sectors = (c + 511) / 512; + if (setup_sectors < SETUP_SECT_MIN) + setup_sectors = SETUP_SECT_MIN; + i = setup_sectors*512; + memset(buf+c, 0, i-c); + + /* Set the default root device */ buf[508] = minor_root; buf[509] = major_root; - if (write(1, buf, 512) != 512) - die("Write call failed"); - close (fd); - - file_open(argv[2]); /* Copy the setup code */ - for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c ) - if (write(1, buf, c) != c) - die("Write call failed"); - if (c != 0) - die("read-error on `setup'"); - close (fd); - - setup_sectors = (i + 511) / 512; /* Pad unused space with zeros */ - /* for compatibility with ancient versions of LILO. */ - if (setup_sectors < SETUP_SECTS) - setup_sectors = SETUP_SECTS; - fprintf(stderr, "Setup is %d bytes.\n", i); - memset(buf, 0, sizeof(buf)); - while (i < setup_sectors * 512) { - c = setup_sectors * 512 - i; - if (c > sizeof(buf)) - c = sizeof(buf); - if (write(1, buf, c) != c) - die("Write call failed"); - i += c; - } - file_open(argv[3]); - if (fstat (fd, &sb)) - die("Unable to stat `%s': %m", argv[3]); + fprintf(stderr, "Setup is %d bytes (padded to %d bytes).\n", c, i); + + /* Open and stat the kernel file */ + fd = open(argv[2], O_RDONLY); + if (fd < 0) + die("Unable to open `%s': %m", argv[2]); + if (fstat(fd, &sb)) + die("Unable to stat `%s': %m", argv[2]); sz = sb.st_size; - fprintf (stderr, "System is %d kB\n", sz/1024); + fprintf (stderr, "System is %d kB\n", (sz+1023)/1024); + kernel = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); + if (kernel == MAP_FAILED) + die("Unable to mmap '%s': %m", argv[2]); sys_size = (sz + 15) / 16; if (!is_big_kernel && sys_size > DEF_SYSSIZE) die("System is too big. Try using bzImage or modules."); - while (sz > 0) { - int l, n; - - l = (sz > sizeof(buf)) ? sizeof(buf) : sz; - if ((n=read(fd, buf, l)) != l) { - if (n < 0) - die("Error reading %s: %m", argv[3]); - else - die("%s: Unexpected EOF", argv[3]); - } - if (write(1, buf, l) != l) - die("Write failed"); - sz -= l; - } + + /* Patch the setup code with the appropriate size parameters */ + buf[0x1f1] = setup_sectors-1; + buf[0x1f4] = sys_size; + buf[0x1f5] = sys_size >> 8; + buf[0x1f6] = sys_size >> 16; + buf[0x1f7] = sys_size >> 24; + + if (fwrite(buf, 1, i, stdout) != i) + die("Writing setup failed"); + + /* Copy the kernel code */ + if (fwrite(kernel, 1, sz, stdout) != sz) + die("Writing kernel failed"); close(fd); - if (lseek(1, 497, SEEK_SET) != 497) /* Write sizes to the bootsector */ - die("Output: seek failed"); - buf[0] = setup_sectors; - if (write(1, buf, 1) != 1) - die("Write of setup sector count failed"); - if (lseek(1, 500, SEEK_SET) != 500) - die("Output: seek failed"); - buf[0] = (sys_size & 0xff); - buf[1] = ((sys_size >> 8) & 0xff); - buf[2] = ((sys_size >> 16) & 0xff); - buf[3] = ((sys_size >> 24) & 0xff); - if (write(1, buf, 4) != 4) - die("Write of image length failed"); - - return 0; /* Everything is OK */ + /* Everything is OK */ + return 0; } diff --git a/arch/i386/boot/tty.c b/arch/i386/boot/tty.c new file mode 100644 index 00000000000..a8db78736b0 --- /dev/null +++ b/arch/i386/boot/tty.c @@ -0,0 +1,112 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/tty.c + * + * Very simple screen I/O + * XXX: Probably should add very simple serial I/O? + */ + +#include "boot.h" + +/* + * These functions are in .inittext so they can be used to signal + * error during initialization. + */ + +void __attribute__((section(".inittext"))) putchar(int ch) +{ + unsigned char c = ch; + + if (c == '\n') + putchar('\r'); /* \n -> \r\n */ + + /* int $0x10 is known to have bugs involving touching registers + it shouldn't. Be extra conservative... */ + asm volatile("pushal; int $0x10; popal" + : : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch)); +} + +void __attribute__((section(".inittext"))) puts(const char *str) +{ + int n = 0; + while (*str) { + putchar(*str++); + n++; + } +} + +/* + * Read the CMOS clock through the BIOS, and return the + * seconds in BCD. + */ + +static u8 gettime(void) +{ + u16 ax = 0x0200; + u16 cx, dx; + + asm("int $0x1a" + : "+a" (ax), "=c" (cx), "=d" (dx) + : : "ebx", "esi", "edi"); + + return dx >> 8; +} + +/* + * Read from the keyboard + */ +int getchar(void) +{ + u16 ax = 0; + asm("int $0x16" : "+a" (ax)); + + return ax & 0xff; +} + +static int kbd_pending(void) +{ + u8 pending; + asm("int $0x16; setnz %0" + : "=rm" (pending) + : "a" (0x0100)); + return pending; +} + +void kbd_flush(void) +{ + for (;;) { + if (!kbd_pending()) + break; + getchar(); + } +} + +int getchar_timeout(void) +{ + int cnt = 30; + int t0, t1; + + t0 = gettime(); + + while (cnt) { + if (kbd_pending()) + return getchar(); + + t1 = gettime(); + if (t0 != t1) { + cnt--; + t0 = t1; + } + } + + return 0; /* Timeout! */ +} diff --git a/arch/i386/boot/version.c b/arch/i386/boot/version.c new file mode 100644 index 00000000000..c61462f7d9a --- /dev/null +++ b/arch/i386/boot/version.c @@ -0,0 +1,23 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/version.c + * + * Kernel version string + */ + +#include "boot.h" +#include <linux/utsrelease.h> +#include <linux/compile.h> + +const char kernel_version[] = + UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") " + UTS_VERSION; diff --git a/arch/i386/boot/vesa.h b/arch/i386/boot/vesa.h new file mode 100644 index 00000000000..ff5b73cd406 --- /dev/null +++ b/arch/i386/boot/vesa.h @@ -0,0 +1,79 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1999-2007 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef BOOT_VESA_H +#define BOOT_VESA_H + +typedef struct { + u16 off, seg; +} far_ptr; + +/* VESA General Information table */ +struct vesa_general_info { + u32 signature; /* 0 Magic number = "VESA" */ + u16 version; /* 4 */ + far_ptr vendor_string; /* 6 */ + u32 capabilities; /* 10 */ + far_ptr video_mode_ptr; /* 14 */ + u16 total_memory; /* 18 */ + + u16 oem_software_rev; /* 20 */ + far_ptr oem_vendor_name_ptr; /* 22 */ + far_ptr oem_product_name_ptr; /* 26 */ + far_ptr oem_product_rev_ptr; /* 30 */ + + u8 reserved[222]; /* 34 */ + u8 oem_data[256]; /* 256 */ +} __attribute__ ((packed)); + +#define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24)) +#define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24)) + +struct vesa_mode_info { + u16 mode_attr; /* 0 */ + u8 win_attr[2]; /* 2 */ + u16 win_grain; /* 4 */ + u16 win_size; /* 6 */ + u16 win_seg[2]; /* 8 */ + far_ptr win_scheme; /* 12 */ + u16 logical_scan; /* 16 */ + + u16 h_res; /* 18 */ + u16 v_res; /* 20 */ + u8 char_width; /* 22 */ + u8 char_height; /* 23 */ + u8 memory_planes; /* 24 */ + u8 bpp; /* 25 */ + u8 banks; /* 26 */ + u8 memory_layout; /* 27 */ + u8 bank_size; /* 28 */ + u8 image_planes; /* 29 */ + u8 page_function; /* 30 */ + + u8 rmask; /* 31 */ + u8 rpos; /* 32 */ + u8 gmask; /* 33 */ + u8 gpos; /* 34 */ + u8 bmask; /* 35 */ + u8 bpos; /* 36 */ + u8 resv_mask; /* 37 */ + u8 resv_pos; /* 38 */ + u8 dcm_info; /* 39 */ + + u32 lfb_ptr; /* 40 Linear frame buffer address */ + u32 offscreen_ptr; /* 44 Offscreen memory address */ + u16 offscreen_size; /* 48 */ + + u8 reserved[206]; /* 50 */ +} __attribute__ ((packed)); + +#endif /* LIB_SYS_VESA_H */ diff --git a/arch/i386/boot/video-bios.c b/arch/i386/boot/video-bios.c new file mode 100644 index 00000000000..afea46c500c --- /dev/null +++ b/arch/i386/boot/video-bios.c @@ -0,0 +1,125 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/video-bios.c + * + * Standard video BIOS modes + * + * We have two options for this; silent and scanned. + */ + +#include "boot.h" +#include "video.h" + +__videocard video_bios; + +/* Set a conventional BIOS mode */ +static int set_bios_mode(u8 mode); + +static int bios_set_mode(struct mode_info *mi) +{ + return set_bios_mode(mi->mode - VIDEO_FIRST_BIOS); +} + +static int set_bios_mode(u8 mode) +{ + u16 ax; + u8 new_mode; + + ax = mode; /* AH=0x00 Set Video Mode */ + asm volatile(INT10 + : "+a" (ax) + : : "ebx", "ecx", "edx", "esi", "edi"); + + ax = 0x0f00; /* Get Current Video Mode */ + asm volatile(INT10 + : "+a" (ax) + : : "ebx", "ecx", "edx", "esi", "edi"); + + do_restore = 1; /* Assume video contents was lost */ + new_mode = ax & 0x7f; /* Not all BIOSes are clean with the top bit */ + + if (new_mode == mode) + return 0; /* Mode change OK */ + + if (new_mode != boot_params.screen_info.orig_video_mode) { + /* Mode setting failed, but we didn't end up where we + started. That's bad. Try to revert to the original + video mode. */ + ax = boot_params.screen_info.orig_video_mode; + asm volatile(INT10 + : "+a" (ax) + : : "ebx", "ecx", "edx", "esi", "edi"); + } + return -1; +} + +static int bios_probe(void) +{ + u8 mode; + u8 saved_mode = boot_params.screen_info.orig_video_mode; + u16 crtc; + struct mode_info *mi; + int nmodes = 0; + + if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA) + return 0; + + set_fs(0); + crtc = vga_crtc(); + + video_bios.modes = GET_HEAP(struct mode_info, 0); + + for (mode = 0x14; mode <= 0x7f; mode++) { + if (heap_free() < sizeof(struct mode_info)) + break; + + if (mode_defined(VIDEO_FIRST_BIOS+mode)) + continue; + + if (set_bios_mode(mode)) + continue; + + /* Try to verify that it's a text mode. */ + + /* Attribute Controller: make graphics controller disabled */ + if (in_idx(0x3c0, 0x10) & 0x01) + continue; + + /* Graphics Controller: verify Alpha addressing enabled */ + if (in_idx(0x3ce, 0x06) & 0x01) + continue; + + /* CRTC cursor location low should be zero(?) */ + if (in_idx(crtc, 0x0f)) + continue; + + mi = GET_HEAP(struct mode_info, 1); + mi->mode = VIDEO_FIRST_BIOS+mode; + mi->x = rdfs16(0x44a); + mi->y = rdfs8(0x484)+1; + nmodes++; + } + + set_bios_mode(saved_mode); + + return nmodes; +} + +__videocard video_bios = +{ + .card_name = "BIOS (scanned)", + .probe = bios_probe, + .set_mode = bios_set_mode, + .unsafe = 1, + .xmode_first = VIDEO_FIRST_BIOS, + .xmode_n = 0x80, +}; diff --git a/arch/i386/boot/video-vesa.c b/arch/i386/boot/video-vesa.c new file mode 100644 index 00000000000..e6aa9eb8d93 --- /dev/null +++ b/arch/i386/boot/video-vesa.c @@ -0,0 +1,284 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/video-vesa.c + * + * VESA text modes + */ + +#include "boot.h" +#include "video.h" +#include "vesa.h" + +/* VESA information */ +static struct vesa_general_info vginfo; +static struct vesa_mode_info vminfo; + +__videocard video_vesa; + +static void vesa_store_mode_params_graphics(void); + +static int vesa_probe(void) +{ +#if defined(CONFIG_VIDEO_VESA) || defined(CONFIG_FIRMWARE_EDID) + u16 ax; + u16 mode; + addr_t mode_ptr; + struct mode_info *mi; + int nmodes = 0; + + video_vesa.modes = GET_HEAP(struct mode_info, 0); + + vginfo.signature = VBE2_MAGIC; + + /* Optimistically assume a VESA BIOS is register-clean... */ + ax = 0x4f00; + asm("int $0x10" : "+a" (ax), "=m" (vginfo) : "D" (&vginfo)); + + if (ax != 0x004f || + vginfo.signature != VESA_MAGIC || + vginfo.version < 0x0102) + return 0; /* Not present */ +#endif /* CONFIG_VIDEO_VESA || CONFIG_FIRMWARE_EDID */ +#ifdef CONFIG_VIDEO_VESA + set_fs(vginfo.video_mode_ptr.seg); + mode_ptr = vginfo.video_mode_ptr.off; + + while ((mode = rdfs16(mode_ptr)) != 0xffff) { + mode_ptr += 2; + + if (heap_free() < sizeof(struct mode_info)) + break; /* Heap full, can't save mode info */ + + if (mode & ~0x1ff) + continue; + + memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ + + ax = 0x4f01; + asm("int $0x10" + : "+a" (ax), "=m" (vminfo) + : "c" (mode), "D" (&vminfo)); + + if (ax != 0x004f) + continue; + + if ((vminfo.mode_attr & 0x15) == 0x05) { + /* Text Mode, TTY BIOS supported, + supported by hardware */ + mi = GET_HEAP(struct mode_info, 1); + mi->mode = mode + VIDEO_FIRST_VESA; + mi->x = vminfo.h_res; + mi->y = vminfo.v_res; + nmodes++; + } else if ((vminfo.mode_attr & 0x99) == 0x99) { +#ifdef CONFIG_FB + /* Graphics mode, color, linear frame buffer + supported -- register the mode but hide from + the menu. Only do this if framebuffer is + configured, however, otherwise the user will + be left without a screen. */ + mi = GET_HEAP(struct mode_info, 1); + mi->mode = mode + VIDEO_FIRST_VESA; + mi->x = mi->y = 0; + nmodes++; +#endif + } + } + + return nmodes; +#else + return 0; +#endif /* CONFIG_VIDEO_VESA */ +} + +static int vesa_set_mode(struct mode_info *mode) +{ + u16 ax; + int is_graphic; + u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA; + + memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ + + ax = 0x4f01; + asm("int $0x10" + : "+a" (ax), "=m" (vminfo) + : "c" (vesa_mode), "D" (&vminfo)); + + if (ax != 0x004f) + return -1; + + if ((vminfo.mode_attr & 0x15) == 0x05) { + /* It's a supported text mode */ + is_graphic = 0; + } else if ((vminfo.mode_attr & 0x99) == 0x99) { + /* It's a graphics mode with linear frame buffer */ + is_graphic = 1; + vesa_mode |= 0x4000; /* Request linear frame buffer */ + } else { + return -1; /* Invalid mode */ + } + + + ax = 0x4f02; + asm volatile("int $0x10" + : "+a" (ax) + : "b" (vesa_mode), "D" (0)); + + if (ax != 0x004f) + return -1; + + graphic_mode = is_graphic; + if (!is_graphic) { + /* Text mode */ + force_x = mode->x; + force_y = mode->y; + do_restore = 1; + } else { + /* Graphics mode */ + vesa_store_mode_params_graphics(); + } + + return 0; +} + + +/* Switch DAC to 8-bit mode */ +static void vesa_dac_set_8bits(void) +{ + u8 dac_size = 6; + + /* If possible, switch the DAC to 8-bit mode */ + if (vginfo.capabilities & 1) { + u16 ax, bx; + + ax = 0x4f08; + bx = 0x0800; + asm volatile(INT10 + : "+a" (ax), "+b" (bx) + : : "ecx", "edx", "esi", "edi"); + + if (ax == 0x004f) + dac_size = bx >> 8; + } + + /* Set the color sizes to the DAC size, and offsets to 0 */ + boot_params.screen_info.red_size = dac_size; + boot_params.screen_info.green_size = dac_size; + boot_params.screen_info.blue_size = dac_size; + boot_params.screen_info.rsvd_size = dac_size; + + boot_params.screen_info.red_pos = 0; + boot_params.screen_info.green_pos = 0; + boot_params.screen_info.blue_pos = 0; + boot_params.screen_info.rsvd_pos = 0; +} + +/* Save the VESA protected mode info */ +static void vesa_store_pm_info(void) +{ + u16 ax, bx, di, es; + + ax = 0x4f0a; + bx = di = 0; + asm("pushw %%es; "INT10"; movw %%es,%0; popw %%es" + : "=d" (es), "+a" (ax), "+b" (bx), "+D" (di) + : : "ecx", "esi"); + + if (ax != 0x004f) + return; + + boot_params.screen_info.vesapm_seg = es; + boot_params.screen_info.vesapm_off = di; +} + +/* + * Save video mode parameters for graphics mode + */ +static void vesa_store_mode_params_graphics(void) +{ + /* Tell the kernel we're in VESA graphics mode */ + boot_params.screen_info.orig_video_isVGA = 0x23; + + /* Mode parameters */ + boot_params.screen_info.vesa_attributes = vminfo.mode_attr; + boot_params.screen_info.lfb_linelength = vminfo.logical_scan; + boot_params.screen_info.lfb_width = vminfo.h_res; + boot_params.screen_info.lfb_height = vminfo.v_res; + boot_params.screen_info.lfb_depth = vminfo.bpp; + boot_params.screen_info.pages = vminfo.image_planes; + boot_params.screen_info.lfb_base = vminfo.lfb_ptr; + memcpy(&boot_params.screen_info.red_size, + &vminfo.rmask, 8); + + /* General parameters */ + boot_params.screen_info.lfb_size = vginfo.total_memory; + + if (vminfo.bpp <= 8) + vesa_dac_set_8bits(); + + vesa_store_pm_info(); +} + +/* + * Save EDID information for the kernel; this is invoked, separately, + * after mode-setting. + */ +void vesa_store_edid(void) +{ +#ifdef CONFIG_FIRMWARE_EDID + u16 ax, bx, cx, dx, di; + + /* Apparently used as a nonsense token... */ + memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info); + + if (vginfo.version < 0x0200) + return; /* EDID requires VBE 2.0+ */ + + ax = 0x4f15; /* VBE DDC */ + bx = 0x0000; /* Report DDC capabilities */ + cx = 0; /* Controller 0 */ + di = 0; /* ES:DI must be 0 by spec */ + + /* Note: The VBE DDC spec is different from the main VESA spec; + we genuinely have to assume all registers are destroyed here. */ + + asm("pushw %%es; movw %2,%%es; "INT10"; popw %%es" + : "+a" (ax), "+b" (bx) + : "c" (cx), "D" (di) + : "esi"); + + if (ax != 0x004f) + return; /* No EDID */ + + /* BH = time in seconds to transfer EDD information */ + /* BL = DDC level supported */ + + ax = 0x4f15; /* VBE DDC */ + bx = 0x0001; /* Read EDID */ + cx = 0; /* Controller 0 */ + dx = 0; /* EDID block number */ + di =(size_t) &boot_params.edid_info; /* (ES:)Pointer to block */ + asm(INT10 + : "+a" (ax), "+b" (bx), "+d" (dx) + : "c" (cx), "D" (di) + : "esi"); +#endif /* CONFIG_FIRMWARE_EDID */ +} + +__videocard video_vesa = +{ + .card_name = "VESA", + .probe = vesa_probe, + .set_mode = vesa_set_mode, + .xmode_first = VIDEO_FIRST_VESA, + .xmode_n = 0x200, +}; diff --git a/arch/i386/boot/video-vga.c b/arch/i386/boot/video-vga.c new file mode 100644 index 00000000000..700d09a9c9b --- /dev/null +++ b/arch/i386/boot/video-vga.c @@ -0,0 +1,260 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/video-vga.c + * + * Common all-VGA modes + */ + +#include "boot.h" +#include "video.h" + +static struct mode_info vga_modes[] = { + { VIDEO_80x25, 80, 25 }, + { VIDEO_8POINT, 80, 50 }, + { VIDEO_80x43, 80, 43 }, + { VIDEO_80x28, 80, 28 }, + { VIDEO_80x30, 80, 30 }, + { VIDEO_80x34, 80, 34 }, + { VIDEO_80x60, 80, 60 }, +}; + +static struct mode_info ega_modes[] = { + { VIDEO_80x25, 80, 25 }, + { VIDEO_8POINT, 80, 43 }, +}; + +static struct mode_info cga_modes[] = { + { VIDEO_80x25, 80, 25 }, +}; + +__videocard video_vga; + +/* Set basic 80x25 mode */ +static u8 vga_set_basic_mode(void) +{ + u16 ax; + u8 rows; + u8 mode; + +#ifdef CONFIG_VIDEO_400_HACK + if (adapter >= ADAPTER_VGA) { + asm(INT10 + : : "a" (0x1202), "b" (0x0030) + : "ecx", "edx", "esi", "edi"); + } +#endif + + ax = 0x0f00; + asm(INT10 + : "+a" (ax) + : : "ebx", "ecx", "edx", "esi", "edi"); + + mode = (u8)ax; + + set_fs(0); + rows = rdfs8(0x484); /* rows minus one */ + +#ifndef CONFIG_VIDEO_400_HACK + if ((ax == 0x5003 || ax == 0x5007) && + (rows == 0 || rows == 24)) + return mode; +#endif + + if (mode != 3 && mode != 7) + mode = 3; + + /* Set the mode */ + asm volatile(INT10 + : : "a" (mode) + : "ebx", "ecx", "edx", "esi", "edi"); + do_restore = 1; + return mode; +} + +static void vga_set_8font(void) +{ + /* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */ + + /* Set 8x8 font */ + asm volatile(INT10 : : "a" (0x1112), "b" (0)); + + /* Use alternate print screen */ + asm volatile(INT10 : : "a" (0x1200), "b" (0x20)); + + /* Turn off cursor emulation */ + asm volatile(INT10 : : "a" (0x1201), "b" (0x34)); + + /* Cursor is scan lines 6-7 */ + asm volatile(INT10 : : "a" (0x0100), "c" (0x0607)); +} + +static void vga_set_14font(void) +{ + /* Set 9x14 font - 80x28 on VGA */ + + /* Set 9x14 font */ + asm volatile(INT10 : : "a" (0x1111), "b" (0)); + + /* Turn off cursor emulation */ + asm volatile(INT10 : : "a" (0x1201), "b" (0x34)); + + /* Cursor is scan lines 11-12 */ + asm volatile(INT10 : : "a" (0x0100), "c" (0x0b0c)); +} + +static void vga_set_80x43(void) +{ + /* Set 80x43 mode on VGA (not EGA) */ + + /* Set 350 scans */ + asm volatile(INT10 : : "a" (0x1201), "b" (0x30)); + + /* Reset video mode */ + asm volatile(INT10 : : "a" (0x0003)); + + vga_set_8font(); +} + +/* I/O address of the VGA CRTC */ +u16 vga_crtc(void) +{ + return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4; +} + +static void vga_set_480_scanlines(int end) +{ + u16 crtc; + u8 csel; + + crtc = vga_crtc(); + + out_idx(0x0c, crtc, 0x11); /* Vertical sync end, unlock CR0-7 */ + out_idx(0x0b, crtc, 0x06); /* Vertical total */ + out_idx(0x3e, crtc, 0x07); /* Vertical overflow */ + out_idx(0xea, crtc, 0x10); /* Vertical sync start */ + out_idx(end, crtc, 0x12); /* Vertical display end */ + out_idx(0xe7, crtc, 0x15); /* Vertical blank start */ + out_idx(0x04, crtc, 0x16); /* Vertical blank end */ + csel = inb(0x3cc); + csel &= 0x0d; + csel |= 0xe2; + outb(csel, 0x3cc); +} + +static void vga_set_80x30(void) +{ + vga_set_480_scanlines(0xdf); +} + +static void vga_set_80x34(void) +{ + vga_set_14font(); + vga_set_480_scanlines(0xdb); +} + +static void vga_set_80x60(void) +{ + vga_set_8font(); + vga_set_480_scanlines(0xdf); +} + +static int vga_set_mode(struct mode_info *mode) +{ + /* Set the basic mode */ + vga_set_basic_mode(); + + /* Override a possibly broken BIOS */ + force_x = mode->x; + force_y = mode->y; + + switch (mode->mode) { + case VIDEO_80x25: + break; + case VIDEO_8POINT: + vga_set_8font(); + break; + case VIDEO_80x43: + vga_set_80x43(); + break; + case VIDEO_80x28: + vga_set_14font(); + break; + case VIDEO_80x30: + vga_set_80x30(); + break; + case VIDEO_80x34: + vga_set_80x34(); + break; + case VIDEO_80x60: + vga_set_80x60(); + break; + } + + return 0; +} + +/* + * Note: this probe includes basic information required by all + * systems. It should be executed first, by making sure + * video-vga.c is listed first in the Makefile. + */ +static int vga_probe(void) +{ + static const char *card_name[] = { + "CGA/MDA/HGC", "EGA", "VGA" + }; + static struct mode_info *mode_lists[] = { + cga_modes, + ega_modes, + vga_modes, + }; + static int mode_count[] = { + sizeof(cga_modes)/sizeof(struct mode_info), + sizeof(ega_modes)/sizeof(struct mode_info), + sizeof(vga_modes)/sizeof(struct mode_info), + }; + u8 vga_flag; + + asm(INT10 + : "=b" (boot_params.screen_info.orig_video_ega_bx) + : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */ + : "ecx", "edx", "esi", "edi"); + + /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */ + if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) { + /* EGA/VGA */ + asm(INT10 + : "=a" (vga_flag) + : "a" (0x1a00) + : "ebx", "ecx", "edx", "esi", "edi"); + + if (vga_flag == 0x1a) { + adapter = ADAPTER_VGA; + boot_params.screen_info.orig_video_isVGA = 1; + } else { + adapter = ADAPTER_EGA; + } + } else { + adapter = ADAPTER_CGA; + } + + video_vga.modes = mode_lists[adapter]; + video_vga.card_name = card_name[adapter]; + return mode_count[adapter]; +} + +__videocard video_vga = +{ + .card_name = "VGA", + .probe = vga_probe, + .set_mode = vga_set_mode, +}; diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S deleted file mode 100644 index 8143c9516cb..00000000000 --- a/arch/i386/boot/video.S +++ /dev/null @@ -1,2043 +0,0 @@ -/* video.S - * - * Display adapter & video mode setup, version 2.13 (14-May-99) - * - * Copyright (C) 1995 -- 1998 Martin Mares <mj@ucw.cz> - * Based on the original setup.S code (C) Linus Torvalds and Mats Anderson - * - * Rewritten to use GNU 'as' by Chris Noe <stiker@northlink.com> May 1999 - * - * For further information, look at Documentation/svga.txt. - * - */ - -/* Enable autodetection of SVGA adapters and modes. */ -#undef CONFIG_VIDEO_SVGA - -/* Enable autodetection of VESA modes */ -#define CONFIG_VIDEO_VESA - -/* Enable compacting of mode table */ -#define CONFIG_VIDEO_COMPACT - -/* Retain screen contents when switching modes */ -#define CONFIG_VIDEO_RETAIN - -/* Enable local mode list */ -#undef CONFIG_VIDEO_LOCAL - -/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */ -#undef CONFIG_VIDEO_400_HACK - -/* Hack that lets you force specific BIOS mode ID and specific dimensions */ -#undef CONFIG_VIDEO_GFX_HACK -#define VIDEO_GFX_BIOS_AX 0x4f02 /* 800x600 on ThinkPad */ -#define VIDEO_GFX_BIOS_BX 0x0102 -#define VIDEO_GFX_DUMMY_RESOLUTION 0x6425 /* 100x37 */ - -/* This code uses an extended set of video mode numbers. These include: - * Aliases for standard modes - * NORMAL_VGA (-1) - * EXTENDED_VGA (-2) - * ASK_VGA (-3) - * Video modes numbered by menu position -- NOT RECOMMENDED because of lack - * of compatibility when extending the table. These are between 0x00 and 0xff. - */ -#define VIDEO_FIRST_MENU 0x0000 - -/* Standard BIOS video modes (BIOS number + 0x0100) */ -#define VIDEO_FIRST_BIOS 0x0100 - -/* VESA BIOS video modes (VESA number + 0x0200) */ -#define VIDEO_FIRST_VESA 0x0200 - -/* Video7 special modes (BIOS number + 0x0900) */ -#define VIDEO_FIRST_V7 0x0900 - -/* Special video modes */ -#define VIDEO_FIRST_SPECIAL 0x0f00 -#define VIDEO_80x25 0x0f00 -#define VIDEO_8POINT 0x0f01 -#define VIDEO_80x43 0x0f02 -#define VIDEO_80x28 0x0f03 -#define VIDEO_CURRENT_MODE 0x0f04 -#define VIDEO_80x30 0x0f05 -#define VIDEO_80x34 0x0f06 -#define VIDEO_80x60 0x0f07 -#define VIDEO_GFX_HACK 0x0f08 -#define VIDEO_LAST_SPECIAL 0x0f09 - -/* Video modes given by resolution */ -#define VIDEO_FIRST_RESOLUTION 0x1000 - -/* The "recalculate timings" flag */ -#define VIDEO_RECALC 0x8000 - -/* Positions of various video parameters passed to the kernel */ -/* (see also include/linux/tty.h) */ -#define PARAM_CURSOR_POS 0x00 -#define PARAM_VIDEO_PAGE 0x04 -#define PARAM_VIDEO_MODE 0x06 -#define PARAM_VIDEO_COLS 0x07 -#define PARAM_VIDEO_EGA_BX 0x0a -#define PARAM_VIDEO_LINES 0x0e -#define PARAM_HAVE_VGA 0x0f -#define PARAM_FONT_POINTS 0x10 - -#define PARAM_LFB_WIDTH 0x12 -#define PARAM_LFB_HEIGHT 0x14 -#define PARAM_LFB_DEPTH 0x16 -#define PARAM_LFB_BASE 0x18 -#define PARAM_LFB_SIZE 0x1c -#define PARAM_LFB_LINELENGTH 0x24 -#define PARAM_LFB_COLORS 0x26 -#define PARAM_VESAPM_SEG 0x2e -#define PARAM_VESAPM_OFF 0x30 -#define PARAM_LFB_PAGES 0x32 -#define PARAM_VESA_ATTRIB 0x34 -#define PARAM_CAPABILITIES 0x36 - -/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ -#ifdef CONFIG_VIDEO_RETAIN -#define DO_STORE call store_screen -#else -#define DO_STORE -#endif /* CONFIG_VIDEO_RETAIN */ - -# This is the main entry point called by setup.S -# %ds *must* be pointing to the bootsector -video: pushw %ds # We use different segments - pushw %ds # FS contains original DS - popw %fs - pushw %cs # DS is equal to CS - popw %ds - pushw %cs # ES is equal to CS - popw %es - xorw %ax, %ax - movw %ax, %gs # GS is zero - cld - call basic_detect # Basic adapter type testing (EGA/VGA/MDA/CGA) -#ifdef CONFIG_VIDEO_SELECT - movw %fs:(0x01fa), %ax # User selected video mode - cmpw $ASK_VGA, %ax # Bring up the menu - jz vid2 - - call mode_set # Set the mode - jc vid1 - - leaw badmdt, %si # Invalid mode ID - call prtstr -vid2: call mode_menu -vid1: -#ifdef CONFIG_VIDEO_RETAIN - call restore_screen # Restore screen contents -#endif /* CONFIG_VIDEO_RETAIN */ - call store_edid -#endif /* CONFIG_VIDEO_SELECT */ - call mode_params # Store mode parameters - popw %ds # Restore original DS - ret - -# Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel. -basic_detect: - movb $0, %fs:(PARAM_HAVE_VGA) - movb $0x12, %ah # Check EGA/VGA - movb $0x10, %bl - int $0x10 - movw %bx, %fs:(PARAM_VIDEO_EGA_BX) # Identifies EGA to the kernel - cmpb $0x10, %bl # No, it's a CGA/MDA/HGA card. - je basret - - incb adapter - movw $0x1a00, %ax # Check EGA or VGA? - int $0x10 - cmpb $0x1a, %al # 1a means VGA... - jne basret # anything else is EGA. - - incb %fs:(PARAM_HAVE_VGA) # We've detected a VGA - incb adapter -basret: ret - -# Store the video mode parameters for later usage by the kernel. -# This is done by asking the BIOS except for the rows/columns -# parameters in the default 80x25 mode -- these are set directly, -# because some very obscure BIOSes supply insane values. -mode_params: -#ifdef CONFIG_VIDEO_SELECT - cmpb $0, graphic_mode - jnz mopar_gr -#endif - movb $0x03, %ah # Read cursor position - xorb %bh, %bh - int $0x10 - movw %dx, %fs:(PARAM_CURSOR_POS) - movb $0x0f, %ah # Read page/mode/width - int $0x10 - movw %bx, %fs:(PARAM_VIDEO_PAGE) - movw %ax, %fs:(PARAM_VIDEO_MODE) # Video mode and screen width - cmpb $0x7, %al # MDA/HGA => segment differs - jnz mopar0 - - movw $0xb000, video_segment -mopar0: movw %gs:(0x485), %ax # Font size - movw %ax, %fs:(PARAM_FONT_POINTS) # (valid only on EGA/VGA) - movw force_size, %ax # Forced size? - orw %ax, %ax - jz mopar1 - - movb %ah, %fs:(PARAM_VIDEO_COLS) - movb %al, %fs:(PARAM_VIDEO_LINES) - ret - -mopar1: movb $25, %al - cmpb $0, adapter # If we are on CGA/MDA/HGA, the - jz mopar2 # screen must have 25 lines. - - movb %gs:(0x484), %al # On EGA/VGA, use the EGA+ BIOS - incb %al # location of max lines. -mopar2: movb %al, %fs:(PARAM_VIDEO_LINES) - ret - -#ifdef CONFIG_VIDEO_SELECT -# Fetching of VESA frame buffer parameters -mopar_gr: - leaw modelist+1024, %di - movb $0x23, %fs:(PARAM_HAVE_VGA) - movw 16(%di), %ax - movw %ax, %fs:(PARAM_LFB_LINELENGTH) - movw 18(%di), %ax - movw %ax, %fs:(PARAM_LFB_WIDTH) - movw 20(%di), %ax - movw %ax, %fs:(PARAM_LFB_HEIGHT) - movb 25(%di), %al - movb $0, %ah - movw %ax, %fs:(PARAM_LFB_DEPTH) - movb 29(%di), %al - movb $0, %ah - movw %ax, %fs:(PARAM_LFB_PAGES) - movl 40(%di), %eax - movl %eax, %fs:(PARAM_LFB_BASE) - movl 31(%di), %eax - movl %eax, %fs:(PARAM_LFB_COLORS) - movl 35(%di), %eax - movl %eax, %fs:(PARAM_LFB_COLORS+4) - movw 0(%di), %ax - movw %ax, %fs:(PARAM_VESA_ATTRIB) - -# get video mem size - leaw modelist+1024, %di - movw $0x4f00, %ax - int $0x10 - xorl %eax, %eax - movw 18(%di), %ax - movl %eax, %fs:(PARAM_LFB_SIZE) - -# store mode capabilities - movl 10(%di), %eax - movl %eax, %fs:(PARAM_CAPABILITIES) - -# switching the DAC to 8-bit is for <= 8 bpp only - movw %fs:(PARAM_LFB_DEPTH), %ax - cmpw $8, %ax - jg dac_done - -# get DAC switching capability - xorl %eax, %eax - movb 10(%di), %al - testb $1, %al - jz dac_set - -# attempt to switch DAC to 8-bit - movw $0x4f08, %ax - movw $0x0800, %bx - int $0x10 - cmpw $0x004f, %ax - jne dac_set - movb %bh, dac_size # store actual DAC size - -dac_set: -# set color size to DAC size - movb dac_size, %al - movb %al, %fs:(PARAM_LFB_COLORS+0) - movb %al, %fs:(PARAM_LFB_COLORS+2) - movb %al, %fs:(PARAM_LFB_COLORS+4) - movb %al, %fs:(PARAM_LFB_COLORS+6) - -# set color offsets to 0 - movb $0, %fs:(PARAM_LFB_COLORS+1) - movb $0, %fs:(PARAM_LFB_COLORS+3) - movb $0, %fs:(PARAM_LFB_COLORS+5) - movb $0, %fs:(PARAM_LFB_COLORS+7) - -dac_done: -# get protected mode interface informations - movw $0x4f0a, %ax - xorw %bx, %bx - xorw %di, %di - int $0x10 - cmp $0x004f, %ax - jnz no_pm - - movw %es, %fs:(PARAM_VESAPM_SEG) - movw %di, %fs:(PARAM_VESAPM_OFF) -no_pm: ret - -# The video mode menu -mode_menu: - leaw keymsg, %si # "Return/Space/Timeout" message - call prtstr - call flush -nokey: call getkt - - cmpb $0x0d, %al # ENTER ? - je listm # yes - manual mode selection - - cmpb $0x20, %al # SPACE ? - je defmd1 # no - repeat - - call beep - jmp nokey - -defmd1: ret # No mode chosen? Default 80x25 - -listm: call mode_table # List mode table -listm0: leaw name_bann, %si # Print adapter name - call prtstr - movw card_name, %si - orw %si, %si - jnz an2 - - movb adapter, %al - leaw old_name, %si - orb %al, %al - jz an1 - - leaw ega_name, %si - decb %al - jz an1 - - leaw vga_name, %si - jmp an1 - -an2: call prtstr - leaw svga_name, %si -an1: call prtstr - leaw listhdr, %si # Table header - call prtstr - movb $0x30, %dl # DL holds mode number - leaw modelist, %si -lm1: cmpw $ASK_VGA, (%si) # End? - jz lm2 - - movb %dl, %al # Menu selection number - call prtchr - call prtsp2 - lodsw - call prthw # Mode ID - call prtsp2 - movb 0x1(%si), %al - call prtdec # Rows - movb $0x78, %al # the letter 'x' - call prtchr - lodsw - call prtdec # Columns - movb $0x0d, %al # New line - call prtchr - movb $0x0a, %al - call prtchr - incb %dl # Next character - cmpb $0x3a, %dl - jnz lm1 - - movb $0x61, %dl - jmp lm1 - -lm2: leaw prompt, %si # Mode prompt - call prtstr - leaw edit_buf, %di # Editor buffer -lm3: call getkey - cmpb $0x0d, %al # Enter? - jz lment - - cmpb $0x08, %al # Backspace? - jz lmbs - - cmpb $0x20, %al # Printable? - jc lm3 - - cmpw $edit_buf+4, %di # Enough space? - jz lm3 - - stosb - call prtchr - jmp lm3 - -lmbs: cmpw $edit_buf, %di # Backspace - jz lm3 - - decw %di - movb $0x08, %al - call prtchr - call prtspc - movb $0x08, %al - call prtchr - jmp lm3 - -lment: movb $0, (%di) - leaw crlft, %si - call prtstr - leaw edit_buf, %si - cmpb $0, (%si) # Empty string = default mode - jz lmdef - - cmpb $0, 1(%si) # One character = menu selection - jz mnusel - - cmpw $0x6373, (%si) # "scan" => mode scanning - jnz lmhx - - cmpw $0x6e61, 2(%si) - jz lmscan - -lmhx: xorw %bx, %bx # Else => mode ID in hex -lmhex: lodsb - orb %al, %al - jz lmuse1 - - subb $0x30, %al - jc lmbad - - cmpb $10, %al - jc lmhx1 - - subb $7, %al - andb $0xdf, %al - cmpb $10, %al - jc lmbad - - cmpb $16, %al - jnc lmbad - -lmhx1: shlw $4, %bx - orb %al, %bl - jmp lmhex - -lmuse1: movw %bx, %ax - jmp lmuse - -mnusel: lodsb # Menu selection - xorb %ah, %ah - subb $0x30, %al - jc lmbad - - cmpb $10, %al - jc lmuse - - cmpb $0x61-0x30, %al - jc lmbad - - subb $0x61-0x30-10, %al - cmpb $36, %al - jnc lmbad - -lmuse: call mode_set - jc lmdef - -lmbad: leaw unknt, %si - call prtstr - jmp lm2 -lmscan: cmpb $0, adapter # Scanning only on EGA/VGA - jz lmbad - - movw $0, mt_end # Scanning of modes is - movb $1, scanning # done as new autodetection. - call mode_table - jmp listm0 -lmdef: ret - -# Additional parts of mode_set... (relative jumps, you know) -setv7: # Video7 extended modes - DO_STORE - subb $VIDEO_FIRST_V7>>8, %bh - movw $0x6f05, %ax - int $0x10 - stc - ret - -_setrec: jmp setrec # Ugly... -_set_80x25: jmp set_80x25 - -# Aliases for backward compatibility. -setalias: - movw $VIDEO_80x25, %ax - incw %bx - jz mode_set - - movb $VIDEO_8POINT-VIDEO_FIRST_SPECIAL, %al - incw %bx - jnz setbad # Fall-through! - -# Setting of user mode (AX=mode ID) => CF=success -mode_set: - movw %ax, %fs:(0x01fa) # Store mode for use in acpi_wakeup.S - movw %ax, %bx - cmpb $0xff, %ah - jz setalias - - testb $VIDEO_RECALC>>8, %ah - jnz _setrec - - cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah - jnc setres - - cmpb $VIDEO_FIRST_SPECIAL>>8, %ah - jz setspc - - cmpb $VIDEO_FIRST_V7>>8, %ah - jz setv7 - - cmpb $VIDEO_FIRST_VESA>>8, %ah - jnc check_vesa - - orb %ah, %ah - jz setmenu - - decb %ah - jz setbios - -setbad: clc - movb $0, do_restore # The screen needn't be restored - ret - -setvesa: - DO_STORE - subb $VIDEO_FIRST_VESA>>8, %bh - movw $0x4f02, %ax # VESA BIOS mode set call - int $0x10 - cmpw $0x004f, %ax # AL=4f if implemented - jnz setbad # AH=0 if OK - - stc - ret - -setbios: - DO_STORE - int $0x10 # Standard BIOS mode set call - pushw %bx - movb $0x0f, %ah # Check if really set - int $0x10 - popw %bx - cmpb %bl, %al - jnz setbad - - stc - ret - -setspc: xorb %bh, %bh # Set special mode - cmpb $VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL, %bl - jnc setbad - - addw %bx, %bx - jmp *spec_inits(%bx) - -setmenu: - orb %al, %al # 80x25 is an exception - jz _set_80x25 - - pushw %bx # Set mode chosen from menu - call mode_table # Build the mode table - popw %ax - shlw $2, %ax - addw %ax, %si - cmpw %di, %si - jnc setbad - - movw (%si), %ax # Fetch mode ID -_m_s: jmp mode_set - -setres: pushw %bx # Set mode chosen by resolution - call mode_table - popw %bx - xchgb %bl, %bh -setr1: lodsw - cmpw $ASK_VGA, %ax # End of the list? - jz setbad - - lodsw - cmpw %bx, %ax - jnz setr1 - - movw -4(%si), %ax # Fetch mode ID - jmp _m_s - -check_vesa: -#ifdef CONFIG_FIRMWARE_EDID - leaw modelist+1024, %di - movw $0x4f00, %ax - int $0x10 - cmpw $0x004f, %ax - jnz setbad - - movw 4(%di), %ax - movw %ax, vbe_version -#endif - leaw modelist+1024, %di - subb $VIDEO_FIRST_VESA>>8, %bh - movw %bx, %cx # Get mode information structure - movw $0x4f01, %ax - int $0x10 - addb $VIDEO_FIRST_VESA>>8, %bh - cmpw $0x004f, %ax - jnz setbad - - movb (%di), %al # Check capabilities. - andb $0x19, %al - cmpb $0x09, %al - jz setvesa # This is a text mode - - movb (%di), %al # Check capabilities. - andb $0x99, %al - cmpb $0x99, %al - jnz _setbad # Doh! No linear frame buffer. - - subb $VIDEO_FIRST_VESA>>8, %bh - orw $0x4000, %bx # Use linear frame buffer - movw $0x4f02, %ax # VESA BIOS mode set call - int $0x10 - cmpw $0x004f, %ax # AL=4f if implemented - jnz _setbad # AH=0 if OK - - movb $1, graphic_mode # flag graphic mode - movb $0, do_restore # no screen restore - stc - ret - -_setbad: jmp setbad # Ugly... - -# Recalculate vertical display end registers -- this fixes various -# inconsistencies of extended modes on many adapters. Called when -# the VIDEO_RECALC flag is set in the mode ID. - -setrec: subb $VIDEO_RECALC>>8, %ah # Set the base mode - call mode_set - jnc rct3 - - movw %gs:(0x485), %ax # Font size in pixels - movb %gs:(0x484), %bl # Number of rows - incb %bl - mulb %bl # Number of visible - decw %ax # scan lines - 1 - movw $0x3d4, %dx - movw %ax, %bx - movb $0x12, %al # Lower 8 bits - movb %bl, %ah - outw %ax, %dx - movb $0x07, %al # Bits 8 and 9 in the overflow register - call inidx - xchgb %al, %ah - andb $0xbd, %ah - shrb %bh - jnc rct1 - orb $0x02, %ah -rct1: shrb %bh - jnc rct2 - orb $0x40, %ah -rct2: movb $0x07, %al - outw %ax, %dx - stc -rct3: ret - -# Table of routines for setting of the special modes. -spec_inits: - .word set_80x25 - .word set_8pixel - .word set_80x43 - .word set_80x28 - .word set_current - .word set_80x30 - .word set_80x34 - .word set_80x60 - .word set_gfx - -# Set the 80x25 mode. If already set, do nothing. -set_80x25: - movw $0x5019, force_size # Override possibly broken BIOS -use_80x25: -#ifdef CONFIG_VIDEO_400_HACK - movw $0x1202, %ax # Force 400 scan lines - movb $0x30, %bl - int $0x10 -#else - movb $0x0f, %ah # Get current mode ID - int $0x10 - cmpw $0x5007, %ax # Mode 7 (80x25 mono) is the only one available - jz st80 # on CGA/MDA/HGA and is also available on EGAM - - cmpw $0x5003, %ax # Unknown mode, force 80x25 color - jnz force3 - -st80: cmpb $0, adapter # CGA/MDA/HGA => mode 3/7 is always 80x25 - jz set80 - - movb %gs:(0x0484), %al # This is EGA+ -- beware of 80x50 etc. - orb %al, %al # Some buggy BIOS'es set 0 rows - jz set80 - - cmpb $24, %al # It's hopefully correct - jz set80 -#endif /* CONFIG_VIDEO_400_HACK */ -force3: DO_STORE - movw $0x0003, %ax # Forced set - int $0x10 -set80: stc - ret - -# Set the 80x50/80x43 8-pixel mode. Simple BIOS calls. -set_8pixel: - DO_STORE - call use_80x25 # The base is 80x25 -set_8pt: - movw $0x1112, %ax # Use 8x8 font - xorb %bl, %bl - int $0x10 - movw $0x1200, %ax # Use alternate print screen - movb $0x20, %bl - int $0x10 - movw $0x1201, %ax # Turn off cursor emulation - movb $0x34, %bl - int $0x10 - movb $0x01, %ah # Define cursor scan lines 6-7 - movw $0x0607, %cx - int $0x10 -set_current: - stc - ret - -# Set the 80x28 mode. This mode works on all VGA's, because it's a standard -# 80x25 mode with 14-point fonts instead of 16-point. -set_80x28: - DO_STORE - call use_80x25 # The base is 80x25 -set14: movw $0x1111, %ax # Use 9x14 font - xorb %bl, %bl - int $0x10 - movb $0x01, %ah # Define cursor scan lines 11-12 - movw $0x0b0c, %cx - int $0x10 - stc - ret - -# Set the 80x43 mode. This mode is works on all VGA's. -# It's a 350-scanline mode with 8-pixel font. -set_80x43: - DO_STORE - movw $0x1201, %ax # Set 350 scans - movb $0x30, %bl - int $0x10 - movw $0x0003, %ax # Reset video mode - int $0x10 - jmp set_8pt # Use 8-pixel font - -# Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font. -set_80x30: - call use_80x25 # Start with real 80x25 - DO_STORE - movw $0x3cc, %dx # Get CRTC port - inb %dx, %al - movb $0xd4, %dl - rorb %al # Mono or color? - jc set48a - - movb $0xb4, %dl -set48a: movw $0x0c11, %ax # Vertical sync end (also unlocks CR0-7) - call outidx - movw $0x0b06, %ax # Vertical total - call outidx - movw $0x3e07, %ax # (Vertical) overflow - call outidx - movw $0xea10, %ax # Vertical sync start - call outidx - movw $0xdf12, %ax # Vertical display end - call outidx - movw $0xe715, %ax # Vertical blank start - call outidx - movw $0x0416, %ax # Vertical blank end - call outidx - pushw %dx - movb $0xcc, %dl # Misc output register (read) - inb %dx, %al - movb $0xc2, %dl # (write) - andb $0x0d, %al # Preserve clock select bits and color bit - orb $0xe2, %al # Set correct sync polarity - outb %al, %dx - popw %dx - movw $0x501e, force_size - stc # That's all. - ret - -# Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font. -set_80x34: - call set_80x30 # Set 480 scans - call set14 # And 14-pt font - movw $0xdb12, %ax # VGA vertical display end - movw $0x5022, force_size -setvde: call outidx - stc - ret - -# Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font. -set_80x60: - call set_80x30 # Set 480 scans - call set_8pt # And 8-pt font - movw $0xdf12, %ax # VGA vertical display end - movw $0x503c, force_size - jmp setvde - -# Special hack for ThinkPad graphics -set_gfx: -#ifdef CONFIG_VIDEO_GFX_HACK - movw $VIDEO_GFX_BIOS_AX, %ax - movw $VIDEO_GFX_BIOS_BX, %bx - int $0x10 - movw $VIDEO_GFX_DUMMY_RESOLUTION, force_size - stc -#endif - ret - -#ifdef CONFIG_VIDEO_RETAIN - -# Store screen contents to temporary buffer. -store_screen: - cmpb $0, do_restore # Already stored? - jnz stsr - - testb $CAN_USE_HEAP, loadflags # Have we space for storing? - jz stsr - - pushw %ax - pushw %bx - pushw force_size # Don't force specific size - movw $0, force_size - call mode_params # Obtain params of current mode - popw force_size - movb %fs:(PARAM_VIDEO_LINES), %ah - movb %fs:(PARAM_VIDEO_COLS), %al - movw %ax, %bx # BX=dimensions - mulb %ah - movw %ax, %cx # CX=number of characters - addw %ax, %ax # Calculate image size - addw $modelist+1024+4, %ax - cmpw heap_end_ptr, %ax - jnc sts1 # Unfortunately, out of memory - - movw %fs:(PARAM_CURSOR_POS), %ax # Store mode params - leaw modelist+1024, %di - stosw - movw %bx, %ax - stosw - pushw %ds # Store the screen - movw video_segment, %ds - xorw %si, %si - rep - movsw - popw %ds - incb do_restore # Screen will be restored later -sts1: popw %bx - popw %ax -stsr: ret - -# Restore screen contents from temporary buffer. -restore_screen: - cmpb $0, do_restore # Has the screen been stored? - jz res1 - - call mode_params # Get parameters of current mode - movb %fs:(PARAM_VIDEO_LINES), %cl - movb %fs:(PARAM_VIDEO_COLS), %ch - leaw modelist+1024, %si # Screen buffer - lodsw # Set cursor position - movw %ax, %dx - cmpb %cl, %dh - jc res2 - - movb %cl, %dh - decb %dh -res2: cmpb %ch, %dl - jc res3 - - movb %ch, %dl - decb %dl -res3: movb $0x02, %ah - movb $0x00, %bh - int $0x10 - lodsw # Display size - movb %ah, %dl # DL=number of lines - movb $0, %ah # BX=phys. length of orig. line - movw %ax, %bx - cmpb %cl, %dl # Too many? - jc res4 - - pushw %ax - movb %dl, %al - subb %cl, %al - mulb %bl - addw %ax, %si - addw %ax, %si - popw %ax - movb %cl, %dl -res4: cmpb %ch, %al # Too wide? - jc res5 - - movb %ch, %al # AX=width of src. line -res5: movb $0, %cl - xchgb %ch, %cl - movw %cx, %bp # BP=width of dest. line - pushw %es - movw video_segment, %es - xorw %di, %di # Move the data - addw %bx, %bx # Convert BX and BP to _bytes_ - addw %bp, %bp -res6: pushw %si - pushw %di - movw %ax, %cx - rep - movsw - popw %di - popw %si - addw %bp, %di - addw %bx, %si - decb %dl - jnz res6 - - popw %es # Done -res1: ret -#endif /* CONFIG_VIDEO_RETAIN */ - -# Write to indexed VGA register (AL=index, AH=data, DX=index reg. port) -outidx: outb %al, %dx - pushw %ax - movb %ah, %al - incw %dx - outb %al, %dx - decw %dx - popw %ax - ret - -# Build the table of video modes (stored after the setup.S code at the -# `modelist' label. Each video mode record looks like: -# .word MODE-ID (our special mode ID (see above)) -# .byte rows (number of rows) -# .byte columns (number of columns) -# Returns address of the end of the table in DI, the end is marked -# with a ASK_VGA ID. -mode_table: - movw mt_end, %di # Already filled? - orw %di, %di - jnz mtab1x - - leaw modelist, %di # Store standard modes: - movl $VIDEO_80x25 + 0x50190000, %eax # The 80x25 mode (ALL) - stosl - movb adapter, %al # CGA/MDA/HGA -- no more modes - orb %al, %al - jz mtabe - - decb %al - jnz mtabv - - movl $VIDEO_8POINT + 0x502b0000, %eax # The 80x43 EGA mode - stosl - jmp mtabe - -mtab1x: jmp mtab1 - -mtabv: leaw vga_modes, %si # All modes for std VGA - movw $vga_modes_end-vga_modes, %cx - rep # I'm unable to use movsw as I don't know how to store a half - movsb # of the expression above to cx without using explicit shr. - - cmpb $0, scanning # Mode scan requested? - jz mscan1 - - call mode_scan -mscan1: - -#ifdef CONFIG_VIDEO_LOCAL - call local_modes -#endif /* CONFIG_VIDEO_LOCAL */ - -#ifdef CONFIG_VIDEO_VESA - call vesa_modes # Detect VESA VGA modes -#endif /* CONFIG_VIDEO_VESA */ - -#ifdef CONFIG_VIDEO_SVGA - cmpb $0, scanning # Bypass when scanning - jnz mscan2 - - call svga_modes # Detect SVGA cards & modes -mscan2: -#endif /* CONFIG_VIDEO_SVGA */ - -mtabe: - -#ifdef CONFIG_VIDEO_COMPACT - leaw modelist, %si - movw %di, %dx - movw %si, %di -cmt1: cmpw %dx, %si # Scan all modes - jz cmt2 - - leaw modelist, %bx # Find in previous entries - movw 2(%si), %cx -cmt3: cmpw %bx, %si - jz cmt4 - - cmpw 2(%bx), %cx # Found => don't copy this entry - jz cmt5 - - addw $4, %bx - jmp cmt3 - -cmt4: movsl # Copy entry - jmp cmt1 - -cmt5: addw $4, %si # Skip entry - jmp cmt1 - -cmt2: -#endif /* CONFIG_VIDEO_COMPACT */ - - movw $ASK_VGA, (%di) # End marker - movw %di, mt_end -mtab1: leaw modelist, %si # SI=mode list, DI=list end -ret0: ret - -# Modes usable on all standard VGAs -vga_modes: - .word VIDEO_8POINT - .word 0x5032 # 80x50 - .word VIDEO_80x43 - .word 0x502b # 80x43 - .word VIDEO_80x28 - .word 0x501c # 80x28 - .word VIDEO_80x30 - .word 0x501e # 80x30 - .word VIDEO_80x34 - .word 0x5022 # 80x34 - .word VIDEO_80x60 - .word 0x503c # 80x60 -#ifdef CONFIG_VIDEO_GFX_HACK - .word VIDEO_GFX_HACK - .word VIDEO_GFX_DUMMY_RESOLUTION -#endif - -vga_modes_end: -# Detect VESA modes. - -#ifdef CONFIG_VIDEO_VESA -vesa_modes: - cmpb $2, adapter # VGA only - jnz ret0 - - movw %di, %bp # BP=original mode table end - addw $0x200, %di # Buffer space - movw $0x4f00, %ax # VESA Get card info call - int $0x10 - movw %bp, %di - cmpw $0x004f, %ax # Successful? - jnz ret0 - - cmpw $0x4556, 0x200(%di) - jnz ret0 - - cmpw $0x4153, 0x202(%di) - jnz ret0 - - movw $vesa_name, card_name # Set name to "VESA VGA" - pushw %gs - lgsw 0x20e(%di), %si # GS:SI=mode list - movw $128, %cx # Iteration limit -vesa1: -# gas version 2.9.1, using BFD version 2.9.1.0.23 buggers the next inst. -# XXX: lodsw %gs:(%si), %ax # Get next mode in the list - gs; lodsw - cmpw $0xffff, %ax # End of the table? - jz vesar - - cmpw $0x0080, %ax # Check validity of mode ID - jc vesa2 - - orb %ah, %ah # Valid IDs: 0x0000-0x007f/0x0100-0x07ff - jz vesan # Certain BIOSes report 0x80-0xff! - - cmpw $0x0800, %ax - jnc vesae - -vesa2: pushw %cx - movw %ax, %cx # Get mode information structure - movw $0x4f01, %ax - int $0x10 - movw %cx, %bx # BX=mode number - addb $VIDEO_FIRST_VESA>>8, %bh - popw %cx - cmpw $0x004f, %ax - jnz vesan # Don't report errors (buggy BIOSES) - - movb (%di), %al # Check capabilities. We require - andb $0x19, %al # a color text mode. - cmpb $0x09, %al - jnz vesan - - cmpw $0xb800, 8(%di) # Standard video memory address required - jnz vesan - - testb $2, (%di) # Mode characteristics supplied? - movw %bx, (%di) # Store mode number - jz vesa3 - - xorw %dx, %dx - movw 0x12(%di), %bx # Width - orb %bh, %bh - jnz vesan - - movb %bl, 0x3(%di) - movw 0x14(%di), %ax # Height - orb %ah, %ah - jnz vesan - - movb %al, 2(%di) - mulb %bl - cmpw $8193, %ax # Small enough for Linux console driver? - jnc vesan - - jmp vesaok - -vesa3: subw $0x8108, %bx # This mode has no detailed info specified, - jc vesan # so it must be a standard VESA mode. - - cmpw $5, %bx - jnc vesan - - movw vesa_text_mode_table(%bx), %ax - movw %ax, 2(%di) -vesaok: addw $4, %di # The mode is valid. Store it. -vesan: loop vesa1 # Next mode. Limit exceeded => error -vesae: leaw vesaer, %si - call prtstr - movw %bp, %di # Discard already found modes. -vesar: popw %gs - ret - -# Dimensions of standard VESA text modes -vesa_text_mode_table: - .byte 60, 80 # 0108 - .byte 25, 132 # 0109 - .byte 43, 132 # 010A - .byte 50, 132 # 010B - .byte 60, 132 # 010C -#endif /* CONFIG_VIDEO_VESA */ - -# Scan for video modes. A bit dirty, but should work. -mode_scan: - movw $0x0100, %cx # Start with mode 0 -scm1: movb $0, %ah # Test the mode - movb %cl, %al - int $0x10 - movb $0x0f, %ah - int $0x10 - cmpb %cl, %al - jnz scm2 # Mode not set - - movw $0x3c0, %dx # Test if it's a text mode - movb $0x10, %al # Mode bits - call inidx - andb $0x03, %al - jnz scm2 - - movb $0xce, %dl # Another set of mode bits - movb $0x06, %al - call inidx - shrb %al - jc scm2 - - movb $0xd4, %dl # Cursor location - movb $0x0f, %al - call inidx - orb %al, %al - jnz scm2 - - movw %cx, %ax # Ok, store the mode - stosw - movb %gs:(0x484), %al # Number of rows - incb %al - stosb - movw %gs:(0x44a), %ax # Number of columns - stosb -scm2: incb %cl - jns scm1 - - movw $0x0003, %ax # Return back to mode 3 - int $0x10 - ret - -tstidx: outw %ax, %dx # OUT DX,AX and inidx -inidx: outb %al, %dx # Read from indexed VGA register - incw %dx # AL=index, DX=index reg port -> AL=data - inb %dx, %al - decw %dx - ret - -# Try to detect type of SVGA card and supply (usually approximate) video -# mode table for it. - -#ifdef CONFIG_VIDEO_SVGA -svga_modes: - leaw svga_table, %si # Test all known SVGA adapters -dosvga: lodsw - movw %ax, %bp # Default mode table - orw %ax, %ax - jz didsv1 - - lodsw # Pointer to test routine - pushw %si - pushw %di - pushw %es - movw $0xc000, %bx - movw %bx, %es - call *%ax # Call test routine - popw %es - popw %di - popw %si - orw %bp, %bp - jz dosvga - - movw %bp, %si # Found, copy the modes - movb svga_prefix, %ah -cpsvga: lodsb - orb %al, %al - jz didsv - - stosw - movsw - jmp cpsvga - -didsv: movw %si, card_name # Store pointer to card name -didsv1: ret - -# Table of all known SVGA cards. For each card, we store a pointer to -# a table of video modes supported by the card and a pointer to a routine -# used for testing of presence of the card. The video mode table is always -# followed by the name of the card or the chipset. -svga_table: - .word ati_md, ati_test - .word oak_md, oak_test - .word paradise_md, paradise_test - .word realtek_md, realtek_test - .word s3_md, s3_test - .word chips_md, chips_test - .word video7_md, video7_test - .word cirrus5_md, cirrus5_test - .word cirrus6_md, cirrus6_test - .word cirrus1_md, cirrus1_test - .word ahead_md, ahead_test - .word everex_md, everex_test - .word genoa_md, genoa_test - .word trident_md, trident_test - .word tseng_md, tseng_test - .word 0 - -# Test routines and mode tables: - -# S3 - The test algorithm was taken from the SuperProbe package -# for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org -s3_test: - movw $0x0f35, %cx # we store some constants in cl/ch - movw $0x03d4, %dx - movb $0x38, %al - call inidx - movb %al, %bh # store current CRT-register 0x38 - movw $0x0038, %ax - call outidx # disable writing to special regs - movb %cl, %al # check whether we can write special reg 0x35 - call inidx - movb %al, %bl # save the current value of CRT reg 0x35 - andb $0xf0, %al # clear bits 0-3 - movb %al, %ah - movb %cl, %al # and write it to CRT reg 0x35 - call outidx - call inidx # now read it back - andb %ch, %al # clear the upper 4 bits - jz s3_2 # the first test failed. But we have a - - movb %bl, %ah # second chance - movb %cl, %al - call outidx - jmp s3_1 # do the other tests - -s3_2: movw %cx, %ax # load ah with 0xf and al with 0x35 - orb %bl, %ah # set the upper 4 bits of ah with the orig value - call outidx # write ... - call inidx # ... and reread - andb %cl, %al # turn off the upper 4 bits - pushw %ax - movb %bl, %ah # restore old value in register 0x35 - movb %cl, %al - call outidx - popw %ax - cmpb %ch, %al # setting lower 4 bits was successful => bad - je no_s3 # writing is allowed => this is not an S3 - -s3_1: movw $0x4838, %ax # allow writing to special regs by putting - call outidx # magic number into CRT-register 0x38 - movb %cl, %al # check whether we can write special reg 0x35 - call inidx - movb %al, %bl - andb $0xf0, %al - movb %al, %ah - movb %cl, %al - call outidx - call inidx - andb %ch, %al - jnz no_s3 # no, we can't write => no S3 - - movw %cx, %ax - orb %bl, %ah - call outidx - call inidx - andb %ch, %al - pushw %ax - movb %bl, %ah # restore old value in register 0x35 - movb %cl, %al - call outidx - popw %ax - cmpb %ch, %al - jne no_s31 # writing not possible => no S3 - movb $0x30, %al - call inidx # now get the S3 id ... - leaw idS3, %di - movw $0x10, %cx - repne - scasb - je no_s31 - - movb %bh, %ah - movb $0x38, %al - jmp s3rest - -no_s3: movb $0x35, %al # restore CRT register 0x35 - movb %bl, %ah - call outidx -no_s31: xorw %bp, %bp # Detection failed -s3rest: movb %bh, %ah - movb $0x38, %al # restore old value of CRT register 0x38 - jmp outidx - -idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 - .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 - -s3_md: .byte 0x54, 0x2b, 0x84 - .byte 0x55, 0x19, 0x84 - .byte 0 - .ascii "S3" - .byte 0 - -# ATI cards. -ati_test: - leaw idati, %si - movw $0x31, %di - movw $0x09, %cx - repe - cmpsb - je atiok - - xorw %bp, %bp -atiok: ret - -idati: .ascii "761295520" - -ati_md: .byte 0x23, 0x19, 0x84 - .byte 0x33, 0x2c, 0x84 - .byte 0x22, 0x1e, 0x64 - .byte 0x21, 0x19, 0x64 - .byte 0x58, 0x21, 0x50 - .byte 0x5b, 0x1e, 0x50 - .byte 0 - .ascii "ATI" - .byte 0 - -# AHEAD -ahead_test: - movw $0x200f, %ax - movw $0x3ce, %dx - outw %ax, %dx - incw %dx - inb %dx, %al - cmpb $0x20, %al - je isahed - - cmpb $0x21, %al - je isahed - - xorw %bp, %bp -isahed: ret - -ahead_md: - .byte 0x22, 0x2c, 0x84 - .byte 0x23, 0x19, 0x84 - .byte 0x24, 0x1c, 0x84 - .byte 0x2f, 0x32, 0xa0 - .byte 0x32, 0x22, 0x50 - .byte 0x34, 0x42, 0x50 - .byte 0 - .ascii "Ahead" - .byte 0 - -# Chips & Tech. -chips_test: - movw $0x3c3, %dx - inb %dx, %al - orb $0x10, %al - outb %al, %dx - movw $0x104, %dx - inb %dx, %al - movb %al, %bl - movw $0x3c3, %dx - inb %dx, %al - andb $0xef, %al - outb %al, %dx - cmpb $0xa5, %bl - je cantok - - xorw %bp, %bp -cantok: ret - -chips_md: - .byte 0x60, 0x19, 0x84 - .byte 0x61, 0x32, 0x84 - .byte 0 - .ascii "Chips & Technologies" - .byte 0 - -# Cirrus Logic 5X0 -cirrus1_test: - movw $0x3d4, %dx - movb $0x0c, %al - outb %al, %dx - incw %dx - inb %dx, %al - movb %al, %bl - xorb %al, %al - outb %al, %dx - decw %dx - movb $0x1f, %al - outb %al, %dx - incw %dx - inb %dx, %al - movb %al, %bh - xorb %ah, %ah - shlb $4, %al - movw %ax, %cx - movb %bh, %al - shrb $4, %al - addw %ax, %cx - shlw $8, %cx - addw $6, %cx - movw %cx, %ax - movw $0x3c4, %dx - outw %ax, %dx - incw %dx - inb %dx, %al - andb %al, %al - jnz nocirr - - movb %bh, %al - outb %al, %dx - inb %dx, %al - cmpb $0x01, %al - je iscirr - -nocirr: xorw %bp, %bp -iscirr: movw $0x3d4, %dx - movb %bl, %al - xorb %ah, %ah - shlw $8, %ax - addw $0x0c, %ax - outw %ax, %dx - ret - -cirrus1_md: - .byte 0x1f, 0x19, 0x84 - .byte 0x20, 0x2c, 0x84 - .byte 0x22, 0x1e, 0x84 - .byte 0x31, 0x25, 0x64 - .byte 0 - .ascii "Cirrus Logic 5X0" - .byte 0 - -# Cirrus Logic 54XX -cirrus5_test: - movw $0x3c4, %dx - movb $6, %al - call inidx - movb %al, %bl # BL=backup - movw $6, %ax - call tstidx - cmpb $0x0f, %al - jne c5fail - - movw $0x1206, %ax - call tstidx - cmpb $0x12, %al - jne c5fail - - movb $0x1e, %al - call inidx - movb %al, %bh - movb %bh, %ah - andb $0xc0, %ah - movb $0x1e, %al - call tstidx - andb $0x3f, %al - jne c5xx - - movb $0x1e, %al - movb %bh, %ah - orb $0x3f, %ah - call tstidx - xorb $0x3f, %al - andb $0x3f, %al -c5xx: pushf - movb $0x1e, %al - movb %bh, %ah - outw %ax, %dx - popf - je c5done - -c5fail: xorw %bp, %bp -c5done: movb $6, %al - movb %bl, %ah - outw %ax, %dx - ret - -cirrus5_md: - .byte 0x14, 0x19, 0x84 - .byte 0x54, 0x2b, 0x84 - .byte 0 - .ascii "Cirrus Logic 54XX" - .byte 0 - -# Cirrus Logic 64XX -- no known extra modes, but must be identified, because -# it's misidentified by the Ahead test. -cirrus6_test: - movw $0x3ce, %dx - movb $0x0a, %al - call inidx - movb %al, %bl # BL=backup - movw $0xce0a, %ax - call tstidx - orb %al, %al - jne c2fail - - movw $0xec0a, %ax - call tstidx - cmpb $0x01, %al - jne c2fail - - movb $0xaa, %al - call inidx # 4X, 5X, 7X and 8X are valid 64XX chip ID's. - shrb $4, %al - subb $4, %al - jz c6done - - decb %al - jz c6done - - subb $2, %al - jz c6done - - decb %al - jz c6done - -c2fail: xorw %bp, %bp -c6done: movb $0x0a, %al - movb %bl, %ah - outw %ax, %dx - ret - -cirrus6_md: - .byte 0 - .ascii "Cirrus Logic 64XX" - .byte 0 - -# Everex / Trident -everex_test: - movw $0x7000, %ax - xorw %bx, %bx - int $0x10 - cmpb $0x70, %al - jne noevrx - - shrw $4, %dx - cmpw $0x678, %dx - je evtrid - - cmpw $0x236, %dx - jne evrxok - -evtrid: leaw trident_md, %bp -evrxok: ret - -noevrx: xorw %bp, %bp - ret - -everex_md: - .byte 0x03, 0x22, 0x50 - .byte 0x04, 0x3c, 0x50 - .byte 0x07, 0x2b, 0x64 - .byte 0x08, 0x4b, 0x64 - .byte 0x0a, 0x19, 0x84 - .byte 0x0b, 0x2c, 0x84 - .byte 0x16, 0x1e, 0x50 - .byte 0x18, 0x1b, 0x64 - .byte 0x21, 0x40, 0xa0 - .byte 0x40, 0x1e, 0x84 - .byte 0 - .ascii "Everex/Trident" - .byte 0 - -# Genoa. -genoa_test: - leaw idgenoa, %si # Check Genoa 'clues' - xorw %ax, %ax - movb %es:(0x37), %al - movw %ax, %di - movw $0x04, %cx - decw %si - decw %di -l1: incw %si - incw %di - movb (%si), %al - testb %al, %al - jz l2 - - cmpb %es:(%di), %al -l2: loope l1 - orw %cx, %cx - je isgen - - xorw %bp, %bp -isgen: ret - -idgenoa: .byte 0x77, 0x00, 0x99, 0x66 - -genoa_md: - .byte 0x58, 0x20, 0x50 - .byte 0x5a, 0x2a, 0x64 - .byte 0x60, 0x19, 0x84 - .byte 0x61, 0x1d, 0x84 - .byte 0x62, 0x20, 0x84 - .byte 0x63, 0x2c, 0x84 - .byte 0x64, 0x3c, 0x84 - .byte 0x6b, 0x4f, 0x64 - .byte 0x72, 0x3c, 0x50 - .byte 0x74, 0x42, 0x50 - .byte 0x78, 0x4b, 0x64 - .byte 0 - .ascii "Genoa" - .byte 0 - -# OAK -oak_test: - leaw idoakvga, %si - movw $0x08, %di - movw $0x08, %cx - repe - cmpsb - je isoak - - xorw %bp, %bp -isoak: ret - -idoakvga: .ascii "OAK VGA " - -oak_md: .byte 0x4e, 0x3c, 0x50 - .byte 0x4f, 0x3c, 0x84 - .byte 0x50, 0x19, 0x84 - .byte 0x51, 0x2b, 0x84 - .byte 0 - .ascii "OAK" - .byte 0 - -# WD Paradise. -paradise_test: - leaw idparadise, %si - movw $0x7d, %di - movw $0x04, %cx - repe - cmpsb - je ispara - - xorw %bp, %bp -ispara: ret - -idparadise: .ascii "VGA=" - -paradise_md: - .byte 0x41, 0x22, 0x50 - .byte 0x47, 0x1c, 0x84 - .byte 0x55, 0x19, 0x84 - .byte 0x54, 0x2c, 0x84 - .byte 0 - .ascii "Paradise" - .byte 0 - -# Trident. -trident_test: - movw $0x3c4, %dx - movb $0x0e, %al - outb %al, %dx - incw %dx - inb %dx, %al - xchgb %al, %ah - xorb %al, %al - outb %al, %dx - inb %dx, %al - xchgb %ah, %al - movb %al, %bl # Strange thing ... in the book this wasn't - andb $0x02, %bl # necessary but it worked on my card which - jz setb2 # is a trident. Without it the screen goes - # blurred ... - andb $0xfd, %al - jmp clrb2 - -setb2: orb $0x02, %al -clrb2: outb %al, %dx - andb $0x0f, %ah - cmpb $0x02, %ah - je istrid - - xorw %bp, %bp -istrid: ret - -trident_md: - .byte 0x50, 0x1e, 0x50 - .byte 0x51, 0x2b, 0x50 - .byte 0x52, 0x3c, 0x50 - .byte 0x57, 0x19, 0x84 - .byte 0x58, 0x1e, 0x84 - .byte 0x59, 0x2b, 0x84 - .byte 0x5a, 0x3c, 0x84 - .byte 0 - .ascii "Trident" - .byte 0 - -# Tseng. -tseng_test: - movw $0x3cd, %dx - inb %dx, %al # Could things be this simple ! :-) - movb %al, %bl - movb $0x55, %al - outb %al, %dx - inb %dx, %al - movb %al, %ah - movb %bl, %al - outb %al, %dx - cmpb $0x55, %ah - je istsen - -isnot: xorw %bp, %bp -istsen: ret - -tseng_md: - .byte 0x26, 0x3c, 0x50 - .byte 0x2a, 0x28, 0x64 - .byte 0x23, 0x19, 0x84 - .byte 0x24, 0x1c, 0x84 - .byte 0x22, 0x2c, 0x84 - .byte 0x21, 0x3c, 0x84 - .byte 0 - .ascii "Tseng" - .byte 0 - -# Video7. -video7_test: - movw $0x3cc, %dx - inb %dx, %al - movw $0x3b4, %dx - andb $0x01, %al - jz even7 - - movw $0x3d4, %dx -even7: movb $0x0c, %al - outb %al, %dx - incw %dx - inb %dx, %al - movb %al, %bl - movb $0x55, %al - outb %al, %dx - inb %dx, %al - decw %dx - movb $0x1f, %al - outb %al, %dx - incw %dx - inb %dx, %al - movb %al, %bh - decw %dx - movb $0x0c, %al - outb %al, %dx - incw %dx - movb %bl, %al - outb %al, %dx - movb $0x55, %al - xorb $0xea, %al - cmpb %bh, %al - jne isnot - - movb $VIDEO_FIRST_V7>>8, svga_prefix # Use special mode switching - ret - -video7_md: - .byte 0x40, 0x2b, 0x50 - .byte 0x43, 0x3c, 0x50 - .byte 0x44, 0x3c, 0x64 - .byte 0x41, 0x19, 0x84 - .byte 0x42, 0x2c, 0x84 - .byte 0x45, 0x1c, 0x84 - .byte 0 - .ascii "Video 7" - .byte 0 - -# Realtek VGA -realtek_test: - leaw idrtvga, %si - movw $0x45, %di - movw $0x0b, %cx - repe - cmpsb - je isrt - - xorw %bp, %bp -isrt: ret - -idrtvga: .ascii "REALTEK VGA" - -realtek_md: - .byte 0x1a, 0x3c, 0x50 - .byte 0x1b, 0x19, 0x84 - .byte 0x1c, 0x1e, 0x84 - .byte 0x1d, 0x2b, 0x84 - .byte 0x1e, 0x3c, 0x84 - .byte 0 - .ascii "REALTEK" - .byte 0 - -#endif /* CONFIG_VIDEO_SVGA */ - -# User-defined local mode table (VGA only) -#ifdef CONFIG_VIDEO_LOCAL -local_modes: - leaw local_mode_table, %si -locm1: lodsw - orw %ax, %ax - jz locm2 - - stosw - movsw - jmp locm1 - -locm2: ret - -# This is the table of local video modes which can be supplied manually -# by the user. Each entry consists of mode ID (word) and dimensions -# (byte for column count and another byte for row count). These modes -# are placed before all SVGA and VESA modes and override them if table -# compacting is enabled. The table must end with a zero word followed -# by NUL-terminated video adapter name. -local_mode_table: - .word 0x0100 # Example: 40x25 - .byte 25,40 - .word 0 - .ascii "Local" - .byte 0 -#endif /* CONFIG_VIDEO_LOCAL */ - -# Read a key and return the ASCII code in al, scan code in ah -getkey: xorb %ah, %ah - int $0x16 - ret - -# Read a key with a timeout of 30 seconds. -# The hardware clock is used to get the time. -getkt: call gettime - addb $30, %al # Wait 30 seconds - cmpb $60, %al - jl lminute - - subb $60, %al -lminute: - movb %al, %cl -again: movb $0x01, %ah - int $0x16 - jnz getkey # key pressed, so get it - - call gettime - cmpb %cl, %al - jne again - - movb $0x20, %al # timeout, return `space' - ret - -# Flush the keyboard buffer -flush: movb $0x01, %ah - int $0x16 - jz empty - - xorb %ah, %ah - int $0x16 - jmp flush - -empty: ret - -# Print hexadecimal number. -prthw: pushw %ax - movb %ah, %al - call prthb - popw %ax -prthb: pushw %ax - shrb $4, %al - call prthn - popw %ax - andb $0x0f, %al -prthn: cmpb $0x0a, %al - jc prth1 - - addb $0x07, %al -prth1: addb $0x30, %al - jmp prtchr - -# Print decimal number in al -prtdec: pushw %ax - pushw %cx - xorb %ah, %ah - movb $0x0a, %cl - idivb %cl - cmpb $0x09, %al - jbe lt100 - - call prtdec - jmp skip10 - -lt100: addb $0x30, %al - call prtchr -skip10: movb %ah, %al - addb $0x30, %al - call prtchr - popw %cx - popw %ax - ret - -store_edid: -#ifdef CONFIG_FIRMWARE_EDID - pushw %es # just save all registers - pushw %ax - pushw %bx - pushw %cx - pushw %dx - pushw %di - - pushw %fs - popw %es - - movl $0x13131313, %eax # memset block with 0x13 - movw $32, %cx - movw $0x140, %di - cld - rep - stosl - - cmpw $0x0200, vbe_version # only do EDID on >= VBE2.0 - jl no_edid - - pushw %es # save ES - xorw %di, %di # Report Capability - pushw %di - popw %es # ES:DI must be 0:0 - movw $0x4f15, %ax - xorw %bx, %bx - xorw %cx, %cx - int $0x10 - popw %es # restore ES - - cmpb $0x00, %ah # call successful - jne no_edid - - cmpb $0x4f, %al # function supported - jne no_edid - - movw $0x4f15, %ax # do VBE/DDC - movw $0x01, %bx - movw $0x00, %cx - movw $0x00, %dx - movw $0x140, %di - int $0x10 - -no_edid: - popw %di # restore all registers - popw %dx - popw %cx - popw %bx - popw %ax - popw %es -#endif - ret - -# VIDEO_SELECT-only variables -mt_end: .word 0 # End of video mode table if built -edit_buf: .space 6 # Line editor buffer -card_name: .word 0 # Pointer to adapter name -scanning: .byte 0 # Performing mode scan -do_restore: .byte 0 # Screen contents altered during mode change -svga_prefix: .byte VIDEO_FIRST_BIOS>>8 # Default prefix for BIOS modes -graphic_mode: .byte 0 # Graphic mode with a linear frame buffer -dac_size: .byte 6 # DAC bit depth -vbe_version: .word 0 # VBE bios version - -# Status messages -keymsg: .ascii "Press <RETURN> to see video modes available, " - .ascii "<SPACE> to continue or wait 30 secs" - .byte 0x0d, 0x0a, 0 - -listhdr: .byte 0x0d, 0x0a - .ascii "Mode: COLSxROWS:" - -crlft: .byte 0x0d, 0x0a, 0 - -prompt: .byte 0x0d, 0x0a - .asciz "Enter mode number or `scan': " - -unknt: .asciz "Unknown mode ID. Try again." - -badmdt: .ascii "You passed an undefined mode number." - .byte 0x0d, 0x0a, 0 - -vesaer: .ascii "Error: Scanning of VESA modes failed. Please " - .ascii "report to <mj@ucw.cz>." - .byte 0x0d, 0x0a, 0 - -old_name: .asciz "CGA/MDA/HGA" - -ega_name: .asciz "EGA" - -svga_name: .ascii " " - -vga_name: .asciz "VGA" - -vesa_name: .asciz "VESA" - -name_bann: .asciz "Video adapter: " -#endif /* CONFIG_VIDEO_SELECT */ - -# Other variables: -adapter: .byte 0 # Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA -video_segment: .word 0xb800 # Video memory segment -force_size: .word 0 # Use this size instead of the one in BIOS vars diff --git a/arch/i386/boot/video.c b/arch/i386/boot/video.c new file mode 100644 index 00000000000..3bb3573cd6a --- /dev/null +++ b/arch/i386/boot/video.c @@ -0,0 +1,456 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/video.c + * + * Select video mode + */ + +#include "boot.h" +#include "video.h" +#include "vesa.h" + +/* + * Mode list variables + */ +static struct card_info cards[]; /* List of cards to probe for */ + +/* + * Common variables + */ +int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ +u16 video_segment; +int force_x, force_y; /* Don't query the BIOS for cols/rows */ + +int do_restore = 0; /* Screen contents changed during mode flip */ +int graphic_mode; /* Graphic mode with linear frame buffer */ + +static void store_cursor_position(void) +{ + u16 curpos; + u16 ax, bx; + + ax = 0x0300; + bx = 0; + asm(INT10 + : "=d" (curpos), "+a" (ax), "+b" (bx) + : : "ecx", "esi", "edi"); + + boot_params.screen_info.orig_x = curpos; + boot_params.screen_info.orig_y = curpos >> 8; +} + +static void store_video_mode(void) +{ + u16 ax, page; + + /* N.B.: the saving of the video page here is a bit silly, + since we pretty much assume page 0 everywhere. */ + ax = 0x0f00; + asm(INT10 + : "+a" (ax), "=b" (page) + : : "ecx", "edx", "esi", "edi"); + + /* Not all BIOSes are clean with respect to the top bit */ + boot_params.screen_info.orig_video_mode = ax & 0x7f; + boot_params.screen_info.orig_video_page = page; +} + +/* + * Store the video mode parameters for later usage by the kernel. + * This is done by asking the BIOS except for the rows/columns + * parameters in the default 80x25 mode -- these are set directly, + * because some very obscure BIOSes supply insane values. + */ +static void store_mode_params(void) +{ + u16 font_size; + int x, y; + + /* For graphics mode, it is up to the mode-setting driver + (currently only video-vesa.c) to store the parameters */ + if (graphic_mode) + return; + + store_cursor_position(); + store_video_mode(); + + if (boot_params.screen_info.orig_video_mode == 0x07) { + /* MDA, HGC, or VGA in monochrome mode */ + video_segment = 0xb000; + } else { + /* CGA, EGA, VGA and so forth */ + video_segment = 0xb800; + } + + set_fs(0); + font_size = rdfs16(0x485); /* Font size, BIOS area */ + boot_params.screen_info.orig_video_points = font_size; + + x = rdfs16(0x44a); + y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; + + if (force_x) + x = force_x; + if (force_y) + y = force_y; + + boot_params.screen_info.orig_video_cols = x; + boot_params.screen_info.orig_video_lines = y; +} + +/* Probe the video drivers and have them generate their mode lists. */ +static void probe_cards(int unsafe) +{ + struct card_info *card; + static u8 probed[2]; + + if (probed[unsafe]) + return; + + probed[unsafe] = 1; + + for (card = video_cards; card < video_cards_end; card++) { + if (card->unsafe == unsafe) { + if (card->probe) + card->nmodes = card->probe(); + else + card->nmodes = 0; + } + } +} + +/* Test if a mode is defined */ +int mode_defined(u16 mode) +{ + struct card_info *card; + struct mode_info *mi; + int i; + + for (card = video_cards; card < video_cards_end; card++) { + mi = card->modes; + for (i = 0; i < card->nmodes; i++, mi++) { + if (mi->mode == mode) + return 1; + } + } + + return 0; +} + +/* Set mode (without recalc) */ +static int raw_set_mode(u16 mode) +{ + int nmode, i; + struct card_info *card; + struct mode_info *mi; + + /* Drop the recalc bit if set */ + mode &= ~VIDEO_RECALC; + + /* Scan for mode based on fixed ID, position, or resolution */ + nmode = 0; + for (card = video_cards; card < video_cards_end; card++) { + mi = card->modes; + for (i = 0; i < card->nmodes; i++, mi++) { + int visible = mi->x || mi->y; + + if ((mode == nmode && visible) || + mode == mi->mode || + mode == (mi->y << 8)+mi->x) + return card->set_mode(mi); + + if (visible) + nmode++; + } + } + + /* Nothing found? Is it an "exceptional" (unprobed) mode? */ + for (card = video_cards; card < video_cards_end; card++) { + if (mode >= card->xmode_first && + mode < card->xmode_first+card->xmode_n) { + struct mode_info mix; + mix.mode = mode; + mix.x = mix.y = 0; + return card->set_mode(&mix); + } + } + + /* Otherwise, failure... */ + return -1; +} + +/* + * Recalculate the vertical video cutoff (hack!) + */ +static void vga_recalc_vertical(void) +{ + unsigned int font_size, rows; + u16 crtc; + u8 ov; + + set_fs(0); + font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ + rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ + + rows *= font_size; /* Visible scan lines */ + rows--; /* ... minus one */ + + crtc = vga_crtc(); + + out_idx((u8)rows, crtc, 0x12); /* Lower height register */ + ov = in_idx(crtc, 0x07); /* Overflow register */ + ov &= 0xbd; + ov |= (rows >> (8-1)) & 0x02; + ov |= (rows >> (9-6)) & 0x40; + out_idx(ov, crtc, 0x07); +} + +/* Set mode (with recalc if specified) */ +static int set_mode(u16 mode) +{ + int rv; + + /* Very special mode numbers... */ + if (mode == VIDEO_CURRENT_MODE) + return 0; /* Nothing to do... */ + else if (mode == NORMAL_VGA) + mode = VIDEO_80x25; + else if (mode == EXTENDED_VGA) + mode = VIDEO_8POINT; + + rv = raw_set_mode(mode); + if (rv) + return rv; + + if (mode & VIDEO_RECALC) + vga_recalc_vertical(); + + return 0; +} + +static unsigned int get_entry(void) +{ + char entry_buf[4]; + int i, len = 0; + int key; + unsigned int v; + + do { + key = getchar(); + + if (key == '\b') { + if (len > 0) { + puts("\b \b"); + len--; + } + } else if ((key >= '0' && key <= '9') || + (key >= 'A' && key <= 'Z') || + (key >= 'a' && key <= 'z')) { + if (len < sizeof entry_buf) { + entry_buf[len++] = key; + putchar(key); + } + } + } while (key != '\r'); + putchar('\n'); + + if (len == 0) + return VIDEO_CURRENT_MODE; /* Default */ + + v = 0; + for (i = 0; i < len; i++) { + v <<= 4; + key = entry_buf[i] | 0x20; + v += (key > '9') ? key-'a'+10 : key-'0'; + } + + return v; +} + +static void display_menu(void) +{ + struct card_info *card; + struct mode_info *mi; + char ch; + int i; + + puts("Mode: COLSxROWS:\n"); + + ch = '0'; + for (card = video_cards; card < video_cards_end; card++) { + mi = card->modes; + for (i = 0; i < card->nmodes; i++, mi++) { + int visible = mi->x && mi->y; + u16 mode_id = mi->mode ? mi->mode : + (mi->y << 8)+mi->x; + + if (!visible) + continue; /* Hidden mode */ + + printf("%c %04X %3dx%-3d %s\n", + ch, mode_id, mi->x, mi->y, card->card_name); + + if (ch == '9') + ch = 'a'; + else if (ch == 'z' || ch == ' ') + ch = ' '; /* Out of keys... */ + else + ch++; + } + } +} + +#define H(x) ((x)-'a'+10) +#define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) + +static unsigned int mode_menu(void) +{ + int key; + unsigned int sel; + + puts("Press <ENTER> to see video modes available, " + "<SPACE> to continue, or wait 30 sec\n"); + + kbd_flush(); + while (1) { + key = getchar_timeout(); + if (key == ' ' || key == 0) + return VIDEO_CURRENT_MODE; /* Default */ + if (key == '\r') + break; + putchar('\a'); /* Beep! */ + } + + + for (;;) { + display_menu(); + + puts("Enter a video mode or \"scan\" to scan for " + "additional modes: "); + sel = get_entry(); + if (sel != SCAN) + return sel; + + probe_cards(1); + } +} + +#ifdef CONFIG_VIDEO_RETAIN +/* Save screen content to the heap */ +struct saved_screen { + int x, y; + int curx, cury; + u16 *data; +} saved; + +static void save_screen(void) +{ + /* Should be called after store_mode_params() */ + saved.x = boot_params.screen_info.orig_video_cols; + saved.y = boot_params.screen_info.orig_video_lines; + saved.curx = boot_params.screen_info.orig_x; + saved.cury = boot_params.screen_info.orig_y; + + if (heap_free() < saved.x*saved.y*sizeof(u16)+512) + return; /* Not enough heap to save the screen */ + + saved.data = GET_HEAP(u16, saved.x*saved.y); + + set_fs(video_segment); + copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); +} + +static void restore_screen(void) +{ + /* Should be called after store_mode_params() */ + int xs = boot_params.screen_info.orig_video_cols; + int ys = boot_params.screen_info.orig_video_lines; + int y; + addr_t dst = 0; + u16 *src = saved.data; + u16 ax, bx, dx; + + if (graphic_mode) + return; /* Can't restore onto a graphic mode */ + + if (!src) + return; /* No saved screen contents */ + + /* Restore screen contents */ + + set_fs(video_segment); + for (y = 0; y < ys; y++) { + int npad; + + if (y < saved.y) { + int copy = (xs < saved.x) ? xs : saved.x; + copy_to_fs(dst, src, copy*sizeof(u16)); + dst += copy*sizeof(u16); + src += saved.x; + npad = (xs < saved.x) ? 0 : xs-saved.x; + } else { + npad = xs; + } + + /* Writes "npad" blank characters to + video_segment:dst and advances dst */ + asm volatile("pushw %%es ; " + "movw %2,%%es ; " + "shrw %%cx ; " + "jnc 1f ; " + "stosw \n\t" + "1: rep;stosl ; " + "popw %%es" + : "+D" (dst), "+c" (npad) + : "bdSm" (video_segment), + "a" (0x07200720)); + } + + /* Restore cursor position */ + ax = 0x0200; /* Set cursor position */ + bx = 0; /* Page number (<< 8) */ + dx = (saved.cury << 8)+saved.curx; + asm volatile(INT10 + : "+a" (ax), "+b" (bx), "+d" (dx) + : : "ecx", "esi", "edi"); +} +#else +#define save_screen() ((void)0) +#define restore_screen() ((void)0) +#endif + +void set_video(void) +{ + u16 mode = boot_params.hdr.vid_mode; + + RESET_HEAP(); + + store_mode_params(); + save_screen(); + probe_cards(0); + + for (;;) { + if (mode == ASK_VGA) + mode = mode_menu(); + + if (!set_mode(mode)) + break; + + printf("Undefined video mode number: %x\n", mode); + mode = ASK_VGA; + } + vesa_store_edid(); + store_mode_params(); + + if (do_restore) + restore_screen(); +} diff --git a/arch/i386/boot/video.h b/arch/i386/boot/video.h new file mode 100644 index 00000000000..29eca1710b2 --- /dev/null +++ b/arch/i386/boot/video.h @@ -0,0 +1,145 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/video.h + * + * Header file for the real-mode video probing code + */ + +#ifndef BOOT_VIDEO_H +#define BOOT_VIDEO_H + +#include <linux/types.h> + +/* Enable autodetection of SVGA adapters and modes. */ +#undef CONFIG_VIDEO_SVGA + +/* Enable autodetection of VESA modes */ +#define CONFIG_VIDEO_VESA + +/* Retain screen contents when switching modes */ +#define CONFIG_VIDEO_RETAIN + +/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */ +#undef CONFIG_VIDEO_400_HACK + +/* This code uses an extended set of video mode numbers. These include: + * Aliases for standard modes + * NORMAL_VGA (-1) + * EXTENDED_VGA (-2) + * ASK_VGA (-3) + * Video modes numbered by menu position -- NOT RECOMMENDED because of lack + * of compatibility when extending the table. These are between 0x00 and 0xff. + */ +#define VIDEO_FIRST_MENU 0x0000 + +/* Standard BIOS video modes (BIOS number + 0x0100) */ +#define VIDEO_FIRST_BIOS 0x0100 + +/* VESA BIOS video modes (VESA number + 0x0200) */ +#define VIDEO_FIRST_VESA 0x0200 + +/* Video7 special modes (BIOS number + 0x0900) */ +#define VIDEO_FIRST_V7 0x0900 + +/* Special video modes */ +#define VIDEO_FIRST_SPECIAL 0x0f00 +#define VIDEO_80x25 0x0f00 +#define VIDEO_8POINT 0x0f01 +#define VIDEO_80x43 0x0f02 +#define VIDEO_80x28 0x0f03 +#define VIDEO_CURRENT_MODE 0x0f04 +#define VIDEO_80x30 0x0f05 +#define VIDEO_80x34 0x0f06 +#define VIDEO_80x60 0x0f07 +#define VIDEO_GFX_HACK 0x0f08 +#define VIDEO_LAST_SPECIAL 0x0f09 + +/* Video modes given by resolution */ +#define VIDEO_FIRST_RESOLUTION 0x1000 + +/* The "recalculate timings" flag */ +#define VIDEO_RECALC 0x8000 + +/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ +#ifdef CONFIG_VIDEO_RETAIN +void store_screen(void); +#define DO_STORE() store_screen() +#else +#define DO_STORE() ((void)0) +#endif /* CONFIG_VIDEO_RETAIN */ + +/* + * Mode table structures + */ + +struct mode_info { + u16 mode; /* Mode number (vga= style) */ + u8 x, y; /* Width, height */ +}; + +struct card_info { + const char *card_name; + int (*set_mode)(struct mode_info *mode); + int (*probe)(void); + struct mode_info *modes; + int nmodes; /* Number of probed modes so far */ + int unsafe; /* Probing is unsafe, only do after "scan" */ + u16 xmode_first; /* Unprobed modes to try to call anyway */ + u16 xmode_n; /* Size of unprobed mode range */ +}; + +#define __videocard struct card_info __attribute__((section(".videocards"))) +extern struct card_info video_cards[], video_cards_end[]; + +int mode_defined(u16 mode); /* video.c */ + +/* Basic video information */ +#define ADAPTER_CGA 0 /* CGA/MDA/HGC */ +#define ADAPTER_EGA 1 +#define ADAPTER_VGA 2 + +extern int adapter; +extern u16 video_segment; +extern int force_x, force_y; /* Don't query the BIOS for cols/rows */ +extern int do_restore; /* Restore screen contents */ +extern int graphic_mode; /* Graphics mode with linear frame buffer */ + +/* + * int $0x10 is notorious for touching registers it shouldn't. + * gcc doesn't like %ebp being clobbered, so define it as a push/pop + * sequence here. + */ +#define INT10 "pushl %%ebp; int $0x10; popl %%ebp" + +/* Accessing VGA indexed registers */ +static inline u8 in_idx(u16 port, u8 index) +{ + outb(index, port); + return inb(port+1); +} + +static inline void out_idx(u8 v, u16 port, u8 index) +{ + outw(index+(v << 8), port); +} + +/* Writes a value to an indexed port and then reads the port again */ +static inline u8 tst_idx(u8 v, u16 port, u8 index) +{ + out_idx(port, index, v); + return in_idx(port, index); +} + +/* Get the I/O port of the VGA CRTC */ +u16 vga_crtc(void); /* video-vga.c */ + +#endif /* BOOT_VIDEO_H */ diff --git a/arch/i386/boot/voyager.c b/arch/i386/boot/voyager.c new file mode 100644 index 00000000000..9221614d0db --- /dev/null +++ b/arch/i386/boot/voyager.c @@ -0,0 +1,46 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + +/* + * arch/i386/boot/voyager.c + * + * Get the Voyager config information + */ + +#include "boot.h" + +#ifdef CONFIG_X86_VOYAGER + +int query_voyager(void) +{ + u8 err; + u16 es, di; + /* Abuse the apm_bios_info area for this */ + u8 *data_ptr = (u8 *)&boot_params.apm_bios_info; + + data_ptr[0] = 0xff; /* Flag on config not found(?) */ + + asm("pushw %%es ; " + "int $0x15 ; " + "setc %0 ; " + "movw %%es, %1 ; " + "popw %%es" + : "=qm" (err), "=rm" (es), "=D" (di) + : "a" (0xffc0)); + + if (err) + return -1; /* Not Voyager */ + + set_fs(es); + copy_from_fs(data_ptr, di, 7); /* Table is 7 bytes apparently */ + return 0; +} + +#endif /* CONFIG_X86_VOYAGER */ diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 280898b045b..a574cd2c8b6 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -621,8 +621,6 @@ static int __init acpi_parse_sbf(struct acpi_table_header *table) static int __init acpi_parse_hpet(struct acpi_table_header *table) { struct acpi_table_hpet *hpet_tbl; - struct resource *hpet_res; - resource_size_t res_start; hpet_tbl = (struct acpi_table_hpet *)table; if (!hpet_tbl) { @@ -636,29 +634,10 @@ static int __init acpi_parse_hpet(struct acpi_table_header *table) return -1; } -#define HPET_RESOURCE_NAME_SIZE 9 - hpet_res = alloc_bootmem(sizeof(*hpet_res) + HPET_RESOURCE_NAME_SIZE); - if (hpet_res) { - memset(hpet_res, 0, sizeof(*hpet_res)); - hpet_res->name = (void *)&hpet_res[1]; - hpet_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - snprintf((char *)hpet_res->name, HPET_RESOURCE_NAME_SIZE, - "HPET %u", hpet_tbl->sequence); - hpet_res->end = (1 * 1024) - 1; - } - hpet_address = hpet_tbl->address.address; printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n", hpet_tbl->id, hpet_address); - res_start = hpet_address; - - if (hpet_res) { - hpet_res->start = res_start; - hpet_res->end += res_start; - insert_resource(&iomem_resource, hpet_res); - } - return 0; } #else diff --git a/arch/i386/kernel/acpi/wakeup.S b/arch/i386/kernel/acpi/wakeup.S index b781b38131c..a2295a34b2c 100644 --- a/arch/i386/kernel/acpi/wakeup.S +++ b/arch/i386/kernel/acpi/wakeup.S @@ -230,6 +230,7 @@ bogus_magic: # ENTRY(acpi_copy_wakeup_routine) + pushl %ebx sgdt saved_gdt sidt saved_idt sldt saved_ldt @@ -263,6 +264,7 @@ ENTRY(acpi_copy_wakeup_routine) movl %edx, video_flags - wakeup_start (%eax) movl $0x12345678, real_magic - wakeup_start (%eax) movl $0x12345678, saved_magic + popl %ebx ret save_registers: diff --git a/arch/i386/kernel/cpu/Makefile b/arch/i386/kernel/cpu/Makefile index 74f27a463db..0b6a8551e9e 100644 --- a/arch/i386/kernel/cpu/Makefile +++ b/arch/i386/kernel/cpu/Makefile @@ -8,7 +8,7 @@ obj-y += amd.o obj-y += cyrix.o obj-y += centaur.o obj-y += transmeta.o -obj-y += intel.o intel_cacheinfo.o +obj-y += intel.o intel_cacheinfo.o addon_cpuid_features.o obj-y += rise.o obj-y += nexgen.o obj-y += umc.o diff --git a/arch/i386/kernel/cpu/addon_cpuid_features.c b/arch/i386/kernel/cpu/addon_cpuid_features.c new file mode 100644 index 00000000000..3e91d3ee26e --- /dev/null +++ b/arch/i386/kernel/cpu/addon_cpuid_features.c @@ -0,0 +1,50 @@ + +/* + * Routines to indentify additional cpu features that are scattered in + * cpuid space. + */ + +#include <linux/cpu.h> + +#include <asm/processor.h> + +struct cpuid_bit { + u16 feature; + u8 reg; + u8 bit; + u32 level; +}; + +enum cpuid_regs { + CR_EAX = 0, + CR_ECX, + CR_EDX, + CR_EBX +}; + +void __cpuinit init_scattered_cpuid_features(struct cpuinfo_x86 *c) +{ + u32 max_level; + u32 regs[4]; + const struct cpuid_bit *cb; + + static const struct cpuid_bit cpuid_bits[] = { + { X86_FEATURE_IDA, CR_EAX, 1, 0x00000006 }, + { 0, 0, 0, 0 } + }; + + for (cb = cpuid_bits; cb->feature; cb++) { + + /* Verify that the level is valid */ + max_level = cpuid_eax(cb->level & 0xffff0000); + if (max_level < cb->level || + max_level > (cb->level | 0xffff)) + continue; + + cpuid(cb->level, ®s[CR_EAX], ®s[CR_EBX], + ®s[CR_ECX], ®s[CR_EDX]); + + if (regs[cb->reg] & (1 << cb->bit)) + set_bit(cb->feature, c->x86_capability); + } +} diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c index 794d593c47e..e5419a9dec8 100644 --- a/arch/i386/kernel/cpu/common.c +++ b/arch/i386/kernel/cpu/common.c @@ -353,6 +353,8 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 * c) if ( xlvl >= 0x80000004 ) get_model_name(c); /* Default name */ } + + init_scattered_cpuid_features(c); } early_intel_workaround(c); diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index e912aae9473..094118ba00d 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig @@ -90,10 +90,17 @@ config X86_POWERNOW_K8 If in doubt, say N. config X86_POWERNOW_K8_ACPI - bool - depends on X86_POWERNOW_K8 && ACPI_PROCESSOR - depends on !(X86_POWERNOW_K8 = y && ACPI_PROCESSOR = m) + bool "ACPI Support" + select ACPI_PROCESSOR + depends on X86_POWERNOW_K8 default y + help + This provides access to the K8s Processor Performance States via ACPI. + This driver is probably required for CPUFreq to work with multi-socket and + SMP systems. It is not required on at least some single-socket yet + multi-core systems, even if SMP is enabled. + + It is safe to say Y here. config X86_GX_SUSPMOD tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation" @@ -109,7 +116,7 @@ config X86_GX_SUSPMOD config X86_SPEEDSTEP_CENTRINO tristate "Intel Enhanced SpeedStep" select CPU_FREQ_TABLE - select X86_SPEEDSTEP_CENTRINO_TABLE if (!X86_SPEEDSTEP_CENTRINO_ACPI) + select X86_SPEEDSTEP_CENTRINO_TABLE help This adds the CPUFreq driver for Enhanced SpeedStep enabled mobile CPUs. This means Intel Pentium M (Centrino) CPUs. However, @@ -121,20 +128,6 @@ config X86_SPEEDSTEP_CENTRINO If in doubt, say N. -config X86_SPEEDSTEP_CENTRINO_ACPI - bool "Use ACPI tables to decode valid frequency/voltage (deprecated)" - depends on X86_SPEEDSTEP_CENTRINO && ACPI_PROCESSOR - depends on !(X86_SPEEDSTEP_CENTRINO = y && ACPI_PROCESSOR = m) - help - This is deprecated and this functionality is now merged into - acpi_cpufreq (X86_ACPI_CPUFREQ). Use that driver instead of - speedstep_centrino. - Use primarily the information provided in the BIOS ACPI tables - to determine valid CPU frequency and voltage pairings. It is - required for the driver to work on non-Banias CPUs. - - If in doubt, say Y. - config X86_SPEEDSTEP_CENTRINO_TABLE bool "Built-in tables for Banias CPUs" depends on X86_SPEEDSTEP_CENTRINO @@ -230,7 +223,7 @@ comment "shared options" config X86_ACPI_CPUFREQ_PROC_INTF bool "/proc/acpi/processor/../performance interface (deprecated)" depends on PROC_FS - depends on X86_ACPI_CPUFREQ || X86_SPEEDSTEP_CENTRINO_ACPI || X86_POWERNOW_K7_ACPI || X86_POWERNOW_K8_ACPI + depends on X86_ACPI_CPUFREQ || X86_POWERNOW_K7_ACPI || X86_POWERNOW_K8_ACPI help This enables the deprecated /proc/acpi/processor/../performance interface. While it is helpful for debugging, the generic, diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 10baa3501ed..18c8b67ea3a 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -167,11 +167,13 @@ static void do_drv_read(struct drv_cmd *cmd) static void do_drv_write(struct drv_cmd *cmd) { - u32 h = 0; + u32 lo, hi; switch (cmd->type) { case SYSTEM_INTEL_MSR_CAPABLE: - wrmsr(cmd->addr.msr.reg, cmd->val, h); + rdmsr(cmd->addr.msr.reg, lo, hi); + lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); + wrmsr(cmd->addr.msr.reg, lo, hi); break; case SYSTEM_IO_CAPABLE: acpi_os_write_port((acpi_io_address)cmd->addr.io.port, @@ -372,7 +374,6 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, struct cpufreq_freqs freqs; cpumask_t online_policy_cpus; struct drv_cmd cmd; - unsigned int msr; unsigned int next_state = 0; /* Index into freq_table */ unsigned int next_perf_state = 0; /* Index into perf table */ unsigned int i; @@ -417,11 +418,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, case SYSTEM_INTEL_MSR_CAPABLE: cmd.type = SYSTEM_INTEL_MSR_CAPABLE; cmd.addr.msr.reg = MSR_IA32_PERF_CTL; - msr = - (u32) perf->states[next_perf_state]. - control & INTEL_MSR_RANGE; - cmd.val = get_cur_val(online_policy_cpus); - cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr; + cmd.val = (u32) perf->states[next_perf_state].control; break; case SYSTEM_IO_CAPABLE: cmd.type = SYSTEM_IO_CAPABLE; diff --git a/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c b/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c index 0d49d73d1b7..66acd503991 100644 --- a/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c +++ b/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c @@ -391,8 +391,6 @@ static struct cpufreq_driver nforce2_driver = { */ static unsigned int nforce2_detect_chipset(void) { - u8 revision; - nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, PCI_ANY_ID, PCI_ANY_ID, NULL); @@ -400,10 +398,8 @@ static unsigned int nforce2_detect_chipset(void) if (nforce2_chipset_dev == NULL) return -ENODEV; - pci_read_config_byte(nforce2_chipset_dev, PCI_REVISION_ID, &revision); - printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n", - revision); + nforce2_chipset_dev->revision); printk(KERN_INFO "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n"); diff --git a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c index 6667e9cceb9..194144539a6 100644 --- a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c +++ b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c @@ -115,7 +115,6 @@ struct gxfreq_params { u8 pci_suscfg; u8 pci_pmer1; u8 pci_pmer2; - u8 pci_rev; struct pci_dev *cs55x0; }; @@ -276,7 +275,7 @@ static void gx_set_cpuspeed(unsigned int khz) pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/* typical 50 to 100ms */ pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1); - if (gx_params->pci_rev < 0x10) { /* CS5530(rev 1.2, 1.3) */ + if (gx_params->cs55x0->revision < 0x10) { /* CS5530(rev 1.2, 1.3) */ suscfg = gx_params->pci_suscfg | SUSMOD; } else { /* CS5530A,B.. */ suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE; @@ -471,7 +470,6 @@ static int __init cpufreq_gx_init(void) pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2)); pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration)); pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration)); - pci_read_config_byte(params->cs55x0, PCI_REVISION_ID, ¶ms->pci_rev); if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) { kfree(params); diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index a3df9c039bd..ef8f0bc3fc7 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c @@ -29,6 +29,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/delay.h> #include <asm/msr.h> #include <asm/timex.h> @@ -55,7 +56,6 @@ /* Flags */ #define USE_ACPI_C3 (1 << 1) #define USE_NORTHBRIDGE (1 << 2) -#define USE_VT8235 (1 << 3) static int cpu_model; static unsigned int numscales=16; @@ -63,22 +63,19 @@ static unsigned int fsb; static const struct mV_pos *vrm_mV_table; static const unsigned char *mV_vrm_table; -struct f_msr { - u8 vrm; - u8 pos; -}; -static struct f_msr f_msr_table[32]; static unsigned int highest_speed, lowest_speed; /* kHz */ static unsigned int minmult, maxmult; static int can_scale_voltage; static struct acpi_processor *pr = NULL; static struct acpi_processor_cx *cx = NULL; +static u32 acpi_regs_addr; static u8 longhaul_flags; -static u8 longhaul_pos; +static unsigned int longhaul_index; /* Module parameters */ static int scale_voltage; +static int disable_acpi_c3; #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg) @@ -144,7 +141,7 @@ static void do_longhaul1(unsigned int clock_ratio_index) rdmsrl(MSR_VIA_BCR2, bcr2.val); /* Enable software clock multiplier */ bcr2.bits.ESOFTBF = 1; - bcr2.bits.CLOCKMUL = clock_ratio_index; + bcr2.bits.CLOCKMUL = clock_ratio_index & 0xff; /* Sync to timer tick */ safe_halt(); @@ -163,14 +160,12 @@ static void do_longhaul1(unsigned int clock_ratio_index) /* For processor with Longhaul MSR */ -static void do_powersaver(int cx_address, unsigned int clock_ratio_index) +static void do_powersaver(int cx_address, unsigned int clock_ratio_index, + unsigned int dir) { union msr_longhaul longhaul; - u8 dest_pos; u32 t; - dest_pos = f_msr_table[clock_ratio_index].pos; - rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Setup new frequency */ longhaul.bits.RevisionKey = longhaul.bits.RevisionID; @@ -178,11 +173,11 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; /* Setup new voltage */ if (can_scale_voltage) - longhaul.bits.SoftVID = f_msr_table[clock_ratio_index].vrm; + longhaul.bits.SoftVID = (clock_ratio_index >> 8) & 0x1f; /* Sync to timer tick */ safe_halt(); /* Raise voltage if necessary */ - if (can_scale_voltage && longhaul_pos < dest_pos) { + if (can_scale_voltage && dir) { longhaul.bits.EnableSoftVID = 1; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Change voltage */ @@ -199,7 +194,6 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) } longhaul.bits.EnableSoftVID = 0; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); - longhaul_pos = dest_pos; } /* Change frequency on next halt or sleep */ @@ -220,7 +214,7 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Reduce voltage if necessary */ - if (can_scale_voltage && longhaul_pos > dest_pos) { + if (can_scale_voltage && !dir) { longhaul.bits.EnableSoftVID = 1; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); /* Change voltage */ @@ -237,7 +231,6 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) } longhaul.bits.EnableSoftVID = 0; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); - longhaul_pos = dest_pos; } } @@ -248,25 +241,28 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index) * Sets a new clock ratio. */ -static void longhaul_setstate(unsigned int clock_ratio_index) +static void longhaul_setstate(unsigned int table_index) { + unsigned int clock_ratio_index; int speed, mult; struct cpufreq_freqs freqs; - static unsigned int old_ratio=-1; unsigned long flags; unsigned int pic1_mask, pic2_mask; + u16 bm_status = 0; + u32 bm_timeout = 1000; + unsigned int dir = 0; - if (old_ratio == clock_ratio_index) - return; - old_ratio = clock_ratio_index; - - mult = clock_ratio[clock_ratio_index]; + clock_ratio_index = longhaul_table[table_index].index; + /* Safety precautions */ + mult = clock_ratio[clock_ratio_index & 0x1f]; if (mult == -1) return; - speed = calc_speed(mult); if ((speed > highest_speed) || (speed < lowest_speed)) return; + /* Voltage transition before frequency transition? */ + if (can_scale_voltage && longhaul_index < table_index) + dir = 1; freqs.old = calc_speed(longhaul_get_cpu_mult()); freqs.new = speed; @@ -285,11 +281,24 @@ static void longhaul_setstate(unsigned int clock_ratio_index) outb(0xFF,0xA1); /* Overkill */ outb(0xFE,0x21); /* TMR0 only */ + /* Wait while PCI bus is busy. */ + if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE + || ((pr != NULL) && pr->flags.bm_control))) { + bm_status = inw(acpi_regs_addr); + bm_status &= 1 << 4; + while (bm_status && bm_timeout) { + outw(1 << 4, acpi_regs_addr); + bm_timeout--; + bm_status = inw(acpi_regs_addr); + bm_status &= 1 << 4; + } + } + if (longhaul_flags & USE_NORTHBRIDGE) { /* Disable AGP and PCI arbiters */ outb(3, 0x22); } else if ((pr != NULL) && pr->flags.bm_control) { - /* Disable bus master arbitration */ + /* Disable bus master arbitration */ acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); } switch (longhaul_version) { @@ -314,9 +323,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) if (longhaul_flags & USE_ACPI_C3) { /* Don't allow wakeup */ acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); - do_powersaver(cx->address, clock_ratio_index); + do_powersaver(cx->address, clock_ratio_index, dir); } else { - do_powersaver(0, clock_ratio_index); + do_powersaver(0, clock_ratio_index, dir); } break; } @@ -336,6 +345,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) freqs.new = calc_speed(longhaul_get_cpu_mult()); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + if (!bm_timeout) + printk(KERN_INFO PFX "Warning: Timeout while waiting for idle PCI bus.\n"); } /* @@ -369,7 +381,8 @@ static int guess_fsb(int mult) static int __init longhaul_get_ranges(void) { - unsigned int j, k = 0; + unsigned int i, j, k = 0; + unsigned int ratio; int mult; /* Get current frequency */ @@ -423,8 +436,7 @@ static int __init longhaul_get_ranges(void) if(!longhaul_table) return -ENOMEM; - for (j=0; j < numscales; j++) { - unsigned int ratio; + for (j = 0; j < numscales; j++) { ratio = clock_ratio[j]; if (ratio == -1) continue; @@ -434,13 +446,41 @@ static int __init longhaul_get_ranges(void) longhaul_table[k].index = j; k++; } + if (k <= 1) { + kfree(longhaul_table); + return -ENODEV; + } + /* Sort */ + for (j = 0; j < k - 1; j++) { + unsigned int min_f, min_i; + min_f = longhaul_table[j].frequency; + min_i = j; + for (i = j + 1; i < k; i++) { + if (longhaul_table[i].frequency < min_f) { + min_f = longhaul_table[i].frequency; + min_i = i; + } + } + if (min_i != j) { + unsigned int temp; + temp = longhaul_table[j].frequency; + longhaul_table[j].frequency = longhaul_table[min_i].frequency; + longhaul_table[min_i].frequency = temp; + temp = longhaul_table[j].index; + longhaul_table[j].index = longhaul_table[min_i].index; + longhaul_table[min_i].index = temp; + } + } longhaul_table[k].frequency = CPUFREQ_TABLE_END; - if (!k) { - kfree (longhaul_table); - return -EINVAL; - } + /* Find index we are running on */ + for (j = 0; j < k; j++) { + if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) { + longhaul_index = j; + break; + } + } return 0; } @@ -448,7 +488,7 @@ static int __init longhaul_get_ranges(void) static void __init longhaul_setup_voltagescaling(void) { union msr_longhaul longhaul; - struct mV_pos minvid, maxvid; + struct mV_pos minvid, maxvid, vid; unsigned int j, speed, pos, kHz_step, numvscales; int min_vid_speed; @@ -459,11 +499,11 @@ static void __init longhaul_setup_voltagescaling(void) } if (!longhaul.bits.VRMRev) { - printk (KERN_INFO PFX "VRM 8.5\n"); + printk(KERN_INFO PFX "VRM 8.5\n"); vrm_mV_table = &vrm85_mV[0]; mV_vrm_table = &mV_vrm85[0]; } else { - printk (KERN_INFO PFX "Mobile VRM\n"); + printk(KERN_INFO PFX "Mobile VRM\n"); if (cpu_model < CPU_NEHEMIAH) return; vrm_mV_table = &mobilevrm_mV[0]; @@ -523,7 +563,6 @@ static void __init longhaul_setup_voltagescaling(void) /* Calculate kHz for one voltage step */ kHz_step = (highest_speed - min_vid_speed) / numvscales; - j = 0; while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { speed = longhaul_table[j].frequency; @@ -531,15 +570,14 @@ static void __init longhaul_setup_voltagescaling(void) pos = (speed - min_vid_speed) / kHz_step + minvid.pos; else pos = minvid.pos; - f_msr_table[longhaul_table[j].index].vrm = mV_vrm_table[pos]; - f_msr_table[longhaul_table[j].index].pos = pos; + longhaul_table[j].index |= mV_vrm_table[pos] << 8; + vid = vrm_mV_table[mV_vrm_table[pos]]; + printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV); j++; } - longhaul_pos = maxvid.pos; can_scale_voltage = 1; - printk(KERN_INFO PFX "Voltage scaling enabled. " - "Use of \"conservative\" governor is highly recommended.\n"); + printk(KERN_INFO PFX "Voltage scaling enabled.\n"); } @@ -553,15 +591,44 @@ static int longhaul_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { unsigned int table_index = 0; - unsigned int new_clock_ratio = 0; + unsigned int i; + unsigned int dir = 0; + u8 vid, current_vid; if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) return -EINVAL; - new_clock_ratio = longhaul_table[table_index].index & 0xFF; - - longhaul_setstate(new_clock_ratio); + /* Don't set same frequency again */ + if (longhaul_index == table_index) + return 0; + if (!can_scale_voltage) + longhaul_setstate(table_index); + else { + /* On test system voltage transitions exceeding single + * step up or down were turning motherboard off. Both + * "ondemand" and "userspace" are unsafe. C7 is doing + * this in hardware, C3 is old and we need to do this + * in software. */ + i = longhaul_index; + current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f; + if (table_index > longhaul_index) + dir = 1; + while (i != table_index) { + vid = (longhaul_table[i].index >> 8) & 0x1f; + if (vid != current_vid) { + longhaul_setstate(i); + current_vid = vid; + msleep(200); + } + if (dir) + i++; + else + i--; + } + longhaul_setstate(table_index); + } + longhaul_index = table_index; return 0; } @@ -590,11 +657,10 @@ static acpi_status longhaul_walk_callback(acpi_handle obj_handle, static int enable_arbiter_disable(void) { struct pci_dev *dev; - int status; + int status = 1; int reg; u8 pci_cmd; - status = 1; /* Find PLE133 host bridge */ reg = 0x78; dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0, @@ -627,13 +693,17 @@ static int enable_arbiter_disable(void) return 0; } -static int longhaul_setup_vt8235(void) +static int longhaul_setup_southbridge(void) { struct pci_dev *dev; u8 pci_cmd; /* Find VT8235 southbridge */ dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL); + if (dev == NULL) + /* Find VT8237 southbridge */ + dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_8237, NULL); if (dev != NULL) { /* Set transition time to max */ pci_read_config_byte(dev, 0xec, &pci_cmd); @@ -645,6 +715,14 @@ static int longhaul_setup_vt8235(void) pci_read_config_byte(dev, 0xe5, &pci_cmd); pci_cmd |= 1 << 7; pci_write_config_byte(dev, 0xe5, pci_cmd); + /* Get address of ACPI registers block*/ + pci_read_config_byte(dev, 0x81, &pci_cmd); + if (pci_cmd & 1 << 7) { + pci_read_config_dword(dev, 0x88, &acpi_regs_addr); + acpi_regs_addr &= 0xff00; + printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", acpi_regs_addr); + } + pci_dev_put(dev); return 1; } @@ -657,7 +735,6 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) char *cpuname=NULL; int ret; u32 lo, hi; - int vt8235_present; /* Check what we have on this motherboard */ switch (c->x86_model) { @@ -755,7 +832,7 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) }; /* Doesn't hurt */ - vt8235_present = longhaul_setup_vt8235(); + longhaul_setup_southbridge(); /* Find ACPI data for processor */ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, @@ -765,35 +842,29 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) /* Check ACPI support for C3 state */ if (pr != NULL && longhaul_version == TYPE_POWERSAVER) { cx = &pr->power.states[ACPI_STATE_C3]; - if (cx->address > 0 && cx->latency <= 1000) { + if (cx->address > 0 && cx->latency <= 1000) longhaul_flags |= USE_ACPI_C3; - goto print_support_type; - } } + /* Disable if it isn't working */ + if (disable_acpi_c3) + longhaul_flags &= ~USE_ACPI_C3; /* Check if northbridge is friendly */ - if (enable_arbiter_disable()) { + if (enable_arbiter_disable()) longhaul_flags |= USE_NORTHBRIDGE; - goto print_support_type; - } - /* Use VT8235 southbridge if present */ - if (longhaul_version == TYPE_POWERSAVER && vt8235_present) { - longhaul_flags |= USE_VT8235; - goto print_support_type; - } + /* Check ACPI support for bus master arbiter disable */ - if ((pr == NULL) || !(pr->flags.bm_control)) { + if (!(longhaul_flags & USE_ACPI_C3 + || longhaul_flags & USE_NORTHBRIDGE) + && ((pr == NULL) || !(pr->flags.bm_control))) { printk(KERN_ERR PFX "No ACPI support. Unsupported northbridge.\n"); return -ENODEV; } -print_support_type: if (longhaul_flags & USE_NORTHBRIDGE) - printk (KERN_INFO PFX "Using northbridge support.\n"); - else if (longhaul_flags & USE_VT8235) - printk (KERN_INFO PFX "Using VT8235 support.\n"); - else - printk (KERN_INFO PFX "Using ACPI support.\n"); + printk(KERN_INFO PFX "Using northbridge support.\n"); + if (longhaul_flags & USE_ACPI_C3) + printk(KERN_INFO PFX "Using ACPI support.\n"); ret = longhaul_get_ranges(); if (ret != 0) @@ -885,6 +956,9 @@ static void __exit longhaul_exit(void) kfree(longhaul_table); } +module_param (disable_acpi_c3, int, 0644); +MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support"); + module_param (scale_voltage, int, 0644); MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor"); diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.h b/arch/i386/kernel/cpu/cpufreq/longhaul.h index 102548f1284..4fcc320997d 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.h +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.h @@ -180,7 +180,7 @@ static const int __initdata ezrat_clock_ratio[32] = { -1, /* 0000 -> RESERVED (10.0x) */ 110, /* 0001 -> 11.0x */ - 120, /* 0010 -> 12.0x */ + -1, /* 0010 -> 12.0x */ -1, /* 0011 -> RESERVED (9.0x)*/ 105, /* 0100 -> 10.5x */ 115, /* 0101 -> 11.5x */ @@ -237,7 +237,7 @@ static const int __initdata ezrat_eblcr[32] = { static const int __initdata nehemiah_clock_ratio[32] = { 100, /* 0000 -> 10.0x */ - 160, /* 0001 -> 16.0x */ + -1, /* 0001 -> 16.0x */ 40, /* 0010 -> 4.0x */ 90, /* 0011 -> 9.0x */ 95, /* 0100 -> 9.5x */ @@ -252,10 +252,10 @@ static const int __initdata nehemiah_clock_ratio[32] = { 75, /* 1101 -> 7.5x */ 85, /* 1110 -> 8.5x */ 120, /* 1111 -> 12.0x */ - 100, /* 0000 -> 10.0x */ + -1, /* 0000 -> 10.0x */ 110, /* 0001 -> 11.0x */ - 120, /* 0010 -> 12.0x */ - 90, /* 0011 -> 9.0x */ + -1, /* 0010 -> 12.0x */ + -1, /* 0011 -> 9.0x */ 105, /* 0100 -> 10.5x */ 115, /* 0101 -> 11.5x */ 125, /* 0110 -> 12.5x */ @@ -267,7 +267,7 @@ static const int __initdata nehemiah_clock_ratio[32] = { 145, /* 1100 -> 14.5x */ 155, /* 1101 -> 15.5x */ -1, /* 1110 -> RESERVED (13.0x) */ - 120, /* 1111 -> 12.0x */ + -1, /* 1111 -> 12.0x */ }; static const int __initdata nehemiah_eblcr[32] = { diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c index 4ade55c5f33..34ed53a0673 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c @@ -599,14 +599,17 @@ static void print_basics(struct powernow_k8_data *data) for (j = 0; j < data->numps; j++) { if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) { if (cpu_family == CPU_HW_PSTATE) { - printk(KERN_INFO PFX " %d : fid 0x%x gid 0x%x (%d MHz)\n", j, (data->powernow_table[j].index & 0xff00) >> 8, - (data->powernow_table[j].index & 0xff0000) >> 16, - data->powernow_table[j].frequency/1000); + printk(KERN_INFO PFX " %d : fid 0x%x did 0x%x (%d MHz)\n", + j, + (data->powernow_table[j].index & 0xff00) >> 8, + (data->powernow_table[j].index & 0xff0000) >> 16, + data->powernow_table[j].frequency/1000); } else { - printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", j, - data->powernow_table[j].index & 0xff, - data->powernow_table[j].frequency/1000, - data->powernow_table[j].index >> 8); + printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", + j, + data->powernow_table[j].index & 0xff, + data->powernow_table[j].frequency/1000, + data->powernow_table[j].index >> 8); } } } @@ -1086,7 +1089,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi if (cpu_family == CPU_HW_PSTATE) dprintk("targ: curr fid 0x%x, did 0x%x\n", - data->currfid, data->currvid); + data->currfid, data->currdid); else { dprintk("targ: curr fid 0x%x, vid 0x%x\n", data->currfid, data->currvid); @@ -1322,16 +1325,22 @@ static struct cpufreq_driver cpufreq_amd64_driver = { static int __cpuinit powernowk8_init(void) { unsigned int i, supported_cpus = 0; + unsigned int booted_cores = 1; for_each_online_cpu(i) { if (check_supported_cpu(i)) supported_cpus++; } +#ifdef CONFIG_SMP + booted_cores = cpu_data[0].booted_cores; +#endif + if (supported_cpus == num_online_cpus()) { printk(KERN_INFO PFX "Found %d %s " - "processors (" VERSION ")\n", supported_cpus, - boot_cpu_data.x86_model_id); + "processors (%d cpu cores) (" VERSION ")\n", + supported_cpus/booted_cores, + boot_cpu_data.x86_model_id, supported_cpus); return cpufreq_register_driver(&cpufreq_amd64_driver); } diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index 35489fd6885..6c5dc2c85ae 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c @@ -21,12 +21,6 @@ #include <linux/delay.h> #include <linux/compiler.h> -#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI -#include <linux/acpi.h> -#include <linux/dmi.h> -#include <acpi/processor.h> -#endif - #include <asm/msr.h> #include <asm/processor.h> #include <asm/cpufeature.h> @@ -257,9 +251,7 @@ static int centrino_cpu_init_table(struct cpufreq_policy *policy) /* Matched a non-match */ dprintk("no table support for CPU model \"%s\"\n", cpu->x86_model_id); -#ifndef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI - dprintk("try compiling with CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI enabled\n"); -#endif + dprintk("try using the acpi-cpufreq driver\n"); return -ENOENT; } @@ -346,213 +338,6 @@ static unsigned int get_cur_freq(unsigned int cpu) } -#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI - -static struct acpi_processor_performance *acpi_perf_data[NR_CPUS]; - -/* - * centrino_cpu_early_init_acpi - Do the preregistering with ACPI P-States - * library - * - * Before doing the actual init, we need to do _PSD related setup whenever - * supported by the BIOS. These are handled by this early_init routine. - */ -static int centrino_cpu_early_init_acpi(void) -{ - unsigned int i, j; - struct acpi_processor_performance *data; - - for_each_possible_cpu(i) { - data = kzalloc(sizeof(struct acpi_processor_performance), - GFP_KERNEL); - if (!data) { - for_each_possible_cpu(j) { - kfree(acpi_perf_data[j]); - acpi_perf_data[j] = NULL; - } - return (-ENOMEM); - } - acpi_perf_data[i] = data; - } - - acpi_processor_preregister_performance(acpi_perf_data); - return 0; -} - - -#ifdef CONFIG_SMP -/* - * Some BIOSes do SW_ANY coordination internally, either set it up in hw - * or do it in BIOS firmware and won't inform about it to OS. If not - * detected, this has a side effect of making CPU run at a different speed - * than OS intended it to run at. Detect it and handle it cleanly. - */ -static int bios_with_sw_any_bug; -static int sw_any_bug_found(struct dmi_system_id *d) -{ - bios_with_sw_any_bug = 1; - return 0; -} - -static struct dmi_system_id sw_any_bug_dmi_table[] = { - { - .callback = sw_any_bug_found, - .ident = "Supermicro Server X6DLP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), - DMI_MATCH(DMI_BIOS_VERSION, "080010"), - DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"), - }, - }, - { } -}; -#endif - -/* - * centrino_cpu_init_acpi - register with ACPI P-States library - * - * Register with the ACPI P-States library (part of drivers/acpi/processor.c) - * in order to determine correct frequency and voltage pairings by reading - * the _PSS of the ACPI DSDT or SSDT tables. - */ -static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) -{ - unsigned long cur_freq; - int result = 0, i; - unsigned int cpu = policy->cpu; - struct acpi_processor_performance *p; - - p = acpi_perf_data[cpu]; - - /* register with ACPI core */ - if (acpi_processor_register_performance(p, cpu)) { - dprintk(PFX "obtaining ACPI data failed\n"); - return -EIO; - } - - policy->shared_type = p->shared_type; - /* - * Will let policy->cpus know about dependency only when software - * coordination is required. - */ - if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL || - policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { - policy->cpus = p->shared_cpu_map; - } - -#ifdef CONFIG_SMP - dmi_check_system(sw_any_bug_dmi_table); - if (bios_with_sw_any_bug && cpus_weight(policy->cpus) == 1) { - policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; - policy->cpus = cpu_core_map[cpu]; - } -#endif - - /* verify the acpi_data */ - if (p->state_count <= 1) { - dprintk("No P-States\n"); - result = -ENODEV; - goto err_unreg; - } - - if ((p->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) || - (p->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { - dprintk("Invalid control/status registers (%x - %x)\n", - p->control_register.space_id, p->status_register.space_id); - result = -EIO; - goto err_unreg; - } - - for (i=0; i<p->state_count; i++) { - if ((p->states[i].control & INTEL_MSR_RANGE) != - (p->states[i].status & INTEL_MSR_RANGE)) { - dprintk("Different MSR bits in control (%llu) and status (%llu)\n", - p->states[i].control, p->states[i].status); - result = -EINVAL; - goto err_unreg; - } - - if (!p->states[i].core_frequency) { - dprintk("Zero core frequency for state %u\n", i); - result = -EINVAL; - goto err_unreg; - } - - if (p->states[i].core_frequency > p->states[0].core_frequency) { - dprintk("P%u has larger frequency (%llu) than P0 (%llu), skipping\n", i, - p->states[i].core_frequency, p->states[0].core_frequency); - p->states[i].core_frequency = 0; - continue; - } - } - - centrino_model[cpu] = kzalloc(sizeof(struct cpu_model), GFP_KERNEL); - if (!centrino_model[cpu]) { - result = -ENOMEM; - goto err_unreg; - } - - centrino_model[cpu]->model_name=NULL; - centrino_model[cpu]->max_freq = p->states[0].core_frequency * 1000; - centrino_model[cpu]->op_points = kmalloc(sizeof(struct cpufreq_frequency_table) * - (p->state_count + 1), GFP_KERNEL); - if (!centrino_model[cpu]->op_points) { - result = -ENOMEM; - goto err_kfree; - } - - for (i=0; i<p->state_count; i++) { - centrino_model[cpu]->op_points[i].index = p->states[i].control & INTEL_MSR_RANGE; - centrino_model[cpu]->op_points[i].frequency = p->states[i].core_frequency * 1000; - dprintk("adding state %i with frequency %u and control value %04x\n", - i, centrino_model[cpu]->op_points[i].frequency, centrino_model[cpu]->op_points[i].index); - } - centrino_model[cpu]->op_points[p->state_count].frequency = CPUFREQ_TABLE_END; - - cur_freq = get_cur_freq(cpu); - - for (i=0; i<p->state_count; i++) { - if (!p->states[i].core_frequency) { - dprintk("skipping state %u\n", i); - centrino_model[cpu]->op_points[i].frequency = CPUFREQ_ENTRY_INVALID; - continue; - } - - if (extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0) != - (centrino_model[cpu]->op_points[i].frequency)) { - dprintk("Invalid encoded frequency (%u vs. %u)\n", - extract_clock(centrino_model[cpu]->op_points[i].index, cpu, 0), - centrino_model[cpu]->op_points[i].frequency); - result = -EINVAL; - goto err_kfree_all; - } - - if (cur_freq == centrino_model[cpu]->op_points[i].frequency) - p->state = i; - } - - /* notify BIOS that we exist */ - acpi_processor_notify_smm(THIS_MODULE); - printk("speedstep-centrino with X86_SPEEDSTEP_CENTRINO_ACPI " - "config is deprecated.\n " - "Use X86_ACPI_CPUFREQ (acpi-cpufreq) instead.\n" ); - - return 0; - - err_kfree_all: - kfree(centrino_model[cpu]->op_points); - err_kfree: - kfree(centrino_model[cpu]); - err_unreg: - acpi_processor_unregister_performance(p, cpu); - dprintk(PFX "invalid ACPI data\n"); - return (result); -} -#else -static inline int centrino_cpu_init_acpi(struct cpufreq_policy *policy) { return -ENODEV; } -static inline int centrino_cpu_early_init_acpi(void) { return 0; } -#endif - static int centrino_cpu_init(struct cpufreq_policy *policy) { struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu]; @@ -568,27 +353,25 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC)) centrino_driver.flags |= CPUFREQ_CONST_LOOPS; - if (centrino_cpu_init_acpi(policy)) { - if (policy->cpu != 0) - return -ENODEV; + if (policy->cpu != 0) + return -ENODEV; - for (i = 0; i < N_IDS; i++) - if (centrino_verify_cpu_id(cpu, &cpu_ids[i])) - break; + for (i = 0; i < N_IDS; i++) + if (centrino_verify_cpu_id(cpu, &cpu_ids[i])) + break; - if (i != N_IDS) - centrino_cpu[policy->cpu] = &cpu_ids[i]; + if (i != N_IDS) + centrino_cpu[policy->cpu] = &cpu_ids[i]; - if (!centrino_cpu[policy->cpu]) { - dprintk("found unsupported CPU with " - "Enhanced SpeedStep: send /proc/cpuinfo to " - MAINTAINER "\n"); - return -ENODEV; - } + if (!centrino_cpu[policy->cpu]) { + dprintk("found unsupported CPU with " + "Enhanced SpeedStep: send /proc/cpuinfo to " + MAINTAINER "\n"); + return -ENODEV; + } - if (centrino_cpu_init_table(policy)) { - return -ENODEV; - } + if (centrino_cpu_init_table(policy)) { + return -ENODEV; } /* Check to see if Enhanced SpeedStep is enabled, and try to @@ -634,20 +417,6 @@ static int centrino_cpu_exit(struct cpufreq_policy *policy) cpufreq_frequency_table_put_attr(cpu); -#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI - if (!centrino_model[cpu]->model_name) { - static struct acpi_processor_performance *p; - - if (acpi_perf_data[cpu]) { - p = acpi_perf_data[cpu]; - dprintk("unregistering and freeing ACPI data\n"); - acpi_processor_unregister_performance(p, cpu); - kfree(centrino_model[cpu]->op_points); - kfree(centrino_model[cpu]); - } - } -#endif - centrino_model[cpu] = NULL; return 0; @@ -849,25 +618,12 @@ static int __init centrino_init(void) if (!cpu_has(cpu, X86_FEATURE_EST)) return -ENODEV; - centrino_cpu_early_init_acpi(); - return cpufreq_register_driver(¢rino_driver); } static void __exit centrino_exit(void) { -#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI - unsigned int j; -#endif - cpufreq_unregister_driver(¢rino_driver); - -#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI - for_each_possible_cpu(j) { - kfree(acpi_perf_data[j]); - acpi_perf_data[j] = NULL; - } -#endif } MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>"); diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c index 698f980eb44..a5b2346faf1 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c @@ -205,7 +205,6 @@ static unsigned int speedstep_detect_chipset (void) * host brige. Abort on these systems. */ static struct pci_dev *hostbridge; - u8 rev = 0; hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_MC, @@ -216,8 +215,7 @@ static unsigned int speedstep_detect_chipset (void) if (!hostbridge) return 2; /* 2-M */ - pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev); - if (rev < 5) { + if (hostbridge->revision < 5) { dprintk("hostbridge does not support speedstep\n"); speedstep_chipset_dev = NULL; pci_dev_put(hostbridge); diff --git a/arch/i386/kernel/cpu/mtrr/cyrix.c b/arch/i386/kernel/cpu/mtrr/cyrix.c index 9edf5625584..1001f1e0fe6 100644 --- a/arch/i386/kernel/cpu/mtrr/cyrix.c +++ b/arch/i386/kernel/cpu/mtrr/cyrix.c @@ -233,12 +233,12 @@ typedef struct { mtrr_type type; } arr_state_t; -static arr_state_t arr_state[8] __devinitdata = { +static arr_state_t arr_state[8] = { {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} }; -static unsigned char ccr_state[7] __devinitdata = { 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; static void cyrix_set_all(void) { diff --git a/arch/i386/kernel/cpu/mtrr/generic.c b/arch/i386/kernel/cpu/mtrr/generic.c index c4ebb5126ef..f6e46943e6e 100644 --- a/arch/i386/kernel/cpu/mtrr/generic.c +++ b/arch/i386/kernel/cpu/mtrr/generic.c @@ -42,7 +42,7 @@ static int mtrr_show; module_param_named(show, mtrr_show, bool, 0); /* Get the MSR pair relating to a var range */ -static void __init +static void get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr) { rdmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); @@ -65,10 +65,11 @@ get_fixed_ranges(mtrr_type * frs) void mtrr_save_fixed_ranges(void *info) { - get_fixed_ranges(mtrr_state.fixed_ranges); + if (cpu_has_mtrr) + get_fixed_ranges(mtrr_state.fixed_ranges); } -static void __cpuinit print_fixed(unsigned base, unsigned step, const mtrr_type*types) +static void print_fixed(unsigned base, unsigned step, const mtrr_type*types) { unsigned i; @@ -469,11 +470,6 @@ int generic_validate_add_page(unsigned long base, unsigned long size, unsigned i } } - if (base < 0x100) { - printk(KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", - base, size); - return -EINVAL; - } /* Check upper bits of base and last are equal and lower bits are 0 for base and 1 for last */ last = base + size - 1; diff --git a/arch/i386/kernel/cpu/mtrr/main.c b/arch/i386/kernel/cpu/mtrr/main.c index 1cf466df330..75dc6d5214b 100644 --- a/arch/i386/kernel/cpu/mtrr/main.c +++ b/arch/i386/kernel/cpu/mtrr/main.c @@ -229,6 +229,8 @@ static void set_mtrr(unsigned int reg, unsigned long base, data.smp_size = size; data.smp_type = type; atomic_set(&data.count, num_booting_cpus() - 1); + /* make sure data.count is visible before unleashing other CPUs */ + smp_wmb(); atomic_set(&data.gate,0); /* Start the ball rolling on other CPUs */ @@ -242,6 +244,7 @@ static void set_mtrr(unsigned int reg, unsigned long base, /* ok, reset count and toggle gate */ atomic_set(&data.count, num_booting_cpus() - 1); + smp_wmb(); atomic_set(&data.gate,1); /* do our MTRR business */ @@ -260,6 +263,7 @@ static void set_mtrr(unsigned int reg, unsigned long base, cpu_relax(); atomic_set(&data.count, num_booting_cpus() - 1); + smp_wmb(); atomic_set(&data.gate,0); /* @@ -639,7 +643,7 @@ static struct sysdev_driver mtrr_sysdev_driver = { * initialized (i.e. before smp_init()). * */ -void mtrr_bp_init(void) +__init void mtrr_bp_init(void) { init_ifs(); @@ -734,10 +738,13 @@ void mtrr_ap_init(void) */ void mtrr_save_state(void) { - if (smp_processor_id() == 0) + int cpu = get_cpu(); + + if (cpu == 0) mtrr_save_fixed_ranges(NULL); else smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1, 1); + put_cpu(); } static int __init mtrr_init_finialize(void) diff --git a/arch/i386/kernel/cpu/perfctr-watchdog.c b/arch/i386/kernel/cpu/perfctr-watchdog.c index 2b04c8f1db6..4d26d514c56 100644 --- a/arch/i386/kernel/cpu/perfctr-watchdog.c +++ b/arch/i386/kernel/cpu/perfctr-watchdog.c @@ -28,7 +28,7 @@ struct wd_ops { void (*unreserve)(void); int (*setup)(unsigned nmi_hz); void (*rearm)(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz); - void (*stop)(void *); + void (*stop)(void); unsigned perfctr; unsigned evntsel; u64 checkbit; @@ -55,14 +55,45 @@ static DEFINE_PER_CPU(struct nmi_watchdog_ctlblk, nmi_watchdog_ctlblk); /* converts an msr to an appropriate reservation bit */ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr) { - return wd_ops ? msr - wd_ops->perfctr : 0; + /* returns the bit offset of the performance counter register */ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + return (msr - MSR_K7_PERFCTR0); + case X86_VENDOR_INTEL: + if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) + return (msr - MSR_ARCH_PERFMON_PERFCTR0); + + switch (boot_cpu_data.x86) { + case 6: + return (msr - MSR_P6_PERFCTR0); + case 15: + return (msr - MSR_P4_BPU_PERFCTR0); + } + } + return 0; } /* converts an msr to an appropriate reservation bit */ /* returns the bit offset of the event selection register */ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr) { - return wd_ops ? msr - wd_ops->evntsel : 0; + /* returns the bit offset of the event selection register */ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + return (msr - MSR_K7_EVNTSEL0); + case X86_VENDOR_INTEL: + if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) + return (msr - MSR_ARCH_PERFMON_EVENTSEL0); + + switch (boot_cpu_data.x86) { + case 6: + return (msr - MSR_P6_EVNTSEL0); + case 15: + return (msr - MSR_P4_BSU_ESCR0); + } + } + return 0; + } /* checks for a bit availability (hack for oprofile) */ @@ -142,7 +173,7 @@ void disable_lapic_nmi_watchdog(void) if (atomic_read(&nmi_active) <= 0) return; - on_each_cpu(wd_ops->stop, NULL, 0, 1); + on_each_cpu(stop_apic_nmi_watchdog, NULL, 0, 1); wd_ops->unreserve(); BUG_ON(atomic_read(&nmi_active) != 0); @@ -255,7 +286,7 @@ static int setup_k7_watchdog(unsigned nmi_hz) return 1; } -static void single_msr_stop_watchdog(void *arg) +static void single_msr_stop_watchdog(void) { struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); @@ -276,8 +307,8 @@ static int single_msr_reserve(void) static void single_msr_unreserve(void) { - release_evntsel_nmi(wd_ops->perfctr); - release_perfctr_nmi(wd_ops->evntsel); + release_evntsel_nmi(wd_ops->evntsel); + release_perfctr_nmi(wd_ops->perfctr); } static void single_msr_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) @@ -442,7 +473,7 @@ static int setup_p4_watchdog(unsigned nmi_hz) return 1; } -static void stop_p4_watchdog(void *arg) +static void stop_p4_watchdog(void) { struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); wrmsr(wd->cccr_msr, 0, 0); @@ -475,10 +506,10 @@ static void p4_unreserve(void) { #ifdef CONFIG_SMP if (smp_num_siblings > 1) - release_evntsel_nmi(MSR_P4_IQ_PERFCTR1); + release_perfctr_nmi(MSR_P4_IQ_PERFCTR1); #endif - release_evntsel_nmi(MSR_P4_IQ_PERFCTR0); - release_perfctr_nmi(MSR_P4_CRU_ESCR0); + release_evntsel_nmi(MSR_P4_CRU_ESCR0); + release_perfctr_nmi(MSR_P4_IQ_PERFCTR0); } static void p4_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) @@ -614,6 +645,12 @@ int lapic_watchdog_init(unsigned nmi_hz) probe_nmi_watchdog(); if (!wd_ops) return -1; + + if (!wd_ops->reserve()) { + printk(KERN_ERR + "NMI watchdog: cannot reserve perfctrs\n"); + return -1; + } } if (!(wd_ops->setup(nmi_hz))) { @@ -628,7 +665,7 @@ int lapic_watchdog_init(unsigned nmi_hz) void lapic_watchdog_stop(void) { if (wd_ops) - wd_ops->stop(NULL); + wd_ops->stop(); } unsigned lapic_adjust_nmi_hz(unsigned hz) diff --git a/arch/i386/kernel/cpu/proc.c b/arch/i386/kernel/cpu/proc.c index 89d91e6cc97..1e31b6caffb 100644 --- a/arch/i386/kernel/cpu/proc.c +++ b/arch/i386/kernel/cpu/proc.c @@ -29,7 +29,8 @@ static int show_cpuinfo(struct seq_file *m, void *v) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL, - NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext", "3dnow", + NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", + "3dnowext", "3dnow", /* Transmeta-defined */ "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, @@ -40,8 +41,9 @@ static int show_cpuinfo(struct seq_file *m, void *v) /* Other (Linux-defined) */ "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL, - "constant_tsc", "up", NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "constant_tsc", "up", NULL, "arch_perfmon", + "pebs", "bts", NULL, "sync_rdtsc", + "rep_good", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* Intel-defined (#2) */ @@ -57,9 +59,16 @@ static int show_cpuinfo(struct seq_file *m, void *v) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* AMD-defined (#2) */ - "lahf_lm", "cmp_legacy", "svm", "extapic", "cr8legacy", "abm", - "sse4a", "misalignsse", - "3dnowprefetch", "osvw", "ibs", NULL, NULL, NULL, NULL, NULL, + "lahf_lm", "cmp_legacy", "svm", "extapic", "cr8_legacy", + "altmovcr8", "abm", "sse4a", + "misalignsse", "3dnowprefetch", + "osvw", "ibs", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Auxiliary (Linux-defined) */ + "ida", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; diff --git a/arch/i386/kernel/e820.c b/arch/i386/kernel/e820.c index 9645bb51f76..fc822a46897 100644 --- a/arch/i386/kernel/e820.c +++ b/arch/i386/kernel/e820.c @@ -734,7 +734,7 @@ void __init print_memory_map(char *who) case E820_NVS: printk("(ACPI NVS)\n"); break; - default: printk("type %lu\n", e820.map[i].type); + default: printk("type %u\n", e820.map[i].type); break; } } diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index b1f16ee65e4..3c3c220488c 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -367,10 +367,6 @@ ENTRY(system_call) CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL GET_THREAD_INFO(%ebp) - testl $TF_MASK,PT_EFLAGS(%esp) - jz no_singlestep - orl $_TIF_SINGLESTEP,TI_flags(%ebp) -no_singlestep: # system call tracing in operation / emulation /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) @@ -385,6 +381,10 @@ syscall_exit: # setting need_resched or sigpending # between sampling and the iret TRACE_IRQS_OFF + testl $TF_MASK,PT_EFLAGS(%esp) # If tracing set singlestep flag on exit + jz no_singlestep + orl $_TIF_SINGLESTEP,TI_flags(%ebp) +no_singlestep: movl TI_flags(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 83f825f2e2d..d865d041bea 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -478,7 +478,7 @@ static int __init microcode_dev_init (void) return 0; } -static void __exit microcode_dev_exit (void) +static void microcode_dev_exit (void) { misc_deregister(µcode_dev); } diff --git a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c index 30b754f7cbe..048f09b6255 100644 --- a/arch/i386/kernel/pci-dma.c +++ b/arch/i386/kernel/pci-dma.c @@ -12,6 +12,7 @@ #include <linux/string.h> #include <linux/pci.h> #include <linux/module.h> +#include <linux/pci.h> #include <asm/io.h> struct dma_coherent_mem { @@ -148,3 +149,29 @@ void *dma_mark_declared_memory_occupied(struct device *dev, return mem->virt_base + (pos << PAGE_SHIFT); } EXPORT_SYMBOL(dma_mark_declared_memory_occupied); + +#ifdef CONFIG_PCI +/* Many VIA bridges seem to corrupt data for DAC. Disable it here */ + +int forbid_dac; +EXPORT_SYMBOL(forbid_dac); + +static __devinit void via_no_dac(struct pci_dev *dev) +{ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) { + printk(KERN_INFO "PCI: VIA PCI bridge detected. Disabling DAC.\n"); + forbid_dac = 1; + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_ANY_ID, via_no_dac); + +static int check_iommu(char *s) +{ + if (!strcmp(s, "usedac")) { + forbid_dac = -1; + return 1; + } + return 0; +} +__setup("iommu=", check_iommu); +#endif diff --git a/arch/i386/kernel/quirks.c b/arch/i386/kernel/quirks.c index 9f6ab1789bb..6722469c263 100644 --- a/arch/i386/kernel/quirks.c +++ b/arch/i386/kernel/quirks.c @@ -20,8 +20,6 @@ static void __devinit quirk_intel_irqbalance(struct pci_dev *dev) if (rev > 0x9) return; - printk(KERN_INFO "Intel E7520/7320/7525 detected."); - /* enable access to config space*/ pci_read_config_byte(dev, 0xf4, &config); pci_write_config_byte(dev, 0xf4, config|0x2); @@ -30,7 +28,8 @@ static void __devinit quirk_intel_irqbalance(struct pci_dev *dev) raw_pci_ops->read(0, 0, 0x40, 0x4c, 2, &word); if (!(word & (1 << 13))) { - printk(KERN_INFO "Disabling irq balancing and affinity\n"); + printk(KERN_INFO "Intel E7520/7320/7525 detected. " + "Disabling irq balancing and affinity\n"); #ifdef CONFIG_IRQBALANCE irqbalance_disable(""); #endif diff --git a/arch/i386/kernel/reboot.c b/arch/i386/kernel/reboot.c index 50dfc65319c..5513f8d5b5b 100644 --- a/arch/i386/kernel/reboot.c +++ b/arch/i386/kernel/reboot.c @@ -89,6 +89,14 @@ static int __init set_bios_reboot(struct dmi_system_id *d) } static struct dmi_system_id __initdata reboot_dmi_table[] = { + { /* Handle problems with rebooting on Dell E520's */ + .callback = set_bios_reboot, + .ident = "Dell E520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"), + }, + }, { /* Handle problems with rebooting on Dell 1300's */ .callback = set_bios_reboot, .ident = "Dell PowerEdge 1300", diff --git a/arch/i386/kernel/reboot_fixups.c b/arch/i386/kernel/reboot_fixups.c index 2d78d918340..03e1cce58f4 100644 --- a/arch/i386/kernel/reboot_fixups.c +++ b/arch/i386/kernel/reboot_fixups.c @@ -5,12 +5,14 @@ * * List of supported fixups: * geode-gx1/cs5530a - Jaya Kumar <jayalk@intworks.biz> + * geode-gx/lx/cs5536 - Andres Salomon <dilinger@debian.org> * */ #include <asm/delay.h> #include <linux/pci.h> #include <asm/reboot_fixups.h> +#include <asm/msr.h> static void cs5530a_warm_reset(struct pci_dev *dev) { @@ -21,6 +23,16 @@ static void cs5530a_warm_reset(struct pci_dev *dev) return; } +static void cs5536_warm_reset(struct pci_dev *dev) +{ + /* + * 6.6.2.12 Soft Reset (DIVIL_SOFT_RESET) + * writing 1 to the LSB of this MSR causes a hard reset. + */ + wrmsrl(0x51400017, 1ULL); + udelay(50); /* shouldn't get here but be safe and spin a while */ +} + struct device_fixup { unsigned int vendor; unsigned int device; @@ -29,6 +41,7 @@ struct device_fixup { static struct device_fixup fixups_table[] = { { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset }, +{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset }, }; /* diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 698c24fe482..2d61e65eeb5 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -102,19 +102,10 @@ static unsigned int highmem_pages = -1; /* * Setup options */ -struct drive_info_struct { char dummy[32]; } drive_info; -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || \ - defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) -EXPORT_SYMBOL(drive_info); -#endif struct screen_info screen_info; EXPORT_SYMBOL(screen_info); struct apm_info apm_info; EXPORT_SYMBOL(apm_info); -struct sys_desc_table_struct { - unsigned short length; - unsigned char table[0]; -}; struct edid_info edid_info; EXPORT_SYMBOL_GPL(edid_info); struct ist_info ist_info; @@ -134,7 +125,7 @@ unsigned long saved_videomode; static char __initdata command_line[COMMAND_LINE_SIZE]; -unsigned char __initdata boot_params[PARAM_SIZE]; +struct boot_params __initdata boot_params; #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) struct edd edd; @@ -528,7 +519,6 @@ void __init setup_arch(char **cmdline_p) #endif ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); - drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; edid_info = EDID_INFO; apm_info.bios = APM_BIOS_INFO; diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 08f07a74a9d..0b2954534b8 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -941,18 +941,6 @@ exit: } #endif -static void smp_tune_scheduling(void) -{ - unsigned long cachesize; /* kB */ - - if (cpu_khz) { - cachesize = boot_cpu_data.x86_cache_size; - - if (cachesize > 0) - max_cache_size = cachesize * 1024; - } -} - /* * Cycle through the processors sending APIC IPIs to boot each. */ @@ -981,7 +969,6 @@ static void __init smp_boot_cpus(unsigned int max_cpus) x86_cpu_to_apicid[0] = boot_cpu_physical_apicid; current_thread_info()->cpu = 0; - smp_tune_scheduling(); set_cpu_sibling_map(0); diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index f64b81f3033..ea63a30ca3e 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c @@ -4,6 +4,7 @@ * See comments there for proper credits. */ +#include <linux/sched.h> #include <linux/clocksource.h> #include <linux/workqueue.h> #include <linux/cpufreq.h> @@ -106,8 +107,13 @@ unsigned long long sched_clock(void) /* * Fall back to jiffies if there's no TSC available: + * ( But note that we still use it if the TSC is marked + * unstable. We do this because unlike Time Of Day, + * the scheduler clock tolerates small errors and it's + * very important for it to be as fast as the platform + * can achive it. ) */ - if (unlikely(!tsc_enabled)) + if (unlikely(!tsc_enabled && !tsc_unstable)) /* No locking but a rare wrong value is not a big deal: */ return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); @@ -277,6 +283,7 @@ static struct clocksource clocksource_tsc = { void mark_tsc_unstable(char *reason) { + sched_clock_unstable_event(); if (!tsc_unstable) { tsc_unstable = 1; tsc_enabled = 0; diff --git a/arch/i386/kernel/verify_cpu.S b/arch/i386/kernel/verify_cpu.S deleted file mode 100644 index f1d1eacf4ab..00000000000 --- a/arch/i386/kernel/verify_cpu.S +++ /dev/null @@ -1,94 +0,0 @@ -/* Check if CPU has some minimum CPUID bits - This runs in 16bit mode so that the caller can still use the BIOS - to output errors on the screen */ -#include <asm/cpufeature.h> -#include <asm/msr.h> - -verify_cpu: - pushfl # Save caller passed flags - pushl $0 # Kill any dangerous flags - popfl - -#if CONFIG_X86_MINIMUM_CPU_MODEL >= 4 - pushfl - pop %eax - orl $(1<<18),%eax # try setting AC - push %eax - popfl - pushfl - popl %eax - testl $(1<<18),%eax - jz bad -#endif -#if REQUIRED_MASK1 != 0 - pushfl # standard way to check for cpuid - popl %eax - movl %eax,%ebx - xorl $0x200000,%eax - pushl %eax - popfl - pushfl - popl %eax - cmpl %eax,%ebx - pushfl # standard way to check for cpuid - popl %eax - movl %eax,%ebx - xorl $0x200000,%eax - pushl %eax - popfl - pushfl - popl %eax - cmpl %eax,%ebx - jz bad # REQUIRED_MASK1 != 0 requires CPUID - - movl $0x0,%eax # See if cpuid 1 is implemented - cpuid - cmpl $0x1,%eax - jb bad # no cpuid 1 - -#if REQUIRED_MASK1 & NEED_CMPXCHG64 - /* Some VIA C3s need magic MSRs to enable CX64. Do this here */ - cmpl $0x746e6543,%ebx # Cent - jne 1f - cmpl $0x48727561,%edx # aurH - jne 1f - cmpl $0x736c7561,%ecx # auls - jne 1f - movl $1,%eax # check model - cpuid - movl %eax,%ebx - shr $8,%ebx - andl $0xf,%ebx - cmp $6,%ebx # check family == 6 - jne 1f - shr $4,%eax - andl $0xf,%eax - cmpl $6,%eax # check model >= 6 - jb 1f - # assume models >= 6 all support this MSR - movl $MSR_VIA_FCR,%ecx - rdmsr - orl $((1<<1)|(1<<7)),%eax # enable CMPXCHG64 and PGE - wrmsr -1: -#endif - movl $0x1,%eax # Does the cpu have what it takes - cpuid - -#if CONFIG_X86_MINIMUM_CPU_MODEL > 4 -#error add proper model checking here -#endif - - andl $REQUIRED_MASK1,%edx - xorl $REQUIRED_MASK1,%edx - jnz bad -#endif /* REQUIRED_MASK1 */ - - popfl - xor %eax,%eax - ret - -bad: - popfl - movl $1,%eax - ret diff --git a/arch/i386/kernel/vmi.c b/arch/i386/kernel/vmi.c index c8726c424b3..c12720d7cbc 100644 --- a/arch/i386/kernel/vmi.c +++ b/arch/i386/kernel/vmi.c @@ -27,6 +27,7 @@ #include <linux/bootmem.h> #include <linux/mm.h> #include <linux/highmem.h> +#include <linux/sched.h> #include <asm/vmi.h> #include <asm/io.h> #include <asm/fixmap.h> diff --git a/arch/i386/mach-es7000/es7000plat.c b/arch/i386/mach-es7000/es7000plat.c index 9be6ceabf04..ab99072d3f9 100644 --- a/arch/i386/mach-es7000/es7000plat.c +++ b/arch/i386/mach-es7000/es7000plat.c @@ -40,6 +40,7 @@ #include <asm/smp.h> #include <asm/apicdef.h> #include "es7000.h" +#include <mach_mpparse.h> /* * ES7000 Globals @@ -174,6 +175,53 @@ find_unisys_acpi_oem_table(unsigned long *oem_addr) } #endif +/* + * This file also gets compiled if CONFIG_X86_GENERICARCH is set. Generic + * arch already has got following function definitions (asm-generic/es7000.c) + * hence no need to define these for that case. + */ +#ifndef CONFIG_X86_GENERICARCH +void es7000_sw_apic(void); +void __init enable_apic_mode(void) +{ + es7000_sw_apic(); + return; +} + +__init int mps_oem_check(struct mp_config_table *mpc, char *oem, + char *productid) +{ + if (mpc->mpc_oemptr) { + struct mp_config_oemtable *oem_table = + (struct mp_config_oemtable *)mpc->mpc_oemptr; + if (!strncmp(oem, "UNISYS", 6)) + return parse_unisys_oem((char *)oem_table); + } + return 0; +} +#ifdef CONFIG_ACPI +/* Hook from generic ACPI tables.c */ +int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + unsigned long oem_addr; + if (!find_unisys_acpi_oem_table(&oem_addr)) { + if (es7000_check_dsdt()) + return parse_unisys_oem((char *)oem_addr); + else { + setup_unisys(); + return 1; + } + } + return 0; +} +#else +int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + return 0; +} +#endif +#endif /* COFIG_X86_GENERICARCH */ + static void es7000_spin(int n) { diff --git a/arch/i386/mach-visws/traps.c b/arch/i386/mach-visws/traps.c index 5199bd03254..843b67acf43 100644 --- a/arch/i386/mach-visws/traps.c +++ b/arch/i386/mach-visws/traps.c @@ -23,13 +23,13 @@ static __init void lithium_init(void) set_fixmap(FIX_LI_PCIB, LI_PCI_B_PHYS); if ((li_pcia_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) || - (li_pcia_read16(PCI_DEVICE_ID) != PCI_VENDOR_ID_SGI_LITHIUM)) { + (li_pcia_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) { printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'A'); panic("This machine is not SGI Visual Workstation 320/540"); } if ((li_pcib_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) || - (li_pcib_read16(PCI_DEVICE_ID) != PCI_VENDOR_ID_SGI_LITHIUM)) { + (li_pcib_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) { printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'B'); panic("This machine is not SGI Visual Workstation 320/540"); } diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c index ddf8fa3bbd0..1853524c8b5 100644 --- a/arch/i386/math-emu/fpu_entry.c +++ b/arch/i386/math-emu/fpu_entry.c @@ -754,7 +754,7 @@ int save_i387_soft(void *s387, struct _fpstate __user * buf) return -1; if ( offset ) if (__copy_to_user(d+other, (u_char *)&S387->st_space, offset)) - return -1 + return -1; RE_ENTRANT_CHECK_ON; return 1; diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 29d7d61543a..1ecb3e43b52 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -458,6 +458,11 @@ bad_area: bad_area_nosemaphore: /* User mode accesses just cause a SIGSEGV */ if (error_code & 4) { + /* + * It's possible to have interrupts off here. + */ + local_irq_enable(); + /* * Valid to do another page fault here because this one came * from user space. diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index b22ce8d6b1b..7135946d366 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -799,6 +799,7 @@ void mark_rodata_ro(void) unsigned long start = PFN_ALIGN(_text); unsigned long size = PFN_ALIGN(_etext) - start; +#ifndef CONFIG_KPROBES #ifdef CONFIG_HOTPLUG_CPU /* It must still be possible to apply SMP alternatives. */ if (num_possible_cpus() <= 1) @@ -808,7 +809,7 @@ void mark_rodata_ro(void) size >> PAGE_SHIFT, PAGE_KERNEL_RX); printk("Write protecting the kernel text: %luk\n", size >> 10); } - +#endif start += size; size = (unsigned long)__end_rodata - start; change_page_attr(virt_to_page(start), diff --git a/arch/i386/mm/pageattr.c b/arch/i386/mm/pageattr.c index 47bd477c8ec..2eb14a73be9 100644 --- a/arch/i386/mm/pageattr.c +++ b/arch/i386/mm/pageattr.c @@ -68,14 +68,23 @@ static struct page *split_large_page(unsigned long address, pgprot_t prot, return base; } -static void flush_kernel_map(void *arg) +static void cache_flush_page(struct page *p) { - unsigned long adr = (unsigned long)arg; + unsigned long adr = (unsigned long)page_address(p); + int i; + for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size) + asm volatile("clflush (%0)" :: "r" (adr + i)); +} + +static void flush_kernel_map(void *arg) +{ + struct list_head *lh = (struct list_head *)arg; + struct page *p; - if (adr && cpu_has_clflush) { - int i; - for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size) - asm volatile("clflush (%0)" :: "r" (adr + i)); + /* High level code is not ready for clflush yet */ + if (0 && cpu_has_clflush) { + list_for_each_entry (p, lh, lru) + cache_flush_page(p); } else if (boot_cpu_data.x86_model >= 4) wbinvd(); @@ -181,9 +190,9 @@ __change_page_attr(struct page *page, pgprot_t prot) return 0; } -static inline void flush_map(void *adr) +static inline void flush_map(struct list_head *l) { - on_each_cpu(flush_kernel_map, adr, 1, 1); + on_each_cpu(flush_kernel_map, l, 1, 1); } /* @@ -225,11 +234,8 @@ void global_flush_tlb(void) spin_lock_irq(&cpa_lock); list_replace_init(&df_list, &l); spin_unlock_irq(&cpa_lock); - if (!cpu_has_clflush) - flush_map(NULL); + flush_map(&l); list_for_each_entry_safe(pg, next, &l, lru) { - if (cpu_has_clflush) - flush_map(page_address(pg)); __free_page(pg); } } diff --git a/arch/i386/oprofile/nmi_int.c b/arch/i386/oprofile/nmi_int.c index a7c0783b269..11b7a51566a 100644 --- a/arch/i386/oprofile/nmi_int.c +++ b/arch/i386/oprofile/nmi_int.c @@ -154,7 +154,7 @@ static int allocate_msrs(void) size_t counters_size = sizeof(struct op_msr) * model->num_counters; int i; - for_each_online_cpu(i) { + for_each_possible_cpu(i) { cpu_msrs[i].counters = kmalloc(counters_size, GFP_KERNEL); if (!cpu_msrs[i].counters) { success = 0; @@ -211,8 +211,14 @@ static int nmi_setup(void) /* Assume saved/restored counters are the same on all CPUs */ model->fill_in_addresses(&cpu_msrs[0]); for_each_possible_cpu (cpu) { - if (cpu != 0) - cpu_msrs[cpu] = cpu_msrs[0]; + if (cpu != 0) { + memcpy(cpu_msrs[cpu].counters, cpu_msrs[0].counters, + sizeof(struct op_msr) * model->num_counters); + + memcpy(cpu_msrs[cpu].controls, cpu_msrs[0].controls, + sizeof(struct op_msr) * model->num_controls); + } + } on_each_cpu(nmi_save_registers, NULL, 0, 1); on_each_cpu(nmi_cpu_setup, NULL, 0, 1); diff --git a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c index b62eafb997b..e7306dbf6c4 100644 --- a/arch/i386/pci/fixup.c +++ b/arch/i386/pci/fixup.c @@ -118,12 +118,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci static void pci_fixup_via_northbridge_bug(struct pci_dev *d) { u8 v; - u8 revision; int where = 0x55; int mask = 0x1f; /* clear bits 5, 6, 7 by default */ - pci_read_config_byte(d, PCI_REVISION_ID, &revision); - if (d->device == PCI_DEVICE_ID_VIA_8367_0) { /* fix pci bus latency issues resulted by NB bios error it appears on bug free^Wreduced kt266x's bios forces @@ -133,8 +130,8 @@ static void pci_fixup_via_northbridge_bug(struct pci_dev *d) where = 0x95; /* the memory write queue timer register is different for the KT266x's: 0x95 not 0x55 */ } else if (d->device == PCI_DEVICE_ID_VIA_8363_0 && - (revision == VIA_8363_KL133_REVISION_ID || - revision == VIA_8363_KM133_REVISION_ID)) { + (d->revision == VIA_8363_KL133_REVISION_ID || + d->revision == VIA_8363_KM133_REVISION_ID)) { mask = 0x3f; /* clear only bits 6 and 7; clearing bit 5 causes screen corruption on the KL133/KM133 */ } @@ -142,7 +139,7 @@ static void pci_fixup_via_northbridge_bug(struct pci_dev *d) pci_read_config_byte(d, where, &v); if (v & ~mask) { printk(KERN_WARNING "Disabling VIA memory write queue (PCI ID %04x, rev %02x): [%02x] %02x & %02x -> %02x\n", \ - d->device, revision, where, v, mask, v & mask); + d->device, d->revision, where, v, mask, v & mask); v &= mask; pci_write_config_byte(d, where, v); } @@ -436,3 +433,14 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, pci_early_fixup_cyrix_5530); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, pci_early_fixup_cyrix_5530); + +/* + * Siemens Nixdorf AG FSC Multiprocessor Interrupt Controller: + * prevent update of the BAR0, which doesn't look like a normal BAR. + */ +static void __devinit pci_siemens_interrupt_controller(struct pci_dev *dev) +{ + dev->resource[0].flags |= IORESOURCE_PCI_FIXED; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SIEMENS, 0x0015, + pci_siemens_interrupt_controller); |