diff options
Diffstat (limited to 'arch/x86')
32 files changed, 4374 insertions, 0 deletions
diff --git a/arch/x86/boot/.gitignore b/arch/x86/boot/.gitignore new file mode 100644 index 000000000000..18465143cfa2 --- /dev/null +++ b/arch/x86/boot/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | bootsect | ||
2 | bzImage | ||
3 | setup | ||
4 | setup.bin | ||
5 | setup.elf | ||
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile new file mode 100644 index 000000000000..cb1035f2b7e9 --- /dev/null +++ b/arch/x86/boot/Makefile | |||
@@ -0,0 +1,171 @@ | |||
1 | # | ||
2 | # arch/x86/boot/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | # Copyright (C) 1994 by Linus Torvalds | ||
9 | # | ||
10 | |||
11 | # ROOT_DEV specifies the default root-device when making the image. | ||
12 | # This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case | ||
13 | # the default of FLOPPY is used by 'build'. | ||
14 | |||
15 | ROOT_DEV := CURRENT | ||
16 | |||
17 | # If you want to preset the SVGA mode, uncomment the next line and | ||
18 | # set SVGA_MODE to whatever number you want. | ||
19 | # Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode. | ||
20 | # The number is the same as you would ordinarily press at bootup. | ||
21 | |||
22 | SVGA_MODE := -DSVGA_MODE=NORMAL_VGA | ||
23 | |||
24 | # If you want the RAM disk device, define this to be the size in blocks. | ||
25 | |||
26 | #RAMDISK := -DRAMDISK=512 | ||
27 | |||
28 | targets := vmlinux.bin setup.bin setup.elf zImage bzImage | ||
29 | subdir- := compressed | ||
30 | |||
31 | setup-y += a20.o apm.o cmdline.o copy.o cpu.o cpucheck.o edd.o | ||
32 | setup-y += header.o main.o mca.o memory.o pm.o pmjump.o | ||
33 | setup-y += printf.o string.o tty.o video.o version.o voyager.o | ||
34 | |||
35 | # The link order of the video-*.o modules can matter. In particular, | ||
36 | # video-vga.o *must* be listed first, followed by video-vesa.o. | ||
37 | # Hardware-specific drivers should follow in the order they should be | ||
38 | # probed, and video-bios.o should typically be last. | ||
39 | setup-y += video-vga.o | ||
40 | setup-y += video-vesa.o | ||
41 | setup-y += video-bios.o | ||
42 | targets += $(setup-y) | ||
43 | hostprogs-y := tools/build | ||
44 | |||
45 | HOSTCFLAGS_build.o := $(LINUXINCLUDE) | ||
46 | |||
47 | # --------------------------------------------------------------------------- | ||
48 | |||
49 | # How to compile the 16-bit code. Note we always compile for -march=i386, | ||
50 | # that way we can complain to the user if the CPU is insufficient. | ||
51 | cflags-i386 := | ||
52 | cflags-x86_64 := -m32 | ||
53 | CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \ | ||
54 | $(cflags-$(ARCH)) \ | ||
55 | -Wall -Wstrict-prototypes \ | ||
56 | -march=i386 -mregparm=3 \ | ||
57 | -include $(srctree)/$(src)/code16gcc.h \ | ||
58 | -fno-strict-aliasing -fomit-frame-pointer \ | ||
59 | $(call cc-option, -ffreestanding) \ | ||
60 | $(call cc-option, -fno-toplevel-reorder,\ | ||
61 | $(call cc-option, -fno-unit-at-a-time)) \ | ||
62 | $(call cc-option, -fno-stack-protector) \ | ||
63 | $(call cc-option, -mpreferred-stack-boundary=2) | ||
64 | AFLAGS := $(CFLAGS) -D__ASSEMBLY__ | ||
65 | |||
66 | $(obj)/zImage: IMAGE_OFFSET := 0x1000 | ||
67 | $(obj)/zImage: EXTRA_AFLAGS := $(SVGA_MODE) $(RAMDISK) | ||
68 | $(obj)/bzImage: IMAGE_OFFSET := 0x100000 | ||
69 | $(obj)/bzImage: EXTRA_CFLAGS := -D__BIG_KERNEL__ | ||
70 | $(obj)/bzImage: EXTRA_AFLAGS := $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__ | ||
71 | $(obj)/bzImage: BUILDFLAGS := -b | ||
72 | |||
73 | quiet_cmd_image = BUILD $@ | ||
74 | cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/setup.bin \ | ||
75 | $(obj)/vmlinux.bin $(ROOT_DEV) > $@ | ||
76 | |||
77 | $(obj)/zImage $(obj)/bzImage: $(obj)/setup.bin \ | ||
78 | $(obj)/vmlinux.bin $(obj)/tools/build FORCE | ||
79 | $(call if_changed,image) | ||
80 | @echo 'Kernel: $@ is ready' ' (#'`cat .version`')' | ||
81 | |||
82 | $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE | ||
83 | $(call if_changed,objcopy) | ||
84 | |||
85 | SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) | ||
86 | |||
87 | LDFLAGS_setup.elf := -T | ||
88 | $(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE | ||
89 | $(call if_changed,ld) | ||
90 | |||
91 | OBJCOPYFLAGS_setup.bin := -O binary | ||
92 | |||
93 | $(obj)/setup.bin: $(obj)/setup.elf FORCE | ||
94 | $(call if_changed,objcopy) | ||
95 | |||
96 | $(obj)/compressed/vmlinux: FORCE | ||
97 | $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ | ||
98 | |||
99 | # Set this if you want to pass append arguments to the zdisk/fdimage/isoimage kernel | ||
100 | FDARGS = | ||
101 | # Set this if you want an initrd included with the zdisk/fdimage/isoimage kernel | ||
102 | FDINITRD = | ||
103 | |||
104 | image_cmdline = default linux $(FDARGS) $(if $(FDINITRD),initrd=initrd.img,) | ||
105 | |||
106 | $(obj)/mtools.conf: $(src)/mtools.conf.in | ||
107 | sed -e 's|@OBJ@|$(obj)|g' < $< > $@ | ||
108 | |||
109 | # This requires write access to /dev/fd0 | ||
110 | zdisk: $(BOOTIMAGE) $(obj)/mtools.conf | ||
111 | MTOOLSRC=$(obj)/mtools.conf mformat a: ; sync | ||
112 | syslinux /dev/fd0 ; sync | ||
113 | echo '$(image_cmdline)' | \ | ||
114 | MTOOLSRC=$(src)/mtools.conf mcopy - a:syslinux.cfg | ||
115 | if [ -f '$(FDINITRD)' ] ; then \ | ||
116 | MTOOLSRC=$(obj)/mtools.conf mcopy '$(FDINITRD)' a:initrd.img ; \ | ||
117 | fi | ||
118 | MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) a:linux ; sync | ||
119 | |||
120 | # These require being root or having syslinux 2.02 or higher installed | ||
121 | fdimage fdimage144: $(BOOTIMAGE) $(obj)/mtools.conf | ||
122 | dd if=/dev/zero of=$(obj)/fdimage bs=1024 count=1440 | ||
123 | MTOOLSRC=$(obj)/mtools.conf mformat v: ; sync | ||
124 | syslinux $(obj)/fdimage ; sync | ||
125 | echo '$(image_cmdline)' | \ | ||
126 | MTOOLSRC=$(obj)/mtools.conf mcopy - v:syslinux.cfg | ||
127 | if [ -f '$(FDINITRD)' ] ; then \ | ||
128 | MTOOLSRC=$(obj)/mtools.conf mcopy '$(FDINITRD)' v:initrd.img ; \ | ||
129 | fi | ||
130 | MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) v:linux ; sync | ||
131 | |||
132 | fdimage288: $(BOOTIMAGE) $(obj)/mtools.conf | ||
133 | dd if=/dev/zero of=$(obj)/fdimage bs=1024 count=2880 | ||
134 | MTOOLSRC=$(obj)/mtools.conf mformat w: ; sync | ||
135 | syslinux $(obj)/fdimage ; sync | ||
136 | echo '$(image_cmdline)' | \ | ||
137 | MTOOLSRC=$(obj)/mtools.conf mcopy - w:syslinux.cfg | ||
138 | if [ -f '$(FDINITRD)' ] ; then \ | ||
139 | MTOOLSRC=$(obj)/mtools.conf mcopy '$(FDINITRD)' w:initrd.img ; \ | ||
140 | fi | ||
141 | MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) w:linux ; sync | ||
142 | |||
143 | isoimage: $(BOOTIMAGE) | ||
144 | -rm -rf $(obj)/isoimage | ||
145 | mkdir $(obj)/isoimage | ||
146 | for i in lib lib64 share end ; do \ | ||
147 | if [ -f /usr/$$i/syslinux/isolinux.bin ] ; then \ | ||
148 | cp /usr/$$i/syslinux/isolinux.bin $(obj)/isoimage ; \ | ||
149 | break ; \ | ||
150 | fi ; \ | ||
151 | if [ $$i = end ] ; then exit 1 ; fi ; \ | ||
152 | done | ||
153 | cp $(BOOTIMAGE) $(obj)/isoimage/linux | ||
154 | echo '$(image_cmdline)' > $(obj)/isoimage/isolinux.cfg | ||
155 | if [ -f '$(FDINITRD)' ] ; then \ | ||
156 | cp '$(FDINITRD)' $(obj)/isoimage/initrd.img ; \ | ||
157 | fi | ||
158 | mkisofs -J -r -o $(obj)/image.iso -b isolinux.bin -c boot.cat \ | ||
159 | -no-emul-boot -boot-load-size 4 -boot-info-table \ | ||
160 | $(obj)/isoimage | ||
161 | rm -rf $(obj)/isoimage | ||
162 | |||
163 | zlilo: $(BOOTIMAGE) | ||
164 | if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi | ||
165 | if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi | ||
166 | cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz | ||
167 | cp System.map $(INSTALL_PATH)/ | ||
168 | if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi | ||
169 | |||
170 | install: | ||
171 | sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)" | ||
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c new file mode 100644 index 000000000000..31348d054fca --- /dev/null +++ b/arch/x86/boot/a20.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/a20.c | ||
13 | * | ||
14 | * Enable A20 gate (return -1 on failure) | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | #define MAX_8042_LOOPS 100000 | ||
20 | |||
21 | static int empty_8042(void) | ||
22 | { | ||
23 | u8 status; | ||
24 | int loops = MAX_8042_LOOPS; | ||
25 | |||
26 | while (loops--) { | ||
27 | io_delay(); | ||
28 | |||
29 | status = inb(0x64); | ||
30 | if (status & 1) { | ||
31 | /* Read and discard input data */ | ||
32 | io_delay(); | ||
33 | (void)inb(0x60); | ||
34 | } else if (!(status & 2)) { | ||
35 | /* Buffers empty, finished! */ | ||
36 | return 0; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | /* Returns nonzero if the A20 line is enabled. The memory address | ||
44 | used as a test is the int $0x80 vector, which should be safe. */ | ||
45 | |||
46 | #define A20_TEST_ADDR (4*0x80) | ||
47 | #define A20_TEST_SHORT 32 | ||
48 | #define A20_TEST_LONG 2097152 /* 2^21 */ | ||
49 | |||
50 | static int a20_test(int loops) | ||
51 | { | ||
52 | int ok = 0; | ||
53 | int saved, ctr; | ||
54 | |||
55 | set_fs(0x0000); | ||
56 | set_gs(0xffff); | ||
57 | |||
58 | saved = ctr = rdfs32(A20_TEST_ADDR); | ||
59 | |||
60 | while (loops--) { | ||
61 | wrfs32(++ctr, A20_TEST_ADDR); | ||
62 | io_delay(); /* Serialize and make delay constant */ | ||
63 | ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr; | ||
64 | if (ok) | ||
65 | break; | ||
66 | } | ||
67 | |||
68 | wrfs32(saved, A20_TEST_ADDR); | ||
69 | return ok; | ||
70 | } | ||
71 | |||
72 | /* Quick test to see if A20 is already enabled */ | ||
73 | static int a20_test_short(void) | ||
74 | { | ||
75 | return a20_test(A20_TEST_SHORT); | ||
76 | } | ||
77 | |||
78 | /* Longer test that actually waits for A20 to come on line; this | ||
79 | is useful when dealing with the KBC or other slow external circuitry. */ | ||
80 | static int a20_test_long(void) | ||
81 | { | ||
82 | return a20_test(A20_TEST_LONG); | ||
83 | } | ||
84 | |||
85 | static void enable_a20_bios(void) | ||
86 | { | ||
87 | asm volatile("pushfl; int $0x15; popfl" | ||
88 | : : "a" ((u16)0x2401)); | ||
89 | } | ||
90 | |||
91 | static void enable_a20_kbc(void) | ||
92 | { | ||
93 | empty_8042(); | ||
94 | |||
95 | outb(0xd1, 0x64); /* Command write */ | ||
96 | empty_8042(); | ||
97 | |||
98 | outb(0xdf, 0x60); /* A20 on */ | ||
99 | empty_8042(); | ||
100 | } | ||
101 | |||
102 | static void enable_a20_fast(void) | ||
103 | { | ||
104 | u8 port_a; | ||
105 | |||
106 | port_a = inb(0x92); /* Configuration port A */ | ||
107 | port_a |= 0x02; /* Enable A20 */ | ||
108 | port_a &= ~0x01; /* Do not reset machine */ | ||
109 | outb(port_a, 0x92); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Actual routine to enable A20; return 0 on ok, -1 on failure | ||
114 | */ | ||
115 | |||
116 | #define A20_ENABLE_LOOPS 255 /* Number of times to try */ | ||
117 | |||
118 | int enable_a20(void) | ||
119 | { | ||
120 | int loops = A20_ENABLE_LOOPS; | ||
121 | |||
122 | #if defined(CONFIG_X86_ELAN) | ||
123 | /* Elan croaks if we try to touch the KBC */ | ||
124 | enable_a20_fast(); | ||
125 | while (!a20_test_long()) | ||
126 | ; | ||
127 | return 0; | ||
128 | #elif defined(CONFIG_X86_VOYAGER) | ||
129 | /* On Voyager, a20_test() is unsafe? */ | ||
130 | enable_a20_kbc(); | ||
131 | return 0; | ||
132 | #else | ||
133 | while (loops--) { | ||
134 | /* First, check to see if A20 is already enabled | ||
135 | (legacy free, etc.) */ | ||
136 | if (a20_test_short()) | ||
137 | return 0; | ||
138 | |||
139 | /* Next, try the BIOS (INT 0x15, AX=0x2401) */ | ||
140 | enable_a20_bios(); | ||
141 | if (a20_test_short()) | ||
142 | return 0; | ||
143 | |||
144 | /* Try enabling A20 through the keyboard controller */ | ||
145 | empty_8042(); | ||
146 | if (a20_test_short()) | ||
147 | return 0; /* BIOS worked, but with delayed reaction */ | ||
148 | |||
149 | enable_a20_kbc(); | ||
150 | if (a20_test_long()) | ||
151 | return 0; | ||
152 | |||
153 | /* Finally, try enabling the "fast A20 gate" */ | ||
154 | enable_a20_fast(); | ||
155 | if (a20_test_long()) | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | return -1; | ||
160 | #endif | ||
161 | } | ||
diff --git a/arch/x86/boot/apm.c b/arch/x86/boot/apm.c new file mode 100644 index 000000000000..eab50c55a3a5 --- /dev/null +++ b/arch/x86/boot/apm.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * Original APM BIOS checking by Stephen Rothwell, May 1994 | ||
7 | * (sfr@canb.auug.org.au) | ||
8 | * | ||
9 | * This file is part of the Linux kernel, and is made available under | ||
10 | * the terms of the GNU General Public License version 2. | ||
11 | * | ||
12 | * ----------------------------------------------------------------------- */ | ||
13 | |||
14 | /* | ||
15 | * arch/i386/boot/apm.c | ||
16 | * | ||
17 | * Get APM BIOS information | ||
18 | */ | ||
19 | |||
20 | #include "boot.h" | ||
21 | |||
22 | #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) | ||
23 | |||
24 | int query_apm_bios(void) | ||
25 | { | ||
26 | u16 ax, bx, cx, dx, di; | ||
27 | u32 ebx, esi; | ||
28 | u8 err; | ||
29 | |||
30 | /* APM BIOS installation check */ | ||
31 | ax = 0x5300; | ||
32 | bx = cx = 0; | ||
33 | asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0" | ||
34 | : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx) | ||
35 | : : "esi", "edi"); | ||
36 | |||
37 | if (err) | ||
38 | return -1; /* No APM BIOS */ | ||
39 | |||
40 | if (bx != 0x504d) /* "PM" signature */ | ||
41 | return -1; | ||
42 | |||
43 | if (!(cx & 0x02)) /* 32 bits supported? */ | ||
44 | return -1; | ||
45 | |||
46 | /* Disconnect first, just in case */ | ||
47 | ax = 0x5304; | ||
48 | bx = 0; | ||
49 | asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp" | ||
50 | : "+a" (ax), "+b" (bx) | ||
51 | : : "ecx", "edx", "esi", "edi"); | ||
52 | |||
53 | /* Paranoia */ | ||
54 | ebx = esi = 0; | ||
55 | cx = dx = di = 0; | ||
56 | |||
57 | /* 32-bit connect */ | ||
58 | asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %6" | ||
59 | : "=a" (ax), "+b" (ebx), "+c" (cx), "+d" (dx), | ||
60 | "+S" (esi), "+D" (di), "=m" (err) | ||
61 | : "a" (0x5303)); | ||
62 | |||
63 | boot_params.apm_bios_info.cseg = ax; | ||
64 | boot_params.apm_bios_info.offset = ebx; | ||
65 | boot_params.apm_bios_info.cseg_16 = cx; | ||
66 | boot_params.apm_bios_info.dseg = dx; | ||
67 | boot_params.apm_bios_info.cseg_len = (u16)esi; | ||
68 | boot_params.apm_bios_info.cseg_16_len = esi >> 16; | ||
69 | boot_params.apm_bios_info.dseg_len = di; | ||
70 | |||
71 | if (err) | ||
72 | return -1; | ||
73 | |||
74 | /* Redo the installation check as the 32-bit connect; | ||
75 | some BIOSes return different flags this way... */ | ||
76 | |||
77 | ax = 0x5300; | ||
78 | bx = cx = 0; | ||
79 | asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp ; setc %0" | ||
80 | : "=d" (err), "+a" (ax), "+b" (bx), "+c" (cx) | ||
81 | : : "esi", "edi"); | ||
82 | |||
83 | if (err || bx != 0x504d) { | ||
84 | /* Failure with 32-bit connect, try to disconect and ignore */ | ||
85 | ax = 0x5304; | ||
86 | bx = 0; | ||
87 | asm volatile("pushl %%ebp ; int $0x15 ; popl %%ebp" | ||
88 | : "+a" (ax), "+b" (bx) | ||
89 | : : "ecx", "edx", "esi", "edi"); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | boot_params.apm_bios_info.version = ax; | ||
94 | boot_params.apm_bios_info.flags = cx; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | #endif | ||
diff --git a/arch/x86/boot/bitops.h b/arch/x86/boot/bitops.h new file mode 100644 index 000000000000..8dcc8dc7db88 --- /dev/null +++ b/arch/x86/boot/bitops.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/bitops.h | ||
13 | * | ||
14 | * Very simple bitops for the boot code. | ||
15 | */ | ||
16 | |||
17 | #ifndef BOOT_BITOPS_H | ||
18 | #define BOOT_BITOPS_H | ||
19 | #define _LINUX_BITOPS_H /* Inhibit inclusion of <linux/bitops.h> */ | ||
20 | |||
21 | static inline int constant_test_bit(int nr, const void *addr) | ||
22 | { | ||
23 | const u32 *p = (const u32 *)addr; | ||
24 | return ((1UL << (nr & 31)) & (p[nr >> 5])) != 0; | ||
25 | } | ||
26 | static inline int variable_test_bit(int nr, const void *addr) | ||
27 | { | ||
28 | u8 v; | ||
29 | const u32 *p = (const u32 *)addr; | ||
30 | |||
31 | asm("btl %2,%1; setc %0" : "=qm" (v) : "m" (*p), "Ir" (nr)); | ||
32 | return v; | ||
33 | } | ||
34 | |||
35 | #define test_bit(nr,addr) \ | ||
36 | (__builtin_constant_p(nr) ? \ | ||
37 | constant_test_bit((nr),(addr)) : \ | ||
38 | variable_test_bit((nr),(addr))) | ||
39 | |||
40 | static inline void set_bit(int nr, void *addr) | ||
41 | { | ||
42 | asm("btsl %1,%0" : "+m" (*(u32 *)addr) : "Ir" (nr)); | ||
43 | } | ||
44 | |||
45 | #endif /* BOOT_BITOPS_H */ | ||
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h new file mode 100644 index 000000000000..20bab9431acb --- /dev/null +++ b/arch/x86/boot/boot.h | |||
@@ -0,0 +1,296 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/boot.h | ||
13 | * | ||
14 | * Header file for the real-mode kernel code | ||
15 | */ | ||
16 | |||
17 | #ifndef BOOT_BOOT_H | ||
18 | #define BOOT_BOOT_H | ||
19 | |||
20 | #ifndef __ASSEMBLY__ | ||
21 | |||
22 | #include <stdarg.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/edd.h> | ||
25 | #include <asm/boot.h> | ||
26 | #include <asm/bootparam.h> | ||
27 | |||
28 | /* Useful macros */ | ||
29 | #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) | ||
30 | |||
31 | extern struct setup_header hdr; | ||
32 | extern struct boot_params boot_params; | ||
33 | |||
34 | /* Basic port I/O */ | ||
35 | static inline void outb(u8 v, u16 port) | ||
36 | { | ||
37 | asm volatile("outb %0,%1" : : "a" (v), "dN" (port)); | ||
38 | } | ||
39 | static inline u8 inb(u16 port) | ||
40 | { | ||
41 | u8 v; | ||
42 | asm volatile("inb %1,%0" : "=a" (v) : "dN" (port)); | ||
43 | return v; | ||
44 | } | ||
45 | |||
46 | static inline void outw(u16 v, u16 port) | ||
47 | { | ||
48 | asm volatile("outw %0,%1" : : "a" (v), "dN" (port)); | ||
49 | } | ||
50 | static inline u16 inw(u16 port) | ||
51 | { | ||
52 | u16 v; | ||
53 | asm volatile("inw %1,%0" : "=a" (v) : "dN" (port)); | ||
54 | return v; | ||
55 | } | ||
56 | |||
57 | static inline void outl(u32 v, u16 port) | ||
58 | { | ||
59 | asm volatile("outl %0,%1" : : "a" (v), "dN" (port)); | ||
60 | } | ||
61 | static inline u32 inl(u32 port) | ||
62 | { | ||
63 | u32 v; | ||
64 | asm volatile("inl %1,%0" : "=a" (v) : "dN" (port)); | ||
65 | return v; | ||
66 | } | ||
67 | |||
68 | static inline void io_delay(void) | ||
69 | { | ||
70 | const u16 DELAY_PORT = 0x80; | ||
71 | asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT)); | ||
72 | } | ||
73 | |||
74 | /* These functions are used to reference data in other segments. */ | ||
75 | |||
76 | static inline u16 ds(void) | ||
77 | { | ||
78 | u16 seg; | ||
79 | asm("movw %%ds,%0" : "=rm" (seg)); | ||
80 | return seg; | ||
81 | } | ||
82 | |||
83 | static inline void set_fs(u16 seg) | ||
84 | { | ||
85 | asm volatile("movw %0,%%fs" : : "rm" (seg)); | ||
86 | } | ||
87 | static inline u16 fs(void) | ||
88 | { | ||
89 | u16 seg; | ||
90 | asm volatile("movw %%fs,%0" : "=rm" (seg)); | ||
91 | return seg; | ||
92 | } | ||
93 | |||
94 | static inline void set_gs(u16 seg) | ||
95 | { | ||
96 | asm volatile("movw %0,%%gs" : : "rm" (seg)); | ||
97 | } | ||
98 | static inline u16 gs(void) | ||
99 | { | ||
100 | u16 seg; | ||
101 | asm volatile("movw %%gs,%0" : "=rm" (seg)); | ||
102 | return seg; | ||
103 | } | ||
104 | |||
105 | typedef unsigned int addr_t; | ||
106 | |||
107 | static inline u8 rdfs8(addr_t addr) | ||
108 | { | ||
109 | u8 v; | ||
110 | asm volatile("movb %%fs:%1,%0" : "=r" (v) : "m" (*(u8 *)addr)); | ||
111 | return v; | ||
112 | } | ||
113 | static inline u16 rdfs16(addr_t addr) | ||
114 | { | ||
115 | u16 v; | ||
116 | asm volatile("movw %%fs:%1,%0" : "=r" (v) : "m" (*(u16 *)addr)); | ||
117 | return v; | ||
118 | } | ||
119 | static inline u32 rdfs32(addr_t addr) | ||
120 | { | ||
121 | u32 v; | ||
122 | asm volatile("movl %%fs:%1,%0" : "=r" (v) : "m" (*(u32 *)addr)); | ||
123 | return v; | ||
124 | } | ||
125 | |||
126 | static inline void wrfs8(u8 v, addr_t addr) | ||
127 | { | ||
128 | asm volatile("movb %1,%%fs:%0" : "+m" (*(u8 *)addr) : "r" (v)); | ||
129 | } | ||
130 | static inline void wrfs16(u16 v, addr_t addr) | ||
131 | { | ||
132 | asm volatile("movw %1,%%fs:%0" : "+m" (*(u16 *)addr) : "r" (v)); | ||
133 | } | ||
134 | static inline void wrfs32(u32 v, addr_t addr) | ||
135 | { | ||
136 | asm volatile("movl %1,%%fs:%0" : "+m" (*(u32 *)addr) : "r" (v)); | ||
137 | } | ||
138 | |||
139 | static inline u8 rdgs8(addr_t addr) | ||
140 | { | ||
141 | u8 v; | ||
142 | asm volatile("movb %%gs:%1,%0" : "=r" (v) : "m" (*(u8 *)addr)); | ||
143 | return v; | ||
144 | } | ||
145 | static inline u16 rdgs16(addr_t addr) | ||
146 | { | ||
147 | u16 v; | ||
148 | asm volatile("movw %%gs:%1,%0" : "=r" (v) : "m" (*(u16 *)addr)); | ||
149 | return v; | ||
150 | } | ||
151 | static inline u32 rdgs32(addr_t addr) | ||
152 | { | ||
153 | u32 v; | ||
154 | asm volatile("movl %%gs:%1,%0" : "=r" (v) : "m" (*(u32 *)addr)); | ||
155 | return v; | ||
156 | } | ||
157 | |||
158 | static inline void wrgs8(u8 v, addr_t addr) | ||
159 | { | ||
160 | asm volatile("movb %1,%%gs:%0" : "+m" (*(u8 *)addr) : "r" (v)); | ||
161 | } | ||
162 | static inline void wrgs16(u16 v, addr_t addr) | ||
163 | { | ||
164 | asm volatile("movw %1,%%gs:%0" : "+m" (*(u16 *)addr) : "r" (v)); | ||
165 | } | ||
166 | static inline void wrgs32(u32 v, addr_t addr) | ||
167 | { | ||
168 | asm volatile("movl %1,%%gs:%0" : "+m" (*(u32 *)addr) : "r" (v)); | ||
169 | } | ||
170 | |||
171 | /* Note: these only return true/false, not a signed return value! */ | ||
172 | static inline int memcmp(const void *s1, const void *s2, size_t len) | ||
173 | { | ||
174 | u8 diff; | ||
175 | asm("repe; cmpsb; setnz %0" | ||
176 | : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); | ||
177 | return diff; | ||
178 | } | ||
179 | |||
180 | static inline int memcmp_fs(const void *s1, addr_t s2, size_t len) | ||
181 | { | ||
182 | u8 diff; | ||
183 | asm volatile("fs; repe; cmpsb; setnz %0" | ||
184 | : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); | ||
185 | return diff; | ||
186 | } | ||
187 | static inline int memcmp_gs(const void *s1, addr_t s2, size_t len) | ||
188 | { | ||
189 | u8 diff; | ||
190 | asm volatile("gs; repe; cmpsb; setnz %0" | ||
191 | : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len)); | ||
192 | return diff; | ||
193 | } | ||
194 | |||
195 | static inline int isdigit(int ch) | ||
196 | { | ||
197 | return (ch >= '0') && (ch <= '9'); | ||
198 | } | ||
199 | |||
200 | /* Heap -- available for dynamic lists. */ | ||
201 | #define STACK_SIZE 512 /* Minimum number of bytes for stack */ | ||
202 | |||
203 | extern char _end[]; | ||
204 | extern char *HEAP; | ||
205 | extern char *heap_end; | ||
206 | #define RESET_HEAP() ((void *)( HEAP = _end )) | ||
207 | static inline char *__get_heap(size_t s, size_t a, size_t n) | ||
208 | { | ||
209 | char *tmp; | ||
210 | |||
211 | HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); | ||
212 | tmp = HEAP; | ||
213 | HEAP += s*n; | ||
214 | return tmp; | ||
215 | } | ||
216 | #define GET_HEAP(type, n) \ | ||
217 | ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) | ||
218 | |||
219 | static inline int heap_free(void) | ||
220 | { | ||
221 | return heap_end-HEAP; | ||
222 | } | ||
223 | |||
224 | /* copy.S */ | ||
225 | |||
226 | void copy_to_fs(addr_t dst, void *src, size_t len); | ||
227 | void *copy_from_fs(void *dst, addr_t src, size_t len); | ||
228 | void copy_to_gs(addr_t dst, void *src, size_t len); | ||
229 | void *copy_from_gs(void *dst, addr_t src, size_t len); | ||
230 | void *memcpy(void *dst, void *src, size_t len); | ||
231 | void *memset(void *dst, int c, size_t len); | ||
232 | |||
233 | #define memcpy(d,s,l) __builtin_memcpy(d,s,l) | ||
234 | #define memset(d,c,l) __builtin_memset(d,c,l) | ||
235 | |||
236 | /* a20.c */ | ||
237 | int enable_a20(void); | ||
238 | |||
239 | /* apm.c */ | ||
240 | int query_apm_bios(void); | ||
241 | |||
242 | /* cmdline.c */ | ||
243 | int cmdline_find_option(const char *option, char *buffer, int bufsize); | ||
244 | |||
245 | /* cpu.c, cpucheck.c */ | ||
246 | int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr); | ||
247 | int validate_cpu(void); | ||
248 | |||
249 | /* edd.c */ | ||
250 | void query_edd(void); | ||
251 | |||
252 | /* header.S */ | ||
253 | void __attribute__((noreturn)) die(void); | ||
254 | |||
255 | /* mca.c */ | ||
256 | int query_mca(void); | ||
257 | |||
258 | /* memory.c */ | ||
259 | int detect_memory(void); | ||
260 | |||
261 | /* pm.c */ | ||
262 | void __attribute__((noreturn)) go_to_protected_mode(void); | ||
263 | |||
264 | /* pmjump.S */ | ||
265 | void __attribute__((noreturn)) | ||
266 | protected_mode_jump(u32 entrypoint, u32 bootparams); | ||
267 | |||
268 | /* printf.c */ | ||
269 | int sprintf(char *buf, const char *fmt, ...); | ||
270 | int vsprintf(char *buf, const char *fmt, va_list args); | ||
271 | int printf(const char *fmt, ...); | ||
272 | |||
273 | /* string.c */ | ||
274 | int strcmp(const char *str1, const char *str2); | ||
275 | size_t strnlen(const char *s, size_t maxlen); | ||
276 | unsigned int atou(const char *s); | ||
277 | |||
278 | /* tty.c */ | ||
279 | void puts(const char *); | ||
280 | void putchar(int); | ||
281 | int getchar(void); | ||
282 | void kbd_flush(void); | ||
283 | int getchar_timeout(void); | ||
284 | |||
285 | /* video.c */ | ||
286 | void set_video(void); | ||
287 | |||
288 | /* video-vesa.c */ | ||
289 | void vesa_store_edid(void); | ||
290 | |||
291 | /* voyager.c */ | ||
292 | int query_voyager(void); | ||
293 | |||
294 | #endif /* __ASSEMBLY__ */ | ||
295 | |||
296 | #endif /* BOOT_BOOT_H */ | ||
diff --git a/arch/x86/boot/cmdline.c b/arch/x86/boot/cmdline.c new file mode 100644 index 000000000000..34bb778c4357 --- /dev/null +++ b/arch/x86/boot/cmdline.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/cmdline.c | ||
13 | * | ||
14 | * Simple command-line parser for early boot. | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | static inline int myisspace(u8 c) | ||
20 | { | ||
21 | return c <= ' '; /* Close enough approximation */ | ||
22 | } | ||
23 | |||
24 | /* | ||
25 | * Find a non-boolean option, that is, "option=argument". In accordance | ||
26 | * with standard Linux practice, if this option is repeated, this returns | ||
27 | * the last instance on the command line. | ||
28 | * | ||
29 | * Returns the length of the argument (regardless of if it was | ||
30 | * truncated to fit in the buffer), or -1 on not found. | ||
31 | */ | ||
32 | int cmdline_find_option(const char *option, char *buffer, int bufsize) | ||
33 | { | ||
34 | u32 cmdline_ptr = boot_params.hdr.cmd_line_ptr; | ||
35 | addr_t cptr; | ||
36 | char c; | ||
37 | int len = -1; | ||
38 | const char *opptr = NULL; | ||
39 | char *bufptr = buffer; | ||
40 | enum { | ||
41 | st_wordstart, /* Start of word/after whitespace */ | ||
42 | st_wordcmp, /* Comparing this word */ | ||
43 | st_wordskip, /* Miscompare, skip */ | ||
44 | st_bufcpy /* Copying this to buffer */ | ||
45 | } state = st_wordstart; | ||
46 | |||
47 | if (!cmdline_ptr || cmdline_ptr >= 0x100000) | ||
48 | return -1; /* No command line, or inaccessible */ | ||
49 | |||
50 | cptr = cmdline_ptr & 0xf; | ||
51 | set_fs(cmdline_ptr >> 4); | ||
52 | |||
53 | while (cptr < 0x10000 && (c = rdfs8(cptr++))) { | ||
54 | switch (state) { | ||
55 | case st_wordstart: | ||
56 | if (myisspace(c)) | ||
57 | break; | ||
58 | |||
59 | /* else */ | ||
60 | state = st_wordcmp; | ||
61 | opptr = option; | ||
62 | /* fall through */ | ||
63 | |||
64 | case st_wordcmp: | ||
65 | if (c == '=' && !*opptr) { | ||
66 | len = 0; | ||
67 | bufptr = buffer; | ||
68 | state = st_bufcpy; | ||
69 | } else if (myisspace(c)) { | ||
70 | state = st_wordstart; | ||
71 | } else if (c != *opptr++) { | ||
72 | state = st_wordskip; | ||
73 | } | ||
74 | break; | ||
75 | |||
76 | case st_wordskip: | ||
77 | if (myisspace(c)) | ||
78 | state = st_wordstart; | ||
79 | break; | ||
80 | |||
81 | case st_bufcpy: | ||
82 | if (myisspace(c)) { | ||
83 | state = st_wordstart; | ||
84 | } else { | ||
85 | if (len < bufsize-1) | ||
86 | *bufptr++ = c; | ||
87 | len++; | ||
88 | } | ||
89 | break; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (bufsize) | ||
94 | *bufptr = '\0'; | ||
95 | |||
96 | return len; | ||
97 | } | ||
diff --git a/arch/x86/boot/code16gcc.h b/arch/x86/boot/code16gcc.h new file mode 100644 index 000000000000..d93e48010b61 --- /dev/null +++ b/arch/x86/boot/code16gcc.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * code16gcc.h | ||
3 | * | ||
4 | * This file is -include'd when compiling 16-bit C code. | ||
5 | * Note: this asm() needs to be emitted before gcc emits any code. | ||
6 | * Depending on gcc version, this requires -fno-unit-at-a-time or | ||
7 | * -fno-toplevel-reorder. | ||
8 | * | ||
9 | * Hopefully gcc will eventually have a real -m16 option so we can | ||
10 | * drop this hack long term. | ||
11 | */ | ||
12 | |||
13 | #ifndef __ASSEMBLY__ | ||
14 | asm(".code16gcc"); | ||
15 | #endif | ||
diff --git a/arch/x86/boot/copy.S b/arch/x86/boot/copy.S new file mode 100644 index 000000000000..ef127e56a3cf --- /dev/null +++ b/arch/x86/boot/copy.S | |||
@@ -0,0 +1,101 @@ | |||
1 | /* ----------------------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/copy.S | ||
13 | * | ||
14 | * Memory copy routines | ||
15 | */ | ||
16 | |||
17 | .code16gcc | ||
18 | .text | ||
19 | |||
20 | .globl memcpy | ||
21 | .type memcpy, @function | ||
22 | memcpy: | ||
23 | pushw %si | ||
24 | pushw %di | ||
25 | movw %ax, %di | ||
26 | movw %dx, %si | ||
27 | pushw %cx | ||
28 | shrw $2, %cx | ||
29 | rep; movsl | ||
30 | popw %cx | ||
31 | andw $3, %cx | ||
32 | rep; movsb | ||
33 | popw %di | ||
34 | popw %si | ||
35 | ret | ||
36 | .size memcpy, .-memcpy | ||
37 | |||
38 | .globl memset | ||
39 | .type memset, @function | ||
40 | memset: | ||
41 | pushw %di | ||
42 | movw %ax, %di | ||
43 | movzbl %dl, %eax | ||
44 | imull $0x01010101,%eax | ||
45 | pushw %cx | ||
46 | shrw $2, %cx | ||
47 | rep; stosl | ||
48 | popw %cx | ||
49 | andw $3, %cx | ||
50 | rep; stosb | ||
51 | popw %di | ||
52 | ret | ||
53 | .size memset, .-memset | ||
54 | |||
55 | .globl copy_from_fs | ||
56 | .type copy_from_fs, @function | ||
57 | copy_from_fs: | ||
58 | pushw %ds | ||
59 | pushw %fs | ||
60 | popw %ds | ||
61 | call memcpy | ||
62 | popw %ds | ||
63 | ret | ||
64 | .size copy_from_fs, .-copy_from_fs | ||
65 | |||
66 | .globl copy_to_fs | ||
67 | .type copy_to_fs, @function | ||
68 | copy_to_fs: | ||
69 | pushw %es | ||
70 | pushw %fs | ||
71 | popw %es | ||
72 | call memcpy | ||
73 | popw %es | ||
74 | ret | ||
75 | .size copy_to_fs, .-copy_to_fs | ||
76 | |||
77 | #if 0 /* Not currently used, but can be enabled as needed */ | ||
78 | |||
79 | .globl copy_from_gs | ||
80 | .type copy_from_gs, @function | ||
81 | copy_from_gs: | ||
82 | pushw %ds | ||
83 | pushw %gs | ||
84 | popw %ds | ||
85 | call memcpy | ||
86 | popw %ds | ||
87 | ret | ||
88 | .size copy_from_gs, .-copy_from_gs | ||
89 | .globl copy_to_gs | ||
90 | |||
91 | .type copy_to_gs, @function | ||
92 | copy_to_gs: | ||
93 | pushw %es | ||
94 | pushw %gs | ||
95 | popw %es | ||
96 | call memcpy | ||
97 | popw %es | ||
98 | ret | ||
99 | .size copy_to_gs, .-copy_to_gs | ||
100 | |||
101 | #endif | ||
diff --git a/arch/x86/boot/cpu.c b/arch/x86/boot/cpu.c new file mode 100644 index 000000000000..2a5c32da5852 --- /dev/null +++ b/arch/x86/boot/cpu.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/cpu.c | ||
13 | * | ||
14 | * Check for obligatory CPU features and abort if the features are not | ||
15 | * present. | ||
16 | */ | ||
17 | |||
18 | #include "boot.h" | ||
19 | #include "bitops.h" | ||
20 | #include <asm/cpufeature.h> | ||
21 | |||
22 | static char *cpu_name(int level) | ||
23 | { | ||
24 | static char buf[6]; | ||
25 | |||
26 | if (level == 64) { | ||
27 | return "x86-64"; | ||
28 | } else { | ||
29 | sprintf(buf, "i%d86", level); | ||
30 | return buf; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | int validate_cpu(void) | ||
35 | { | ||
36 | u32 *err_flags; | ||
37 | int cpu_level, req_level; | ||
38 | |||
39 | check_cpu(&cpu_level, &req_level, &err_flags); | ||
40 | |||
41 | if (cpu_level < req_level) { | ||
42 | printf("This kernel requires an %s CPU, ", | ||
43 | cpu_name(req_level)); | ||
44 | printf("but only detected an %s CPU.\n", | ||
45 | cpu_name(cpu_level)); | ||
46 | return -1; | ||
47 | } | ||
48 | |||
49 | if (err_flags) { | ||
50 | int i, j; | ||
51 | puts("This kernel requires the following features " | ||
52 | "not present on the CPU:\n"); | ||
53 | |||
54 | for (i = 0; i < NCAPINTS; i++) { | ||
55 | u32 e = err_flags[i]; | ||
56 | |||
57 | for (j = 0; j < 32; j++) { | ||
58 | if (e & 1) | ||
59 | printf("%d:%d ", i, j); | ||
60 | |||
61 | e >>= 1; | ||
62 | } | ||
63 | } | ||
64 | putchar('\n'); | ||
65 | return -1; | ||
66 | } else { | ||
67 | return 0; | ||
68 | } | ||
69 | } | ||
diff --git a/arch/x86/boot/cpucheck.c b/arch/x86/boot/cpucheck.c new file mode 100644 index 000000000000..e655a89c5510 --- /dev/null +++ b/arch/x86/boot/cpucheck.c | |||
@@ -0,0 +1,268 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/cpucheck.c | ||
13 | * | ||
14 | * Check for obligatory CPU features and abort if the features are not | ||
15 | * present. This code should be compilable as 16-, 32- or 64-bit | ||
16 | * code, so be very careful with types and inline assembly. | ||
17 | * | ||
18 | * This code should not contain any messages; that requires an | ||
19 | * additional wrapper. | ||
20 | * | ||
21 | * As written, this code is not safe for inclusion into the kernel | ||
22 | * proper (after FPU initialization, in particular). | ||
23 | */ | ||
24 | |||
25 | #ifdef _SETUP | ||
26 | # include "boot.h" | ||
27 | # include "bitops.h" | ||
28 | #endif | ||
29 | #include <linux/types.h> | ||
30 | #include <asm/cpufeature.h> | ||
31 | #include <asm/processor-flags.h> | ||
32 | #include <asm/required-features.h> | ||
33 | #include <asm/msr-index.h> | ||
34 | |||
35 | struct cpu_features { | ||
36 | int level; /* Family, or 64 for x86-64 */ | ||
37 | int model; | ||
38 | u32 flags[NCAPINTS]; | ||
39 | }; | ||
40 | |||
41 | static struct cpu_features cpu; | ||
42 | static u32 cpu_vendor[3]; | ||
43 | static u32 err_flags[NCAPINTS]; | ||
44 | |||
45 | #ifdef CONFIG_X86_64 | ||
46 | static const int req_level = 64; | ||
47 | #elif defined(CONFIG_X86_MINIMUM_CPU_FAMILY) | ||
48 | static const int req_level = CONFIG_X86_MINIMUM_CPU_FAMILY; | ||
49 | #else | ||
50 | static const int req_level = 3; | ||
51 | #endif | ||
52 | |||
53 | static const u32 req_flags[NCAPINTS] = | ||
54 | { | ||
55 | REQUIRED_MASK0, | ||
56 | REQUIRED_MASK1, | ||
57 | REQUIRED_MASK2, | ||
58 | REQUIRED_MASK3, | ||
59 | REQUIRED_MASK4, | ||
60 | REQUIRED_MASK5, | ||
61 | REQUIRED_MASK6, | ||
62 | REQUIRED_MASK7, | ||
63 | }; | ||
64 | |||
65 | #define A32(a,b,c,d) (((d) << 24)+((c) << 16)+((b) << 8)+(a)) | ||
66 | |||
67 | static int is_amd(void) | ||
68 | { | ||
69 | return cpu_vendor[0] == A32('A','u','t','h') && | ||
70 | cpu_vendor[1] == A32('e','n','t','i') && | ||
71 | cpu_vendor[2] == A32('c','A','M','D'); | ||
72 | } | ||
73 | |||
74 | static int is_centaur(void) | ||
75 | { | ||
76 | return cpu_vendor[0] == A32('C','e','n','t') && | ||
77 | cpu_vendor[1] == A32('a','u','r','H') && | ||
78 | cpu_vendor[2] == A32('a','u','l','s'); | ||
79 | } | ||
80 | |||
81 | static int is_transmeta(void) | ||
82 | { | ||
83 | return cpu_vendor[0] == A32('G','e','n','u') && | ||
84 | cpu_vendor[1] == A32('i','n','e','T') && | ||
85 | cpu_vendor[2] == A32('M','x','8','6'); | ||
86 | } | ||
87 | |||
88 | static int has_fpu(void) | ||
89 | { | ||
90 | u16 fcw = -1, fsw = -1; | ||
91 | u32 cr0; | ||
92 | |||
93 | asm("movl %%cr0,%0" : "=r" (cr0)); | ||
94 | if (cr0 & (X86_CR0_EM|X86_CR0_TS)) { | ||
95 | cr0 &= ~(X86_CR0_EM|X86_CR0_TS); | ||
96 | asm volatile("movl %0,%%cr0" : : "r" (cr0)); | ||
97 | } | ||
98 | |||
99 | asm volatile("fninit ; fnstsw %0 ; fnstcw %1" | ||
100 | : "+m" (fsw), "+m" (fcw)); | ||
101 | |||
102 | return fsw == 0 && (fcw & 0x103f) == 0x003f; | ||
103 | } | ||
104 | |||
105 | static int has_eflag(u32 mask) | ||
106 | { | ||
107 | u32 f0, f1; | ||
108 | |||
109 | asm("pushfl ; " | ||
110 | "pushfl ; " | ||
111 | "popl %0 ; " | ||
112 | "movl %0,%1 ; " | ||
113 | "xorl %2,%1 ; " | ||
114 | "pushl %1 ; " | ||
115 | "popfl ; " | ||
116 | "pushfl ; " | ||
117 | "popl %1 ; " | ||
118 | "popfl" | ||
119 | : "=&r" (f0), "=&r" (f1) | ||
120 | : "ri" (mask)); | ||
121 | |||
122 | return !!((f0^f1) & mask); | ||
123 | } | ||
124 | |||
125 | static void get_flags(void) | ||
126 | { | ||
127 | u32 max_intel_level, max_amd_level; | ||
128 | u32 tfms; | ||
129 | |||
130 | if (has_fpu()) | ||
131 | set_bit(X86_FEATURE_FPU, cpu.flags); | ||
132 | |||
133 | if (has_eflag(X86_EFLAGS_ID)) { | ||
134 | asm("cpuid" | ||
135 | : "=a" (max_intel_level), | ||
136 | "=b" (cpu_vendor[0]), | ||
137 | "=d" (cpu_vendor[1]), | ||
138 | "=c" (cpu_vendor[2]) | ||
139 | : "a" (0)); | ||
140 | |||
141 | if (max_intel_level >= 0x00000001 && | ||
142 | max_intel_level <= 0x0000ffff) { | ||
143 | asm("cpuid" | ||
144 | : "=a" (tfms), | ||
145 | "=c" (cpu.flags[4]), | ||
146 | "=d" (cpu.flags[0]) | ||
147 | : "a" (0x00000001) | ||
148 | : "ebx"); | ||
149 | cpu.level = (tfms >> 8) & 15; | ||
150 | cpu.model = (tfms >> 4) & 15; | ||
151 | if (cpu.level >= 6) | ||
152 | cpu.model += ((tfms >> 16) & 0xf) << 4; | ||
153 | } | ||
154 | |||
155 | asm("cpuid" | ||
156 | : "=a" (max_amd_level) | ||
157 | : "a" (0x80000000) | ||
158 | : "ebx", "ecx", "edx"); | ||
159 | |||
160 | if (max_amd_level >= 0x80000001 && | ||
161 | max_amd_level <= 0x8000ffff) { | ||
162 | u32 eax = 0x80000001; | ||
163 | asm("cpuid" | ||
164 | : "+a" (eax), | ||
165 | "=c" (cpu.flags[6]), | ||
166 | "=d" (cpu.flags[1]) | ||
167 | : : "ebx"); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | /* Returns a bitmask of which words we have error bits in */ | ||
173 | static int check_flags(void) | ||
174 | { | ||
175 | u32 err; | ||
176 | int i; | ||
177 | |||
178 | err = 0; | ||
179 | for (i = 0; i < NCAPINTS; i++) { | ||
180 | err_flags[i] = req_flags[i] & ~cpu.flags[i]; | ||
181 | if (err_flags[i]) | ||
182 | err |= 1 << i; | ||
183 | } | ||
184 | |||
185 | return err; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * Returns -1 on error. | ||
190 | * | ||
191 | * *cpu_level is set to the current CPU level; *req_level to the required | ||
192 | * level. x86-64 is considered level 64 for this purpose. | ||
193 | * | ||
194 | * *err_flags_ptr is set to the flags error array if there are flags missing. | ||
195 | */ | ||
196 | int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr) | ||
197 | { | ||
198 | int err; | ||
199 | |||
200 | memset(&cpu.flags, 0, sizeof cpu.flags); | ||
201 | cpu.level = 3; | ||
202 | |||
203 | if (has_eflag(X86_EFLAGS_AC)) | ||
204 | cpu.level = 4; | ||
205 | |||
206 | get_flags(); | ||
207 | err = check_flags(); | ||
208 | |||
209 | if (test_bit(X86_FEATURE_LM, cpu.flags)) | ||
210 | cpu.level = 64; | ||
211 | |||
212 | if (err == 0x01 && | ||
213 | !(err_flags[0] & | ||
214 | ~((1 << X86_FEATURE_XMM)|(1 << X86_FEATURE_XMM2))) && | ||
215 | is_amd()) { | ||
216 | /* If this is an AMD and we're only missing SSE+SSE2, try to | ||
217 | turn them on */ | ||
218 | |||
219 | u32 ecx = MSR_K7_HWCR; | ||
220 | u32 eax, edx; | ||
221 | |||
222 | asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); | ||
223 | eax &= ~(1 << 15); | ||
224 | asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); | ||
225 | |||
226 | get_flags(); /* Make sure it really did something */ | ||
227 | err = check_flags(); | ||
228 | } else if (err == 0x01 && | ||
229 | !(err_flags[0] & ~(1 << X86_FEATURE_CX8)) && | ||
230 | is_centaur() && cpu.model >= 6) { | ||
231 | /* If this is a VIA C3, we might have to enable CX8 | ||
232 | explicitly */ | ||
233 | |||
234 | u32 ecx = MSR_VIA_FCR; | ||
235 | u32 eax, edx; | ||
236 | |||
237 | asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); | ||
238 | eax |= (1<<1)|(1<<7); | ||
239 | asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); | ||
240 | |||
241 | set_bit(X86_FEATURE_CX8, cpu.flags); | ||
242 | err = check_flags(); | ||
243 | } else if (err == 0x01 && is_transmeta()) { | ||
244 | /* Transmeta might have masked feature bits in word 0 */ | ||
245 | |||
246 | u32 ecx = 0x80860004; | ||
247 | u32 eax, edx; | ||
248 | u32 level = 1; | ||
249 | |||
250 | asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); | ||
251 | asm("wrmsr" : : "a" (~0), "d" (edx), "c" (ecx)); | ||
252 | asm("cpuid" | ||
253 | : "+a" (level), "=d" (cpu.flags[0]) | ||
254 | : : "ecx", "ebx"); | ||
255 | asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx)); | ||
256 | |||
257 | err = check_flags(); | ||
258 | } | ||
259 | |||
260 | if (err_flags_ptr) | ||
261 | *err_flags_ptr = err ? err_flags : NULL; | ||
262 | if (cpu_level_ptr) | ||
263 | *cpu_level_ptr = cpu.level; | ||
264 | if (req_level_ptr) | ||
265 | *req_level_ptr = req_level; | ||
266 | |||
267 | return (cpu.level < req_level || err) ? -1 : 0; | ||
268 | } | ||
diff --git a/arch/x86/boot/edd.c b/arch/x86/boot/edd.c new file mode 100644 index 000000000000..bd138e442ec2 --- /dev/null +++ b/arch/x86/boot/edd.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/edd.c | ||
13 | * | ||
14 | * Get EDD BIOS disk information | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include <linux/edd.h> | ||
19 | |||
20 | #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) | ||
21 | |||
22 | /* | ||
23 | * Read the MBR (first sector) from a specific device. | ||
24 | */ | ||
25 | static int read_mbr(u8 devno, void *buf) | ||
26 | { | ||
27 | u16 ax, bx, cx, dx; | ||
28 | |||
29 | ax = 0x0201; /* Legacy Read, one sector */ | ||
30 | cx = 0x0001; /* Sector 0-0-1 */ | ||
31 | dx = devno; | ||
32 | bx = (size_t)buf; | ||
33 | asm volatile("pushfl; stc; int $0x13; setc %%al; popfl" | ||
34 | : "+a" (ax), "+c" (cx), "+d" (dx), "+b" (bx) | ||
35 | : : "esi", "edi", "memory"); | ||
36 | |||
37 | return -(u8)ax; /* 0 or -1 */ | ||
38 | } | ||
39 | |||
40 | static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) | ||
41 | { | ||
42 | int sector_size; | ||
43 | char *mbrbuf_ptr, *mbrbuf_end; | ||
44 | u32 buf_base, mbr_base; | ||
45 | extern char _end[]; | ||
46 | |||
47 | sector_size = ei->params.bytes_per_sector; | ||
48 | if (!sector_size) | ||
49 | sector_size = 512; /* Best available guess */ | ||
50 | |||
51 | /* Produce a naturally aligned buffer on the heap */ | ||
52 | buf_base = (ds() << 4) + (u32)&_end; | ||
53 | mbr_base = (buf_base+sector_size-1) & ~(sector_size-1); | ||
54 | mbrbuf_ptr = _end + (mbr_base-buf_base); | ||
55 | mbrbuf_end = mbrbuf_ptr + sector_size; | ||
56 | |||
57 | /* Make sure we actually have space on the heap... */ | ||
58 | if (!(boot_params.hdr.loadflags & CAN_USE_HEAP)) | ||
59 | return -1; | ||
60 | if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) | ||
61 | return -1; | ||
62 | |||
63 | if (read_mbr(devno, mbrbuf_ptr)) | ||
64 | return -1; | ||
65 | |||
66 | *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int get_edd_info(u8 devno, struct edd_info *ei) | ||
71 | { | ||
72 | u16 ax, bx, cx, dx, di; | ||
73 | |||
74 | memset(ei, 0, sizeof *ei); | ||
75 | |||
76 | /* Check Extensions Present */ | ||
77 | |||
78 | ax = 0x4100; | ||
79 | bx = EDDMAGIC1; | ||
80 | dx = devno; | ||
81 | asm("pushfl; stc; int $0x13; setc %%al; popfl" | ||
82 | : "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx) | ||
83 | : : "esi", "edi"); | ||
84 | |||
85 | if ((u8)ax) | ||
86 | return -1; /* No extended information */ | ||
87 | |||
88 | if (bx != EDDMAGIC2) | ||
89 | return -1; | ||
90 | |||
91 | ei->device = devno; | ||
92 | ei->version = ax >> 8; /* EDD version number */ | ||
93 | ei->interface_support = cx; /* EDD functionality subsets */ | ||
94 | |||
95 | /* Extended Get Device Parameters */ | ||
96 | |||
97 | ei->params.length = sizeof(ei->params); | ||
98 | ax = 0x4800; | ||
99 | dx = devno; | ||
100 | asm("pushfl; int $0x13; popfl" | ||
101 | : "+a" (ax), "+d" (dx), "=m" (ei->params) | ||
102 | : "S" (&ei->params) | ||
103 | : "ebx", "ecx", "edi"); | ||
104 | |||
105 | /* Get legacy CHS parameters */ | ||
106 | |||
107 | /* Ralf Brown recommends setting ES:DI to 0:0 */ | ||
108 | ax = 0x0800; | ||
109 | dx = devno; | ||
110 | di = 0; | ||
111 | asm("pushw %%es; " | ||
112 | "movw %%di,%%es; " | ||
113 | "pushfl; stc; int $0x13; setc %%al; popfl; " | ||
114 | "popw %%es" | ||
115 | : "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di) | ||
116 | : : "esi"); | ||
117 | |||
118 | if ((u8)ax == 0) { | ||
119 | ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2); | ||
120 | ei->legacy_max_head = dx >> 8; | ||
121 | ei->legacy_sectors_per_track = cx & 0x3f; | ||
122 | } | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | void query_edd(void) | ||
128 | { | ||
129 | char eddarg[8]; | ||
130 | int do_mbr = 1; | ||
131 | int do_edd = 1; | ||
132 | int devno; | ||
133 | struct edd_info ei, *edp; | ||
134 | u32 *mbrptr; | ||
135 | |||
136 | if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) { | ||
137 | if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) | ||
138 | do_mbr = 0; | ||
139 | else if (!strcmp(eddarg, "off")) | ||
140 | do_edd = 0; | ||
141 | } | ||
142 | |||
143 | edp = boot_params.eddbuf; | ||
144 | mbrptr = boot_params.edd_mbr_sig_buffer; | ||
145 | |||
146 | if (!do_edd) | ||
147 | return; | ||
148 | |||
149 | for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { | ||
150 | /* | ||
151 | * Scan the BIOS-supported hard disks and query EDD | ||
152 | * information... | ||
153 | */ | ||
154 | get_edd_info(devno, &ei); | ||
155 | |||
156 | if (boot_params.eddbuf_entries < EDDMAXNR) { | ||
157 | memcpy(edp, &ei, sizeof ei); | ||
158 | edp++; | ||
159 | boot_params.eddbuf_entries++; | ||
160 | } | ||
161 | |||
162 | if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++)) | ||
163 | boot_params.edd_mbr_sig_buf_entries = devno-0x80+1; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | #endif | ||
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S new file mode 100644 index 000000000000..f3140e596d40 --- /dev/null +++ b/arch/x86/boot/header.S | |||
@@ -0,0 +1,283 @@ | |||
1 | /* | ||
2 | * header.S | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | * | ||
6 | * Based on bootsect.S and setup.S | ||
7 | * modified by more people than can be counted | ||
8 | * | ||
9 | * Rewritten as a common file by H. Peter Anvin (Apr 2007) | ||
10 | * | ||
11 | * BIG FAT NOTE: We're in real mode using 64k segments. Therefore segment | ||
12 | * addresses must be multiplied by 16 to obtain their respective linear | ||
13 | * addresses. To avoid confusion, linear addresses are written using leading | ||
14 | * hex while segment addresses are written as segment:offset. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <asm/segment.h> | ||
19 | #include <linux/utsrelease.h> | ||
20 | #include <asm/boot.h> | ||
21 | #include <asm/e820.h> | ||
22 | #include <asm/page.h> | ||
23 | #include <asm/setup.h> | ||
24 | #include "boot.h" | ||
25 | |||
26 | SETUPSECTS = 4 /* default nr of setup-sectors */ | ||
27 | BOOTSEG = 0x07C0 /* original address of boot-sector */ | ||
28 | SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */ | ||
29 | SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */ | ||
30 | /* to be loaded */ | ||
31 | ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */ | ||
32 | SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */ | ||
33 | |||
34 | #ifndef SVGA_MODE | ||
35 | #define SVGA_MODE ASK_VGA | ||
36 | #endif | ||
37 | |||
38 | #ifndef RAMDISK | ||
39 | #define RAMDISK 0 | ||
40 | #endif | ||
41 | |||
42 | #ifndef ROOT_RDONLY | ||
43 | #define ROOT_RDONLY 1 | ||
44 | #endif | ||
45 | |||
46 | .code16 | ||
47 | .section ".bstext", "ax" | ||
48 | |||
49 | .global bootsect_start | ||
50 | bootsect_start: | ||
51 | |||
52 | # Normalize the start address | ||
53 | ljmp $BOOTSEG, $start2 | ||
54 | |||
55 | start2: | ||
56 | movw %cs, %ax | ||
57 | movw %ax, %ds | ||
58 | movw %ax, %es | ||
59 | movw %ax, %ss | ||
60 | xorw %sp, %sp | ||
61 | sti | ||
62 | cld | ||
63 | |||
64 | movw $bugger_off_msg, %si | ||
65 | |||
66 | msg_loop: | ||
67 | lodsb | ||
68 | andb %al, %al | ||
69 | jz bs_die | ||
70 | movb $0xe, %ah | ||
71 | movw $7, %bx | ||
72 | int $0x10 | ||
73 | jmp msg_loop | ||
74 | |||
75 | bs_die: | ||
76 | # Allow the user to press a key, then reboot | ||
77 | xorw %ax, %ax | ||
78 | int $0x16 | ||
79 | int $0x19 | ||
80 | |||
81 | # int 0x19 should never return. In case it does anyway, | ||
82 | # invoke the BIOS reset code... | ||
83 | ljmp $0xf000,$0xfff0 | ||
84 | |||
85 | .section ".bsdata", "a" | ||
86 | bugger_off_msg: | ||
87 | .ascii "Direct booting from floppy is no longer supported.\r\n" | ||
88 | .ascii "Please use a boot loader program instead.\r\n" | ||
89 | .ascii "\n" | ||
90 | .ascii "Remove disk and press any key to reboot . . .\r\n" | ||
91 | .byte 0 | ||
92 | |||
93 | |||
94 | # Kernel attributes; used by setup. This is part 1 of the | ||
95 | # header, from the old boot sector. | ||
96 | |||
97 | .section ".header", "a" | ||
98 | .globl hdr | ||
99 | hdr: | ||
100 | setup_sects: .byte SETUPSECTS | ||
101 | root_flags: .word ROOT_RDONLY | ||
102 | syssize: .long SYSSIZE | ||
103 | ram_size: .word RAMDISK | ||
104 | vid_mode: .word SVGA_MODE | ||
105 | root_dev: .word ROOT_DEV | ||
106 | boot_flag: .word 0xAA55 | ||
107 | |||
108 | # offset 512, entry point | ||
109 | |||
110 | .globl _start | ||
111 | _start: | ||
112 | # Explicitly enter this as bytes, or the assembler | ||
113 | # tries to generate a 3-byte jump here, which causes | ||
114 | # everything else to push off to the wrong offset. | ||
115 | .byte 0xeb # short (2-byte) jump | ||
116 | .byte start_of_setup-1f | ||
117 | 1: | ||
118 | |||
119 | # Part 2 of the header, from the old setup.S | ||
120 | |||
121 | .ascii "HdrS" # header signature | ||
122 | .word 0x0206 # header version number (>= 0x0105) | ||
123 | # or else old loadlin-1.5 will fail) | ||
124 | .globl realmode_swtch | ||
125 | realmode_swtch: .word 0, 0 # default_switch, SETUPSEG | ||
126 | start_sys_seg: .word SYSSEG | ||
127 | .word kernel_version-512 # pointing to kernel version string | ||
128 | # above section of header is compatible | ||
129 | # with loadlin-1.5 (header v1.5). Don't | ||
130 | # change it. | ||
131 | |||
132 | type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, | ||
133 | # Bootlin, SYSLX, bootsect...) | ||
134 | # See Documentation/i386/boot.txt for | ||
135 | # assigned ids | ||
136 | |||
137 | # flags, unused bits must be zero (RFU) bit within loadflags | ||
138 | loadflags: | ||
139 | LOADED_HIGH = 1 # If set, the kernel is loaded high | ||
140 | CAN_USE_HEAP = 0x80 # If set, the loader also has set | ||
141 | # heap_end_ptr to tell how much | ||
142 | # space behind setup.S can be used for | ||
143 | # heap purposes. | ||
144 | # Only the loader knows what is free | ||
145 | #ifndef __BIG_KERNEL__ | ||
146 | .byte 0 | ||
147 | #else | ||
148 | .byte LOADED_HIGH | ||
149 | #endif | ||
150 | |||
151 | setup_move_size: .word 0x8000 # size to move, when setup is not | ||
152 | # loaded at 0x90000. We will move setup | ||
153 | # to 0x90000 then just before jumping | ||
154 | # into the kernel. However, only the | ||
155 | # loader knows how much data behind | ||
156 | # us also needs to be loaded. | ||
157 | |||
158 | code32_start: # here loaders can put a different | ||
159 | # start address for 32-bit code. | ||
160 | #ifndef __BIG_KERNEL__ | ||
161 | .long 0x1000 # 0x1000 = default for zImage | ||
162 | #else | ||
163 | .long 0x100000 # 0x100000 = default for big kernel | ||
164 | #endif | ||
165 | |||
166 | ramdisk_image: .long 0 # address of loaded ramdisk image | ||
167 | # Here the loader puts the 32-bit | ||
168 | # address where it loaded the image. | ||
169 | # This only will be read by the kernel. | ||
170 | |||
171 | ramdisk_size: .long 0 # its size in bytes | ||
172 | |||
173 | bootsect_kludge: | ||
174 | .long 0 # obsolete | ||
175 | |||
176 | heap_end_ptr: .word _end+1024 # (Header version 0x0201 or later) | ||
177 | # space from here (exclusive) down to | ||
178 | # end of setup code can be used by setup | ||
179 | # for local heap purposes. | ||
180 | |||
181 | pad1: .word 0 | ||
182 | cmd_line_ptr: .long 0 # (Header version 0x0202 or later) | ||
183 | # If nonzero, a 32-bit pointer | ||
184 | # to the kernel command line. | ||
185 | # The command line should be | ||
186 | # located between the start of | ||
187 | # setup and the end of low | ||
188 | # memory (0xa0000), or it may | ||
189 | # get overwritten before it | ||
190 | # gets read. If this field is | ||
191 | # used, there is no longer | ||
192 | # anything magical about the | ||
193 | # 0x90000 segment; the setup | ||
194 | # can be located anywhere in | ||
195 | # low memory 0x10000 or higher. | ||
196 | |||
197 | ramdisk_max: .long (-__PAGE_OFFSET-(512 << 20)-1) & 0x7fffffff | ||
198 | # (Header version 0x0203 or later) | ||
199 | # The highest safe address for | ||
200 | # the contents of an initrd | ||
201 | |||
202 | kernel_alignment: .long CONFIG_PHYSICAL_ALIGN #physical addr alignment | ||
203 | #required for protected mode | ||
204 | #kernel | ||
205 | #ifdef CONFIG_RELOCATABLE | ||
206 | relocatable_kernel: .byte 1 | ||
207 | #else | ||
208 | relocatable_kernel: .byte 0 | ||
209 | #endif | ||
210 | pad2: .byte 0 | ||
211 | pad3: .word 0 | ||
212 | |||
213 | cmdline_size: .long COMMAND_LINE_SIZE-1 #length of the command line, | ||
214 | #added with boot protocol | ||
215 | #version 2.06 | ||
216 | |||
217 | # End of setup header ##################################################### | ||
218 | |||
219 | .section ".inittext", "ax" | ||
220 | start_of_setup: | ||
221 | #ifdef SAFE_RESET_DISK_CONTROLLER | ||
222 | # Reset the disk controller. | ||
223 | movw $0x0000, %ax # Reset disk controller | ||
224 | movb $0x80, %dl # All disks | ||
225 | int $0x13 | ||
226 | #endif | ||
227 | |||
228 | # We will have entered with %cs = %ds+0x20, normalize %cs so | ||
229 | # it is on par with the other segments. | ||
230 | pushw %ds | ||
231 | pushw $setup2 | ||
232 | lretw | ||
233 | |||
234 | setup2: | ||
235 | # Force %es = %ds | ||
236 | movw %ds, %ax | ||
237 | movw %ax, %es | ||
238 | cld | ||
239 | |||
240 | # Stack paranoia: align the stack and make sure it is good | ||
241 | # for both 16- and 32-bit references. In particular, if we | ||
242 | # were meant to have been using the full 16-bit segment, the | ||
243 | # caller might have set %sp to zero, which breaks %esp-based | ||
244 | # references. | ||
245 | andw $~3, %sp # dword align (might as well...) | ||
246 | jnz 1f | ||
247 | movw $0xfffc, %sp # Make sure we're not zero | ||
248 | 1: movzwl %sp, %esp # Clear upper half of %esp | ||
249 | sti | ||
250 | |||
251 | # Check signature at end of setup | ||
252 | cmpl $0x5a5aaa55, setup_sig | ||
253 | jne setup_bad | ||
254 | |||
255 | # Zero the bss | ||
256 | movw $__bss_start, %di | ||
257 | movw $_end+3, %cx | ||
258 | xorl %eax, %eax | ||
259 | subw %di, %cx | ||
260 | shrw $2, %cx | ||
261 | rep; stosl | ||
262 | |||
263 | # Jump to C code (should not return) | ||
264 | calll main | ||
265 | |||
266 | # Setup corrupt somehow... | ||
267 | setup_bad: | ||
268 | movl $setup_corrupt, %eax | ||
269 | calll puts | ||
270 | # Fall through... | ||
271 | |||
272 | .globl die | ||
273 | .type die, @function | ||
274 | die: | ||
275 | hlt | ||
276 | jmp die | ||
277 | |||
278 | .size die, .-die | ||
279 | |||
280 | .section ".initdata", "a" | ||
281 | setup_corrupt: | ||
282 | .byte 7 | ||
283 | .string "No setup signature found...\n" | ||
diff --git a/arch/x86/boot/install.sh b/arch/x86/boot/install.sh new file mode 100644 index 000000000000..88d77761d01b --- /dev/null +++ b/arch/x86/boot/install.sh | |||
@@ -0,0 +1,61 @@ | |||
1 | #!/bin/sh | ||
2 | # | ||
3 | # arch/i386/boot/install.sh | ||
4 | # | ||
5 | # This file is subject to the terms and conditions of the GNU General Public | ||
6 | # License. See the file "COPYING" in the main directory of this archive | ||
7 | # for more details. | ||
8 | # | ||
9 | # Copyright (C) 1995 by Linus Torvalds | ||
10 | # | ||
11 | # Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin | ||
12 | # | ||
13 | # "make install" script for i386 architecture | ||
14 | # | ||
15 | # Arguments: | ||
16 | # $1 - kernel version | ||
17 | # $2 - kernel image file | ||
18 | # $3 - kernel map file | ||
19 | # $4 - default install path (blank if root directory) | ||
20 | # | ||
21 | |||
22 | verify () { | ||
23 | if [ ! -f "$1" ]; then | ||
24 | echo "" 1>&2 | ||
25 | echo " *** Missing file: $1" 1>&2 | ||
26 | echo ' *** You need to run "make" before "make install".' 1>&2 | ||
27 | echo "" 1>&2 | ||
28 | exit 1 | ||
29 | fi | ||
30 | } | ||
31 | |||
32 | # Make sure the files actually exist | ||
33 | verify "$2" | ||
34 | verify "$3" | ||
35 | |||
36 | # User may have a custom install script | ||
37 | |||
38 | if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi | ||
39 | if [ -x /sbin/${CROSS_COMPILE}installkernel ]; then exec /sbin/${CROSS_COMPILE}installkernel "$@"; fi | ||
40 | |||
41 | # Default install - same as make zlilo | ||
42 | |||
43 | if [ -f $4/vmlinuz ]; then | ||
44 | mv $4/vmlinuz $4/vmlinuz.old | ||
45 | fi | ||
46 | |||
47 | if [ -f $4/System.map ]; then | ||
48 | mv $4/System.map $4/System.old | ||
49 | fi | ||
50 | |||
51 | cat $2 > $4/vmlinuz | ||
52 | cp $3 $4/System.map | ||
53 | |||
54 | if [ -x /sbin/lilo ]; then | ||
55 | /sbin/lilo | ||
56 | elif [ -x /etc/lilo/install ]; then | ||
57 | /etc/lilo/install | ||
58 | else | ||
59 | sync | ||
60 | echo "Cannot find LILO." | ||
61 | fi | ||
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c new file mode 100644 index 000000000000..0eeef3989a17 --- /dev/null +++ b/arch/x86/boot/main.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/main.c | ||
13 | * | ||
14 | * Main module for the real-mode kernel code | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | struct boot_params boot_params __attribute__((aligned(16))); | ||
20 | |||
21 | char *HEAP = _end; | ||
22 | char *heap_end = _end; /* Default end of heap = no heap */ | ||
23 | |||
24 | /* | ||
25 | * Copy the header into the boot parameter block. Since this | ||
26 | * screws up the old-style command line protocol, adjust by | ||
27 | * filling in the new-style command line pointer instead. | ||
28 | */ | ||
29 | #define OLD_CL_MAGIC 0xA33F | ||
30 | #define OLD_CL_ADDRESS 0x20 | ||
31 | |||
32 | static void copy_boot_params(void) | ||
33 | { | ||
34 | struct old_cmdline { | ||
35 | u16 cl_magic; | ||
36 | u16 cl_offset; | ||
37 | }; | ||
38 | const struct old_cmdline * const oldcmd = | ||
39 | (const struct old_cmdline *)OLD_CL_ADDRESS; | ||
40 | |||
41 | BUILD_BUG_ON(sizeof boot_params != 4096); | ||
42 | memcpy(&boot_params.hdr, &hdr, sizeof hdr); | ||
43 | |||
44 | if (!boot_params.hdr.cmd_line_ptr && | ||
45 | oldcmd->cl_magic == OLD_CL_MAGIC) { | ||
46 | /* Old-style command line protocol. */ | ||
47 | u16 cmdline_seg; | ||
48 | |||
49 | /* Figure out if the command line falls in the region | ||
50 | of memory that an old kernel would have copied up | ||
51 | to 0x90000... */ | ||
52 | if (oldcmd->cl_offset < boot_params.hdr.setup_move_size) | ||
53 | cmdline_seg = ds(); | ||
54 | else | ||
55 | cmdline_seg = 0x9000; | ||
56 | |||
57 | boot_params.hdr.cmd_line_ptr = | ||
58 | (cmdline_seg << 4) + oldcmd->cl_offset; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Set the keyboard repeat rate to maximum. Unclear why this | ||
64 | * is done here; this might be possible to kill off as stale code. | ||
65 | */ | ||
66 | static void keyboard_set_repeat(void) | ||
67 | { | ||
68 | u16 ax = 0x0305; | ||
69 | u16 bx = 0; | ||
70 | asm volatile("int $0x16" | ||
71 | : "+a" (ax), "+b" (bx) | ||
72 | : : "ecx", "edx", "esi", "edi"); | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * Get Intel SpeedStep (IST) information. | ||
77 | */ | ||
78 | static void query_ist(void) | ||
79 | { | ||
80 | asm("int $0x15" | ||
81 | : "=a" (boot_params.ist_info.signature), | ||
82 | "=b" (boot_params.ist_info.command), | ||
83 | "=c" (boot_params.ist_info.event), | ||
84 | "=d" (boot_params.ist_info.perf_level) | ||
85 | : "a" (0x0000e980), /* IST Support */ | ||
86 | "d" (0x47534943)); /* Request value */ | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Tell the BIOS what CPU mode we intend to run in. | ||
91 | */ | ||
92 | static void set_bios_mode(void) | ||
93 | { | ||
94 | #ifdef CONFIG_X86_64 | ||
95 | u32 eax, ebx; | ||
96 | |||
97 | eax = 0xec00; | ||
98 | ebx = 2; | ||
99 | asm volatile("int $0x15" | ||
100 | : "+a" (eax), "+b" (ebx) | ||
101 | : : "ecx", "edx", "esi", "edi"); | ||
102 | #endif | ||
103 | } | ||
104 | |||
105 | void main(void) | ||
106 | { | ||
107 | /* First, copy the boot header into the "zeropage" */ | ||
108 | copy_boot_params(); | ||
109 | |||
110 | /* End of heap check */ | ||
111 | if (boot_params.hdr.loadflags & CAN_USE_HEAP) { | ||
112 | heap_end = (char *)(boot_params.hdr.heap_end_ptr | ||
113 | +0x200-STACK_SIZE); | ||
114 | } else { | ||
115 | /* Boot protocol 2.00 only, no heap available */ | ||
116 | puts("WARNING: Ancient bootloader, some functionality " | ||
117 | "may be limited!\n"); | ||
118 | } | ||
119 | |||
120 | /* Make sure we have all the proper CPU support */ | ||
121 | if (validate_cpu()) { | ||
122 | puts("Unable to boot - please use a kernel appropriate " | ||
123 | "for your CPU.\n"); | ||
124 | die(); | ||
125 | } | ||
126 | |||
127 | /* Tell the BIOS what CPU mode we intend to run in. */ | ||
128 | set_bios_mode(); | ||
129 | |||
130 | /* Detect memory layout */ | ||
131 | detect_memory(); | ||
132 | |||
133 | /* Set keyboard repeat rate (why?) */ | ||
134 | keyboard_set_repeat(); | ||
135 | |||
136 | /* Set the video mode */ | ||
137 | set_video(); | ||
138 | |||
139 | /* Query MCA information */ | ||
140 | query_mca(); | ||
141 | |||
142 | /* Voyager */ | ||
143 | #ifdef CONFIG_X86_VOYAGER | ||
144 | query_voyager(); | ||
145 | #endif | ||
146 | |||
147 | /* Query Intel SpeedStep (IST) information */ | ||
148 | query_ist(); | ||
149 | |||
150 | /* Query APM information */ | ||
151 | #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) | ||
152 | query_apm_bios(); | ||
153 | #endif | ||
154 | |||
155 | /* Query EDD information */ | ||
156 | #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) | ||
157 | query_edd(); | ||
158 | #endif | ||
159 | /* Do the last things and invoke protected mode */ | ||
160 | go_to_protected_mode(); | ||
161 | } | ||
diff --git a/arch/x86/boot/mca.c b/arch/x86/boot/mca.c new file mode 100644 index 000000000000..68222f2d4b67 --- /dev/null +++ b/arch/x86/boot/mca.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/mca.c | ||
13 | * | ||
14 | * Get the MCA system description table | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | int query_mca(void) | ||
20 | { | ||
21 | u8 err; | ||
22 | u16 es, bx, len; | ||
23 | |||
24 | asm("pushw %%es ; " | ||
25 | "int $0x15 ; " | ||
26 | "setc %0 ; " | ||
27 | "movw %%es, %1 ; " | ||
28 | "popw %%es" | ||
29 | : "=acd" (err), "=acdSD" (es), "=b" (bx) | ||
30 | : "a" (0xc000)); | ||
31 | |||
32 | if (err) | ||
33 | return -1; /* No MCA present */ | ||
34 | |||
35 | set_fs(es); | ||
36 | len = rdfs16(bx); | ||
37 | |||
38 | if (len > sizeof(boot_params.sys_desc_table)) | ||
39 | len = sizeof(boot_params.sys_desc_table); | ||
40 | |||
41 | copy_from_fs(&boot_params.sys_desc_table, bx, len); | ||
42 | return 0; | ||
43 | } | ||
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c new file mode 100644 index 000000000000..378353956b5d --- /dev/null +++ b/arch/x86/boot/memory.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/memory.c | ||
13 | * | ||
14 | * Memory detection code | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | #define SMAP 0x534d4150 /* ASCII "SMAP" */ | ||
20 | |||
21 | static int detect_memory_e820(void) | ||
22 | { | ||
23 | int count = 0; | ||
24 | u32 next = 0; | ||
25 | u32 size, id; | ||
26 | u8 err; | ||
27 | struct e820entry *desc = boot_params.e820_map; | ||
28 | |||
29 | do { | ||
30 | size = sizeof(struct e820entry); | ||
31 | |||
32 | /* Important: %edx is clobbered by some BIOSes, | ||
33 | so it must be either used for the error output | ||
34 | or explicitly marked clobbered. */ | ||
35 | asm("int $0x15; setc %0" | ||
36 | : "=d" (err), "+b" (next), "=a" (id), "+c" (size), | ||
37 | "=m" (*desc) | ||
38 | : "D" (desc), "d" (SMAP), "a" (0xe820)); | ||
39 | |||
40 | /* Some BIOSes stop returning SMAP in the middle of | ||
41 | the search loop. We don't know exactly how the BIOS | ||
42 | screwed up the map at that point, we might have a | ||
43 | partial map, the full map, or complete garbage, so | ||
44 | just return failure. */ | ||
45 | if (id != SMAP) { | ||
46 | count = 0; | ||
47 | break; | ||
48 | } | ||
49 | |||
50 | if (err) | ||
51 | break; | ||
52 | |||
53 | count++; | ||
54 | desc++; | ||
55 | } while (next && count < E820MAX); | ||
56 | |||
57 | return boot_params.e820_entries = count; | ||
58 | } | ||
59 | |||
60 | static int detect_memory_e801(void) | ||
61 | { | ||
62 | u16 ax, bx, cx, dx; | ||
63 | u8 err; | ||
64 | |||
65 | bx = cx = dx = 0; | ||
66 | ax = 0xe801; | ||
67 | asm("stc; int $0x15; setc %0" | ||
68 | : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); | ||
69 | |||
70 | if (err) | ||
71 | return -1; | ||
72 | |||
73 | /* Do we really need to do this? */ | ||
74 | if (cx || dx) { | ||
75 | ax = cx; | ||
76 | bx = dx; | ||
77 | } | ||
78 | |||
79 | if (ax > 15*1024) | ||
80 | return -1; /* Bogus! */ | ||
81 | |||
82 | /* This ignores memory above 16MB if we have a memory hole | ||
83 | there. If someone actually finds a machine with a memory | ||
84 | hole at 16MB and no support for 0E820h they should probably | ||
85 | generate a fake e820 map. */ | ||
86 | boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int detect_memory_88(void) | ||
92 | { | ||
93 | u16 ax; | ||
94 | u8 err; | ||
95 | |||
96 | ax = 0x8800; | ||
97 | asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); | ||
98 | |||
99 | boot_params.screen_info.ext_mem_k = ax; | ||
100 | |||
101 | return -err; | ||
102 | } | ||
103 | |||
104 | int detect_memory(void) | ||
105 | { | ||
106 | int err = -1; | ||
107 | |||
108 | if (detect_memory_e820() > 0) | ||
109 | err = 0; | ||
110 | |||
111 | if (!detect_memory_e801()) | ||
112 | err = 0; | ||
113 | |||
114 | if (!detect_memory_88()) | ||
115 | err = 0; | ||
116 | |||
117 | return err; | ||
118 | } | ||
diff --git a/arch/x86/boot/mtools.conf.in b/arch/x86/boot/mtools.conf.in new file mode 100644 index 000000000000..efd6d2490c1d --- /dev/null +++ b/arch/x86/boot/mtools.conf.in | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # mtools configuration file for "make (b)zdisk" | ||
3 | # | ||
4 | |||
5 | # Actual floppy drive | ||
6 | drive a: | ||
7 | file="/dev/fd0" | ||
8 | |||
9 | # 1.44 MB floppy disk image | ||
10 | drive v: | ||
11 | file="@OBJ@/fdimage" cylinders=80 heads=2 sectors=18 filter | ||
12 | |||
13 | # 2.88 MB floppy disk image (mostly for virtual uses) | ||
14 | drive w: | ||
15 | file="@OBJ@/fdimage" cylinders=80 heads=2 sectors=36 filter | ||
16 | |||
17 | |||
diff --git a/arch/x86/boot/pm.c b/arch/x86/boot/pm.c new file mode 100644 index 000000000000..09fb342cc62e --- /dev/null +++ b/arch/x86/boot/pm.c | |||
@@ -0,0 +1,174 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/pm.c | ||
13 | * | ||
14 | * Prepare the machine for transition to protected mode. | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include <asm/segment.h> | ||
19 | |||
20 | /* | ||
21 | * Invoke the realmode switch hook if present; otherwise | ||
22 | * disable all interrupts. | ||
23 | */ | ||
24 | static void realmode_switch_hook(void) | ||
25 | { | ||
26 | if (boot_params.hdr.realmode_swtch) { | ||
27 | asm volatile("lcallw *%0" | ||
28 | : : "m" (boot_params.hdr.realmode_swtch) | ||
29 | : "eax", "ebx", "ecx", "edx"); | ||
30 | } else { | ||
31 | asm volatile("cli"); | ||
32 | outb(0x80, 0x70); /* Disable NMI */ | ||
33 | io_delay(); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * A zImage kernel is loaded at 0x10000 but wants to run at 0x1000. | ||
39 | * A bzImage kernel is loaded and runs at 0x100000. | ||
40 | */ | ||
41 | static void move_kernel_around(void) | ||
42 | { | ||
43 | /* Note: rely on the compile-time option here rather than | ||
44 | the LOADED_HIGH flag. The Qemu kernel loader unconditionally | ||
45 | sets the loadflags to zero. */ | ||
46 | #ifndef __BIG_KERNEL__ | ||
47 | u16 dst_seg, src_seg; | ||
48 | u32 syssize; | ||
49 | |||
50 | dst_seg = 0x1000 >> 4; | ||
51 | src_seg = 0x10000 >> 4; | ||
52 | syssize = boot_params.hdr.syssize; /* Size in 16-byte paragraphs */ | ||
53 | |||
54 | while (syssize) { | ||
55 | int paras = (syssize >= 0x1000) ? 0x1000 : syssize; | ||
56 | int dwords = paras << 2; | ||
57 | |||
58 | asm volatile("pushw %%es ; " | ||
59 | "pushw %%ds ; " | ||
60 | "movw %1,%%es ; " | ||
61 | "movw %2,%%ds ; " | ||
62 | "xorw %%di,%%di ; " | ||
63 | "xorw %%si,%%si ; " | ||
64 | "rep;movsl ; " | ||
65 | "popw %%ds ; " | ||
66 | "popw %%es" | ||
67 | : "+c" (dwords) | ||
68 | : "r" (dst_seg), "r" (src_seg) | ||
69 | : "esi", "edi"); | ||
70 | |||
71 | syssize -= paras; | ||
72 | dst_seg += paras; | ||
73 | src_seg += paras; | ||
74 | } | ||
75 | #endif | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Disable all interrupts at the legacy PIC. | ||
80 | */ | ||
81 | static void mask_all_interrupts(void) | ||
82 | { | ||
83 | outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */ | ||
84 | io_delay(); | ||
85 | outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */ | ||
86 | io_delay(); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Reset IGNNE# if asserted in the FPU. | ||
91 | */ | ||
92 | static void reset_coprocessor(void) | ||
93 | { | ||
94 | outb(0, 0xf0); | ||
95 | io_delay(); | ||
96 | outb(0, 0xf1); | ||
97 | io_delay(); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Set up the GDT | ||
102 | */ | ||
103 | #define GDT_ENTRY(flags,base,limit) \ | ||
104 | (((u64)(base & 0xff000000) << 32) | \ | ||
105 | ((u64)flags << 40) | \ | ||
106 | ((u64)(limit & 0x00ff0000) << 32) | \ | ||
107 | ((u64)(base & 0x00ffff00) << 16) | \ | ||
108 | ((u64)(limit & 0x0000ffff))) | ||
109 | |||
110 | struct gdt_ptr { | ||
111 | u16 len; | ||
112 | u32 ptr; | ||
113 | } __attribute__((packed)); | ||
114 | |||
115 | static void setup_gdt(void) | ||
116 | { | ||
117 | /* There are machines which are known to not boot with the GDT | ||
118 | being 8-byte unaligned. Intel recommends 16 byte alignment. */ | ||
119 | static const u64 boot_gdt[] __attribute__((aligned(16))) = { | ||
120 | /* CS: code, read/execute, 4 GB, base 0 */ | ||
121 | [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), | ||
122 | /* DS: data, read/write, 4 GB, base 0 */ | ||
123 | [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), | ||
124 | }; | ||
125 | /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead | ||
126 | of the gdt_ptr contents. Thus, make it static so it will | ||
127 | stay in memory, at least long enough that we switch to the | ||
128 | proper kernel GDT. */ | ||
129 | static struct gdt_ptr gdt; | ||
130 | |||
131 | gdt.len = sizeof(boot_gdt)-1; | ||
132 | gdt.ptr = (u32)&boot_gdt + (ds() << 4); | ||
133 | |||
134 | asm volatile("lgdtl %0" : : "m" (gdt)); | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Set up the IDT | ||
139 | */ | ||
140 | static void setup_idt(void) | ||
141 | { | ||
142 | static const struct gdt_ptr null_idt = {0, 0}; | ||
143 | asm volatile("lidtl %0" : : "m" (null_idt)); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Actual invocation sequence | ||
148 | */ | ||
149 | void go_to_protected_mode(void) | ||
150 | { | ||
151 | /* Hook before leaving real mode, also disables interrupts */ | ||
152 | realmode_switch_hook(); | ||
153 | |||
154 | /* Move the kernel/setup to their final resting places */ | ||
155 | move_kernel_around(); | ||
156 | |||
157 | /* Enable the A20 gate */ | ||
158 | if (enable_a20()) { | ||
159 | puts("A20 gate not responding, unable to boot...\n"); | ||
160 | die(); | ||
161 | } | ||
162 | |||
163 | /* Reset coprocessor (IGNNE#) */ | ||
164 | reset_coprocessor(); | ||
165 | |||
166 | /* Mask all interrupts in the PIC */ | ||
167 | mask_all_interrupts(); | ||
168 | |||
169 | /* Actual transition to protected mode... */ | ||
170 | setup_idt(); | ||
171 | setup_gdt(); | ||
172 | protected_mode_jump(boot_params.hdr.code32_start, | ||
173 | (u32)&boot_params + (ds() << 4)); | ||
174 | } | ||
diff --git a/arch/x86/boot/pmjump.S b/arch/x86/boot/pmjump.S new file mode 100644 index 000000000000..2e559233725a --- /dev/null +++ b/arch/x86/boot/pmjump.S | |||
@@ -0,0 +1,54 @@ | |||
1 | /* ----------------------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/pmjump.S | ||
13 | * | ||
14 | * The actual transition into protected mode | ||
15 | */ | ||
16 | |||
17 | #include <asm/boot.h> | ||
18 | #include <asm/segment.h> | ||
19 | |||
20 | .text | ||
21 | |||
22 | .globl protected_mode_jump | ||
23 | .type protected_mode_jump, @function | ||
24 | |||
25 | .code16 | ||
26 | |||
27 | /* | ||
28 | * void protected_mode_jump(u32 entrypoint, u32 bootparams); | ||
29 | */ | ||
30 | protected_mode_jump: | ||
31 | xorl %ebx, %ebx # Flag to indicate this is a boot | ||
32 | movl %edx, %esi # Pointer to boot_params table | ||
33 | movl %eax, 2f # Patch ljmpl instruction | ||
34 | jmp 1f # Short jump to flush instruction q. | ||
35 | |||
36 | 1: | ||
37 | movw $__BOOT_DS, %cx | ||
38 | |||
39 | movl %cr0, %edx | ||
40 | orb $1, %dl # Protected mode (PE) bit | ||
41 | movl %edx, %cr0 | ||
42 | |||
43 | movw %cx, %ds | ||
44 | movw %cx, %es | ||
45 | movw %cx, %fs | ||
46 | movw %cx, %gs | ||
47 | movw %cx, %ss | ||
48 | |||
49 | # Jump to the 32-bit entrypoint | ||
50 | .byte 0x66, 0xea # ljmpl opcode | ||
51 | 2: .long 0 # offset | ||
52 | .word __BOOT_CS # segment | ||
53 | |||
54 | .size protected_mode_jump, .-protected_mode_jump | ||
diff --git a/arch/x86/boot/printf.c b/arch/x86/boot/printf.c new file mode 100644 index 000000000000..1a09f9309d3c --- /dev/null +++ b/arch/x86/boot/printf.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/printf.c | ||
13 | * | ||
14 | * Oh, it's a waste of space, but oh-so-yummy for debugging. This | ||
15 | * version of printf() does not include 64-bit support. "Live with | ||
16 | * it." | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include "boot.h" | ||
21 | |||
22 | static int skip_atoi(const char **s) | ||
23 | { | ||
24 | int i = 0; | ||
25 | |||
26 | while (isdigit(**s)) | ||
27 | i = i * 10 + *((*s)++) - '0'; | ||
28 | return i; | ||
29 | } | ||
30 | |||
31 | #define ZEROPAD 1 /* pad with zero */ | ||
32 | #define SIGN 2 /* unsigned/signed long */ | ||
33 | #define PLUS 4 /* show plus */ | ||
34 | #define SPACE 8 /* space if plus */ | ||
35 | #define LEFT 16 /* left justified */ | ||
36 | #define SPECIAL 32 /* 0x */ | ||
37 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ | ||
38 | |||
39 | #define do_div(n,base) ({ \ | ||
40 | int __res; \ | ||
41 | __res = ((unsigned long) n) % (unsigned) base; \ | ||
42 | n = ((unsigned long) n) / (unsigned) base; \ | ||
43 | __res; }) | ||
44 | |||
45 | static char *number(char *str, long num, int base, int size, int precision, | ||
46 | int type) | ||
47 | { | ||
48 | char c, sign, tmp[66]; | ||
49 | const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; | ||
50 | int i; | ||
51 | |||
52 | if (type & LARGE) | ||
53 | digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
54 | if (type & LEFT) | ||
55 | type &= ~ZEROPAD; | ||
56 | if (base < 2 || base > 36) | ||
57 | return 0; | ||
58 | c = (type & ZEROPAD) ? '0' : ' '; | ||
59 | sign = 0; | ||
60 | if (type & SIGN) { | ||
61 | if (num < 0) { | ||
62 | sign = '-'; | ||
63 | num = -num; | ||
64 | size--; | ||
65 | } else if (type & PLUS) { | ||
66 | sign = '+'; | ||
67 | size--; | ||
68 | } else if (type & SPACE) { | ||
69 | sign = ' '; | ||
70 | size--; | ||
71 | } | ||
72 | } | ||
73 | if (type & SPECIAL) { | ||
74 | if (base == 16) | ||
75 | size -= 2; | ||
76 | else if (base == 8) | ||
77 | size--; | ||
78 | } | ||
79 | i = 0; | ||
80 | if (num == 0) | ||
81 | tmp[i++] = '0'; | ||
82 | else | ||
83 | while (num != 0) | ||
84 | tmp[i++] = digits[do_div(num, base)]; | ||
85 | if (i > precision) | ||
86 | precision = i; | ||
87 | size -= precision; | ||
88 | if (!(type & (ZEROPAD + LEFT))) | ||
89 | while (size-- > 0) | ||
90 | *str++ = ' '; | ||
91 | if (sign) | ||
92 | *str++ = sign; | ||
93 | if (type & SPECIAL) { | ||
94 | if (base == 8) | ||
95 | *str++ = '0'; | ||
96 | else if (base == 16) { | ||
97 | *str++ = '0'; | ||
98 | *str++ = digits[33]; | ||
99 | } | ||
100 | } | ||
101 | if (!(type & LEFT)) | ||
102 | while (size-- > 0) | ||
103 | *str++ = c; | ||
104 | while (i < precision--) | ||
105 | *str++ = '0'; | ||
106 | while (i-- > 0) | ||
107 | *str++ = tmp[i]; | ||
108 | while (size-- > 0) | ||
109 | *str++ = ' '; | ||
110 | return str; | ||
111 | } | ||
112 | |||
113 | int vsprintf(char *buf, const char *fmt, va_list args) | ||
114 | { | ||
115 | int len; | ||
116 | unsigned long num; | ||
117 | int i, base; | ||
118 | char *str; | ||
119 | const char *s; | ||
120 | |||
121 | int flags; /* flags to number() */ | ||
122 | |||
123 | int field_width; /* width of output field */ | ||
124 | int precision; /* min. # of digits for integers; max | ||
125 | number of chars for from string */ | ||
126 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ | ||
127 | |||
128 | for (str = buf; *fmt; ++fmt) { | ||
129 | if (*fmt != '%') { | ||
130 | *str++ = *fmt; | ||
131 | continue; | ||
132 | } | ||
133 | |||
134 | /* process flags */ | ||
135 | flags = 0; | ||
136 | repeat: | ||
137 | ++fmt; /* this also skips first '%' */ | ||
138 | switch (*fmt) { | ||
139 | case '-': | ||
140 | flags |= LEFT; | ||
141 | goto repeat; | ||
142 | case '+': | ||
143 | flags |= PLUS; | ||
144 | goto repeat; | ||
145 | case ' ': | ||
146 | flags |= SPACE; | ||
147 | goto repeat; | ||
148 | case '#': | ||
149 | flags |= SPECIAL; | ||
150 | goto repeat; | ||
151 | case '0': | ||
152 | flags |= ZEROPAD; | ||
153 | goto repeat; | ||
154 | } | ||
155 | |||
156 | /* get field width */ | ||
157 | field_width = -1; | ||
158 | if (isdigit(*fmt)) | ||
159 | field_width = skip_atoi(&fmt); | ||
160 | else if (*fmt == '*') { | ||
161 | ++fmt; | ||
162 | /* it's the next argument */ | ||
163 | field_width = va_arg(args, int); | ||
164 | if (field_width < 0) { | ||
165 | field_width = -field_width; | ||
166 | flags |= LEFT; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* get the precision */ | ||
171 | precision = -1; | ||
172 | if (*fmt == '.') { | ||
173 | ++fmt; | ||
174 | if (isdigit(*fmt)) | ||
175 | precision = skip_atoi(&fmt); | ||
176 | else if (*fmt == '*') { | ||
177 | ++fmt; | ||
178 | /* it's the next argument */ | ||
179 | precision = va_arg(args, int); | ||
180 | } | ||
181 | if (precision < 0) | ||
182 | precision = 0; | ||
183 | } | ||
184 | |||
185 | /* get the conversion qualifier */ | ||
186 | qualifier = -1; | ||
187 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { | ||
188 | qualifier = *fmt; | ||
189 | ++fmt; | ||
190 | } | ||
191 | |||
192 | /* default base */ | ||
193 | base = 10; | ||
194 | |||
195 | switch (*fmt) { | ||
196 | case 'c': | ||
197 | if (!(flags & LEFT)) | ||
198 | while (--field_width > 0) | ||
199 | *str++ = ' '; | ||
200 | *str++ = (unsigned char)va_arg(args, int); | ||
201 | while (--field_width > 0) | ||
202 | *str++ = ' '; | ||
203 | continue; | ||
204 | |||
205 | case 's': | ||
206 | s = va_arg(args, char *); | ||
207 | len = strnlen(s, precision); | ||
208 | |||
209 | if (!(flags & LEFT)) | ||
210 | while (len < field_width--) | ||
211 | *str++ = ' '; | ||
212 | for (i = 0; i < len; ++i) | ||
213 | *str++ = *s++; | ||
214 | while (len < field_width--) | ||
215 | *str++ = ' '; | ||
216 | continue; | ||
217 | |||
218 | case 'p': | ||
219 | if (field_width == -1) { | ||
220 | field_width = 2 * sizeof(void *); | ||
221 | flags |= ZEROPAD; | ||
222 | } | ||
223 | str = number(str, | ||
224 | (unsigned long)va_arg(args, void *), 16, | ||
225 | field_width, precision, flags); | ||
226 | continue; | ||
227 | |||
228 | case 'n': | ||
229 | if (qualifier == 'l') { | ||
230 | long *ip = va_arg(args, long *); | ||
231 | *ip = (str - buf); | ||
232 | } else { | ||
233 | int *ip = va_arg(args, int *); | ||
234 | *ip = (str - buf); | ||
235 | } | ||
236 | continue; | ||
237 | |||
238 | case '%': | ||
239 | *str++ = '%'; | ||
240 | continue; | ||
241 | |||
242 | /* integer number formats - set up the flags and "break" */ | ||
243 | case 'o': | ||
244 | base = 8; | ||
245 | break; | ||
246 | |||
247 | case 'X': | ||
248 | flags |= LARGE; | ||
249 | case 'x': | ||
250 | base = 16; | ||
251 | break; | ||
252 | |||
253 | case 'd': | ||
254 | case 'i': | ||
255 | flags |= SIGN; | ||
256 | case 'u': | ||
257 | break; | ||
258 | |||
259 | default: | ||
260 | *str++ = '%'; | ||
261 | if (*fmt) | ||
262 | *str++ = *fmt; | ||
263 | else | ||
264 | --fmt; | ||
265 | continue; | ||
266 | } | ||
267 | if (qualifier == 'l') | ||
268 | num = va_arg(args, unsigned long); | ||
269 | else if (qualifier == 'h') { | ||
270 | num = (unsigned short)va_arg(args, int); | ||
271 | if (flags & SIGN) | ||
272 | num = (short)num; | ||
273 | } else if (flags & SIGN) | ||
274 | num = va_arg(args, int); | ||
275 | else | ||
276 | num = va_arg(args, unsigned int); | ||
277 | str = number(str, num, base, field_width, precision, flags); | ||
278 | } | ||
279 | *str = '\0'; | ||
280 | return str - buf; | ||
281 | } | ||
282 | |||
283 | int sprintf(char *buf, const char *fmt, ...) | ||
284 | { | ||
285 | va_list args; | ||
286 | int i; | ||
287 | |||
288 | va_start(args, fmt); | ||
289 | i = vsprintf(buf, fmt, args); | ||
290 | va_end(args); | ||
291 | return i; | ||
292 | } | ||
293 | |||
294 | int printf(const char *fmt, ...) | ||
295 | { | ||
296 | char printf_buf[1024]; | ||
297 | va_list args; | ||
298 | int printed; | ||
299 | |||
300 | va_start(args, fmt); | ||
301 | printed = vsprintf(printf_buf, fmt, args); | ||
302 | va_end(args); | ||
303 | |||
304 | puts(printf_buf); | ||
305 | |||
306 | return printed; | ||
307 | } | ||
diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld new file mode 100644 index 000000000000..df9234b3a5e0 --- /dev/null +++ b/arch/x86/boot/setup.ld | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * setup.ld | ||
3 | * | ||
4 | * Linker script for the i386 setup code | ||
5 | */ | ||
6 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | ||
7 | OUTPUT_ARCH(i386) | ||
8 | ENTRY(_start) | ||
9 | |||
10 | SECTIONS | ||
11 | { | ||
12 | . = 0; | ||
13 | .bstext : { *(.bstext) } | ||
14 | .bsdata : { *(.bsdata) } | ||
15 | |||
16 | . = 497; | ||
17 | .header : { *(.header) } | ||
18 | .inittext : { *(.inittext) } | ||
19 | .initdata : { *(.initdata) } | ||
20 | .text : { *(.text*) } | ||
21 | |||
22 | . = ALIGN(16); | ||
23 | .rodata : { *(.rodata*) } | ||
24 | |||
25 | .videocards : { | ||
26 | video_cards = .; | ||
27 | *(.videocards) | ||
28 | video_cards_end = .; | ||
29 | } | ||
30 | |||
31 | . = ALIGN(16); | ||
32 | .data : { *(.data*) } | ||
33 | |||
34 | .signature : { | ||
35 | setup_sig = .; | ||
36 | LONG(0x5a5aaa55) | ||
37 | } | ||
38 | |||
39 | |||
40 | . = ALIGN(16); | ||
41 | .bss : | ||
42 | { | ||
43 | __bss_start = .; | ||
44 | *(.bss) | ||
45 | __bss_end = .; | ||
46 | } | ||
47 | . = ALIGN(16); | ||
48 | _end = .; | ||
49 | |||
50 | /DISCARD/ : { *(.note*) } | ||
51 | |||
52 | . = ASSERT(_end <= 0x8000, "Setup too big!"); | ||
53 | . = ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!"); | ||
54 | } | ||
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c new file mode 100644 index 000000000000..481a22097781 --- /dev/null +++ b/arch/x86/boot/string.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/string.c | ||
13 | * | ||
14 | * Very basic string functions | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | int strcmp(const char *str1, const char *str2) | ||
20 | { | ||
21 | const unsigned char *s1 = (const unsigned char *)str1; | ||
22 | const unsigned char *s2 = (const unsigned char *)str2; | ||
23 | int delta = 0; | ||
24 | |||
25 | while (*s1 || *s2) { | ||
26 | delta = *s2 - *s1; | ||
27 | if (delta) | ||
28 | return delta; | ||
29 | s1++; | ||
30 | s2++; | ||
31 | } | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | size_t strnlen(const char *s, size_t maxlen) | ||
36 | { | ||
37 | const char *es = s; | ||
38 | while (*es && maxlen) { | ||
39 | es++; | ||
40 | maxlen--; | ||
41 | } | ||
42 | |||
43 | return (es - s); | ||
44 | } | ||
45 | |||
46 | unsigned int atou(const char *s) | ||
47 | { | ||
48 | unsigned int i = 0; | ||
49 | while (isdigit(*s)) | ||
50 | i = i * 10 + (*s++ - '0'); | ||
51 | return i; | ||
52 | } | ||
diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c new file mode 100644 index 000000000000..f3f14bd26371 --- /dev/null +++ b/arch/x86/boot/tty.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/tty.c | ||
13 | * | ||
14 | * Very simple screen I/O | ||
15 | * XXX: Probably should add very simple serial I/O? | ||
16 | */ | ||
17 | |||
18 | #include "boot.h" | ||
19 | |||
20 | /* | ||
21 | * These functions are in .inittext so they can be used to signal | ||
22 | * error during initialization. | ||
23 | */ | ||
24 | |||
25 | void __attribute__((section(".inittext"))) putchar(int ch) | ||
26 | { | ||
27 | unsigned char c = ch; | ||
28 | |||
29 | if (c == '\n') | ||
30 | putchar('\r'); /* \n -> \r\n */ | ||
31 | |||
32 | /* int $0x10 is known to have bugs involving touching registers | ||
33 | it shouldn't. Be extra conservative... */ | ||
34 | asm volatile("pushal; pushw %%ds; int $0x10; popw %%ds; popal" | ||
35 | : : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch)); | ||
36 | } | ||
37 | |||
38 | void __attribute__((section(".inittext"))) puts(const char *str) | ||
39 | { | ||
40 | int n = 0; | ||
41 | while (*str) { | ||
42 | putchar(*str++); | ||
43 | n++; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Read the CMOS clock through the BIOS, and return the | ||
49 | * seconds in BCD. | ||
50 | */ | ||
51 | |||
52 | static u8 gettime(void) | ||
53 | { | ||
54 | u16 ax = 0x0200; | ||
55 | u16 cx, dx; | ||
56 | |||
57 | asm volatile("int $0x1a" | ||
58 | : "+a" (ax), "=c" (cx), "=d" (dx) | ||
59 | : : "ebx", "esi", "edi"); | ||
60 | |||
61 | return dx >> 8; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Read from the keyboard | ||
66 | */ | ||
67 | int getchar(void) | ||
68 | { | ||
69 | u16 ax = 0; | ||
70 | asm volatile("int $0x16" : "+a" (ax)); | ||
71 | |||
72 | return ax & 0xff; | ||
73 | } | ||
74 | |||
75 | static int kbd_pending(void) | ||
76 | { | ||
77 | u8 pending; | ||
78 | asm volatile("int $0x16; setnz %0" | ||
79 | : "=rm" (pending) | ||
80 | : "a" (0x0100)); | ||
81 | return pending; | ||
82 | } | ||
83 | |||
84 | void kbd_flush(void) | ||
85 | { | ||
86 | for (;;) { | ||
87 | if (!kbd_pending()) | ||
88 | break; | ||
89 | getchar(); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | int getchar_timeout(void) | ||
94 | { | ||
95 | int cnt = 30; | ||
96 | int t0, t1; | ||
97 | |||
98 | t0 = gettime(); | ||
99 | |||
100 | while (cnt) { | ||
101 | if (kbd_pending()) | ||
102 | return getchar(); | ||
103 | |||
104 | t1 = gettime(); | ||
105 | if (t0 != t1) { | ||
106 | cnt--; | ||
107 | t0 = t1; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return 0; /* Timeout! */ | ||
112 | } | ||
diff --git a/arch/x86/boot/version.c b/arch/x86/boot/version.c new file mode 100644 index 000000000000..c61462f7d9a7 --- /dev/null +++ b/arch/x86/boot/version.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/version.c | ||
13 | * | ||
14 | * Kernel version string | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include <linux/utsrelease.h> | ||
19 | #include <linux/compile.h> | ||
20 | |||
21 | const char kernel_version[] = | ||
22 | UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") " | ||
23 | UTS_VERSION; | ||
diff --git a/arch/x86/boot/vesa.h b/arch/x86/boot/vesa.h new file mode 100644 index 000000000000..ff5b73cd406f --- /dev/null +++ b/arch/x86/boot/vesa.h | |||
@@ -0,0 +1,79 @@ | |||
1 | /* ----------------------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright 1999-2007 H. Peter Anvin - All Rights Reserved | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, Inc., 53 Temple Place Ste 330, | ||
8 | * Boston MA 02111-1307, USA; either version 2 of the License, or | ||
9 | * (at your option) any later version; incorporated herein by reference. | ||
10 | * | ||
11 | * ----------------------------------------------------------------------- */ | ||
12 | |||
13 | #ifndef BOOT_VESA_H | ||
14 | #define BOOT_VESA_H | ||
15 | |||
16 | typedef struct { | ||
17 | u16 off, seg; | ||
18 | } far_ptr; | ||
19 | |||
20 | /* VESA General Information table */ | ||
21 | struct vesa_general_info { | ||
22 | u32 signature; /* 0 Magic number = "VESA" */ | ||
23 | u16 version; /* 4 */ | ||
24 | far_ptr vendor_string; /* 6 */ | ||
25 | u32 capabilities; /* 10 */ | ||
26 | far_ptr video_mode_ptr; /* 14 */ | ||
27 | u16 total_memory; /* 18 */ | ||
28 | |||
29 | u16 oem_software_rev; /* 20 */ | ||
30 | far_ptr oem_vendor_name_ptr; /* 22 */ | ||
31 | far_ptr oem_product_name_ptr; /* 26 */ | ||
32 | far_ptr oem_product_rev_ptr; /* 30 */ | ||
33 | |||
34 | u8 reserved[222]; /* 34 */ | ||
35 | u8 oem_data[256]; /* 256 */ | ||
36 | } __attribute__ ((packed)); | ||
37 | |||
38 | #define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24)) | ||
39 | #define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24)) | ||
40 | |||
41 | struct vesa_mode_info { | ||
42 | u16 mode_attr; /* 0 */ | ||
43 | u8 win_attr[2]; /* 2 */ | ||
44 | u16 win_grain; /* 4 */ | ||
45 | u16 win_size; /* 6 */ | ||
46 | u16 win_seg[2]; /* 8 */ | ||
47 | far_ptr win_scheme; /* 12 */ | ||
48 | u16 logical_scan; /* 16 */ | ||
49 | |||
50 | u16 h_res; /* 18 */ | ||
51 | u16 v_res; /* 20 */ | ||
52 | u8 char_width; /* 22 */ | ||
53 | u8 char_height; /* 23 */ | ||
54 | u8 memory_planes; /* 24 */ | ||
55 | u8 bpp; /* 25 */ | ||
56 | u8 banks; /* 26 */ | ||
57 | u8 memory_layout; /* 27 */ | ||
58 | u8 bank_size; /* 28 */ | ||
59 | u8 image_planes; /* 29 */ | ||
60 | u8 page_function; /* 30 */ | ||
61 | |||
62 | u8 rmask; /* 31 */ | ||
63 | u8 rpos; /* 32 */ | ||
64 | u8 gmask; /* 33 */ | ||
65 | u8 gpos; /* 34 */ | ||
66 | u8 bmask; /* 35 */ | ||
67 | u8 bpos; /* 36 */ | ||
68 | u8 resv_mask; /* 37 */ | ||
69 | u8 resv_pos; /* 38 */ | ||
70 | u8 dcm_info; /* 39 */ | ||
71 | |||
72 | u32 lfb_ptr; /* 40 Linear frame buffer address */ | ||
73 | u32 offscreen_ptr; /* 44 Offscreen memory address */ | ||
74 | u16 offscreen_size; /* 48 */ | ||
75 | |||
76 | u8 reserved[206]; /* 50 */ | ||
77 | } __attribute__ ((packed)); | ||
78 | |||
79 | #endif /* LIB_SYS_VESA_H */ | ||
diff --git a/arch/x86/boot/video-bios.c b/arch/x86/boot/video-bios.c new file mode 100644 index 000000000000..68e65d95cdfd --- /dev/null +++ b/arch/x86/boot/video-bios.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video-bios.c | ||
13 | * | ||
14 | * Standard video BIOS modes | ||
15 | * | ||
16 | * We have two options for this; silent and scanned. | ||
17 | */ | ||
18 | |||
19 | #include "boot.h" | ||
20 | #include "video.h" | ||
21 | |||
22 | __videocard video_bios; | ||
23 | |||
24 | /* Set a conventional BIOS mode */ | ||
25 | static int set_bios_mode(u8 mode); | ||
26 | |||
27 | static int bios_set_mode(struct mode_info *mi) | ||
28 | { | ||
29 | return set_bios_mode(mi->mode - VIDEO_FIRST_BIOS); | ||
30 | } | ||
31 | |||
32 | static int set_bios_mode(u8 mode) | ||
33 | { | ||
34 | u16 ax; | ||
35 | u8 new_mode; | ||
36 | |||
37 | ax = mode; /* AH=0x00 Set Video Mode */ | ||
38 | asm volatile(INT10 | ||
39 | : "+a" (ax) | ||
40 | : : "ebx", "ecx", "edx", "esi", "edi"); | ||
41 | |||
42 | ax = 0x0f00; /* Get Current Video Mode */ | ||
43 | asm volatile(INT10 | ||
44 | : "+a" (ax) | ||
45 | : : "ebx", "ecx", "edx", "esi", "edi"); | ||
46 | |||
47 | do_restore = 1; /* Assume video contents were lost */ | ||
48 | new_mode = ax & 0x7f; /* Not all BIOSes are clean with the top bit */ | ||
49 | |||
50 | if (new_mode == mode) | ||
51 | return 0; /* Mode change OK */ | ||
52 | |||
53 | if (new_mode != boot_params.screen_info.orig_video_mode) { | ||
54 | /* Mode setting failed, but we didn't end up where we | ||
55 | started. That's bad. Try to revert to the original | ||
56 | video mode. */ | ||
57 | ax = boot_params.screen_info.orig_video_mode; | ||
58 | asm volatile(INT10 | ||
59 | : "+a" (ax) | ||
60 | : : "ebx", "ecx", "edx", "esi", "edi"); | ||
61 | } | ||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | static int bios_probe(void) | ||
66 | { | ||
67 | u8 mode; | ||
68 | u8 saved_mode = boot_params.screen_info.orig_video_mode; | ||
69 | u16 crtc; | ||
70 | struct mode_info *mi; | ||
71 | int nmodes = 0; | ||
72 | |||
73 | if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA) | ||
74 | return 0; | ||
75 | |||
76 | set_fs(0); | ||
77 | crtc = vga_crtc(); | ||
78 | |||
79 | video_bios.modes = GET_HEAP(struct mode_info, 0); | ||
80 | |||
81 | for (mode = 0x14; mode <= 0x7f; mode++) { | ||
82 | if (heap_free() < sizeof(struct mode_info)) | ||
83 | break; | ||
84 | |||
85 | if (mode_defined(VIDEO_FIRST_BIOS+mode)) | ||
86 | continue; | ||
87 | |||
88 | if (set_bios_mode(mode)) | ||
89 | continue; | ||
90 | |||
91 | /* Try to verify that it's a text mode. */ | ||
92 | |||
93 | /* Attribute Controller: make graphics controller disabled */ | ||
94 | if (in_idx(0x3c0, 0x10) & 0x01) | ||
95 | continue; | ||
96 | |||
97 | /* Graphics Controller: verify Alpha addressing enabled */ | ||
98 | if (in_idx(0x3ce, 0x06) & 0x01) | ||
99 | continue; | ||
100 | |||
101 | /* CRTC cursor location low should be zero(?) */ | ||
102 | if (in_idx(crtc, 0x0f)) | ||
103 | continue; | ||
104 | |||
105 | mi = GET_HEAP(struct mode_info, 1); | ||
106 | mi->mode = VIDEO_FIRST_BIOS+mode; | ||
107 | mi->x = rdfs16(0x44a); | ||
108 | mi->y = rdfs8(0x484)+1; | ||
109 | nmodes++; | ||
110 | } | ||
111 | |||
112 | set_bios_mode(saved_mode); | ||
113 | |||
114 | return nmodes; | ||
115 | } | ||
116 | |||
117 | __videocard video_bios = | ||
118 | { | ||
119 | .card_name = "BIOS (scanned)", | ||
120 | .probe = bios_probe, | ||
121 | .set_mode = bios_set_mode, | ||
122 | .unsafe = 1, | ||
123 | .xmode_first = VIDEO_FIRST_BIOS, | ||
124 | .xmode_n = 0x80, | ||
125 | }; | ||
diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c new file mode 100644 index 000000000000..192190710710 --- /dev/null +++ b/arch/x86/boot/video-vesa.c | |||
@@ -0,0 +1,292 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video-vesa.c | ||
13 | * | ||
14 | * VESA text modes | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include "video.h" | ||
19 | #include "vesa.h" | ||
20 | |||
21 | /* VESA information */ | ||
22 | static struct vesa_general_info vginfo; | ||
23 | static struct vesa_mode_info vminfo; | ||
24 | |||
25 | __videocard video_vesa; | ||
26 | |||
27 | static void vesa_store_mode_params_graphics(void); | ||
28 | |||
29 | static int vesa_probe(void) | ||
30 | { | ||
31 | #if defined(CONFIG_VIDEO_VESA) || defined(CONFIG_FIRMWARE_EDID) | ||
32 | u16 ax, cx, di; | ||
33 | u16 mode; | ||
34 | addr_t mode_ptr; | ||
35 | struct mode_info *mi; | ||
36 | int nmodes = 0; | ||
37 | |||
38 | video_vesa.modes = GET_HEAP(struct mode_info, 0); | ||
39 | |||
40 | vginfo.signature = VBE2_MAGIC; | ||
41 | |||
42 | ax = 0x4f00; | ||
43 | di = (size_t)&vginfo; | ||
44 | asm(INT10 | ||
45 | : "+a" (ax), "+D" (di), "=m" (vginfo) | ||
46 | : : "ebx", "ecx", "edx", "esi"); | ||
47 | |||
48 | if (ax != 0x004f || | ||
49 | vginfo.signature != VESA_MAGIC || | ||
50 | vginfo.version < 0x0102) | ||
51 | return 0; /* Not present */ | ||
52 | #endif /* CONFIG_VIDEO_VESA || CONFIG_FIRMWARE_EDID */ | ||
53 | #ifdef CONFIG_VIDEO_VESA | ||
54 | set_fs(vginfo.video_mode_ptr.seg); | ||
55 | mode_ptr = vginfo.video_mode_ptr.off; | ||
56 | |||
57 | while ((mode = rdfs16(mode_ptr)) != 0xffff) { | ||
58 | mode_ptr += 2; | ||
59 | |||
60 | if (heap_free() < sizeof(struct mode_info)) | ||
61 | break; /* Heap full, can't save mode info */ | ||
62 | |||
63 | if (mode & ~0x1ff) | ||
64 | continue; | ||
65 | |||
66 | memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ | ||
67 | |||
68 | ax = 0x4f01; | ||
69 | cx = mode; | ||
70 | di = (size_t)&vminfo; | ||
71 | asm(INT10 | ||
72 | : "+a" (ax), "+c" (cx), "+D" (di), "=m" (vminfo) | ||
73 | : : "ebx", "edx", "esi"); | ||
74 | |||
75 | if (ax != 0x004f) | ||
76 | continue; | ||
77 | |||
78 | if ((vminfo.mode_attr & 0x15) == 0x05) { | ||
79 | /* Text Mode, TTY BIOS supported, | ||
80 | supported by hardware */ | ||
81 | mi = GET_HEAP(struct mode_info, 1); | ||
82 | mi->mode = mode + VIDEO_FIRST_VESA; | ||
83 | mi->x = vminfo.h_res; | ||
84 | mi->y = vminfo.v_res; | ||
85 | nmodes++; | ||
86 | } else if ((vminfo.mode_attr & 0x99) == 0x99) { | ||
87 | #ifdef CONFIG_FB | ||
88 | /* Graphics mode, color, linear frame buffer | ||
89 | supported -- register the mode but hide from | ||
90 | the menu. Only do this if framebuffer is | ||
91 | configured, however, otherwise the user will | ||
92 | be left without a screen. */ | ||
93 | mi = GET_HEAP(struct mode_info, 1); | ||
94 | mi->mode = mode + VIDEO_FIRST_VESA; | ||
95 | mi->x = mi->y = 0; | ||
96 | nmodes++; | ||
97 | #endif | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return nmodes; | ||
102 | #else | ||
103 | return 0; | ||
104 | #endif /* CONFIG_VIDEO_VESA */ | ||
105 | } | ||
106 | |||
107 | static int vesa_set_mode(struct mode_info *mode) | ||
108 | { | ||
109 | u16 ax, bx, cx, di; | ||
110 | int is_graphic; | ||
111 | u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA; | ||
112 | |||
113 | memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ | ||
114 | |||
115 | ax = 0x4f01; | ||
116 | cx = vesa_mode; | ||
117 | di = (size_t)&vminfo; | ||
118 | asm(INT10 | ||
119 | : "+a" (ax), "+c" (cx), "+D" (di), "=m" (vminfo) | ||
120 | : : "ebx", "edx", "esi"); | ||
121 | |||
122 | if (ax != 0x004f) | ||
123 | return -1; | ||
124 | |||
125 | if ((vminfo.mode_attr & 0x15) == 0x05) { | ||
126 | /* It's a supported text mode */ | ||
127 | is_graphic = 0; | ||
128 | } else if ((vminfo.mode_attr & 0x99) == 0x99) { | ||
129 | /* It's a graphics mode with linear frame buffer */ | ||
130 | is_graphic = 1; | ||
131 | vesa_mode |= 0x4000; /* Request linear frame buffer */ | ||
132 | } else { | ||
133 | return -1; /* Invalid mode */ | ||
134 | } | ||
135 | |||
136 | |||
137 | ax = 0x4f02; | ||
138 | bx = vesa_mode; | ||
139 | di = 0; | ||
140 | asm volatile(INT10 | ||
141 | : "+a" (ax), "+b" (bx), "+D" (di) | ||
142 | : : "ecx", "edx", "esi"); | ||
143 | |||
144 | if (ax != 0x004f) | ||
145 | return -1; | ||
146 | |||
147 | graphic_mode = is_graphic; | ||
148 | if (!is_graphic) { | ||
149 | /* Text mode */ | ||
150 | force_x = mode->x; | ||
151 | force_y = mode->y; | ||
152 | do_restore = 1; | ||
153 | } else { | ||
154 | /* Graphics mode */ | ||
155 | vesa_store_mode_params_graphics(); | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | |||
162 | /* Switch DAC to 8-bit mode */ | ||
163 | static void vesa_dac_set_8bits(void) | ||
164 | { | ||
165 | u8 dac_size = 6; | ||
166 | |||
167 | /* If possible, switch the DAC to 8-bit mode */ | ||
168 | if (vginfo.capabilities & 1) { | ||
169 | u16 ax, bx; | ||
170 | |||
171 | ax = 0x4f08; | ||
172 | bx = 0x0800; | ||
173 | asm volatile(INT10 | ||
174 | : "+a" (ax), "+b" (bx) | ||
175 | : : "ecx", "edx", "esi", "edi"); | ||
176 | |||
177 | if (ax == 0x004f) | ||
178 | dac_size = bx >> 8; | ||
179 | } | ||
180 | |||
181 | /* Set the color sizes to the DAC size, and offsets to 0 */ | ||
182 | boot_params.screen_info.red_size = dac_size; | ||
183 | boot_params.screen_info.green_size = dac_size; | ||
184 | boot_params.screen_info.blue_size = dac_size; | ||
185 | boot_params.screen_info.rsvd_size = dac_size; | ||
186 | |||
187 | boot_params.screen_info.red_pos = 0; | ||
188 | boot_params.screen_info.green_pos = 0; | ||
189 | boot_params.screen_info.blue_pos = 0; | ||
190 | boot_params.screen_info.rsvd_pos = 0; | ||
191 | } | ||
192 | |||
193 | /* Save the VESA protected mode info */ | ||
194 | static void vesa_store_pm_info(void) | ||
195 | { | ||
196 | u16 ax, bx, di, es; | ||
197 | |||
198 | ax = 0x4f0a; | ||
199 | bx = di = 0; | ||
200 | asm("pushw %%es; "INT10"; movw %%es,%0; popw %%es" | ||
201 | : "=d" (es), "+a" (ax), "+b" (bx), "+D" (di) | ||
202 | : : "ecx", "esi"); | ||
203 | |||
204 | if (ax != 0x004f) | ||
205 | return; | ||
206 | |||
207 | boot_params.screen_info.vesapm_seg = es; | ||
208 | boot_params.screen_info.vesapm_off = di; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Save video mode parameters for graphics mode | ||
213 | */ | ||
214 | static void vesa_store_mode_params_graphics(void) | ||
215 | { | ||
216 | /* Tell the kernel we're in VESA graphics mode */ | ||
217 | boot_params.screen_info.orig_video_isVGA = 0x23; | ||
218 | |||
219 | /* Mode parameters */ | ||
220 | boot_params.screen_info.vesa_attributes = vminfo.mode_attr; | ||
221 | boot_params.screen_info.lfb_linelength = vminfo.logical_scan; | ||
222 | boot_params.screen_info.lfb_width = vminfo.h_res; | ||
223 | boot_params.screen_info.lfb_height = vminfo.v_res; | ||
224 | boot_params.screen_info.lfb_depth = vminfo.bpp; | ||
225 | boot_params.screen_info.pages = vminfo.image_planes; | ||
226 | boot_params.screen_info.lfb_base = vminfo.lfb_ptr; | ||
227 | memcpy(&boot_params.screen_info.red_size, | ||
228 | &vminfo.rmask, 8); | ||
229 | |||
230 | /* General parameters */ | ||
231 | boot_params.screen_info.lfb_size = vginfo.total_memory; | ||
232 | |||
233 | if (vminfo.bpp <= 8) | ||
234 | vesa_dac_set_8bits(); | ||
235 | |||
236 | vesa_store_pm_info(); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Save EDID information for the kernel; this is invoked, separately, | ||
241 | * after mode-setting. | ||
242 | */ | ||
243 | void vesa_store_edid(void) | ||
244 | { | ||
245 | #ifdef CONFIG_FIRMWARE_EDID | ||
246 | u16 ax, bx, cx, dx, di; | ||
247 | |||
248 | /* Apparently used as a nonsense token... */ | ||
249 | memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info); | ||
250 | |||
251 | if (vginfo.version < 0x0200) | ||
252 | return; /* EDID requires VBE 2.0+ */ | ||
253 | |||
254 | ax = 0x4f15; /* VBE DDC */ | ||
255 | bx = 0x0000; /* Report DDC capabilities */ | ||
256 | cx = 0; /* Controller 0 */ | ||
257 | di = 0; /* ES:DI must be 0 by spec */ | ||
258 | |||
259 | /* Note: The VBE DDC spec is different from the main VESA spec; | ||
260 | we genuinely have to assume all registers are destroyed here. */ | ||
261 | |||
262 | asm("pushw %%es; movw %2,%%es; "INT10"; popw %%es" | ||
263 | : "+a" (ax), "+b" (bx) | ||
264 | : "c" (cx), "D" (di) | ||
265 | : "esi"); | ||
266 | |||
267 | if (ax != 0x004f) | ||
268 | return; /* No EDID */ | ||
269 | |||
270 | /* BH = time in seconds to transfer EDD information */ | ||
271 | /* BL = DDC level supported */ | ||
272 | |||
273 | ax = 0x4f15; /* VBE DDC */ | ||
274 | bx = 0x0001; /* Read EDID */ | ||
275 | cx = 0; /* Controller 0 */ | ||
276 | dx = 0; /* EDID block number */ | ||
277 | di =(size_t) &boot_params.edid_info; /* (ES:)Pointer to block */ | ||
278 | asm(INT10 | ||
279 | : "+a" (ax), "+b" (bx), "+d" (dx), "=m" (boot_params.edid_info) | ||
280 | : "c" (cx), "D" (di) | ||
281 | : "esi"); | ||
282 | #endif /* CONFIG_FIRMWARE_EDID */ | ||
283 | } | ||
284 | |||
285 | __videocard video_vesa = | ||
286 | { | ||
287 | .card_name = "VESA", | ||
288 | .probe = vesa_probe, | ||
289 | .set_mode = vesa_set_mode, | ||
290 | .xmode_first = VIDEO_FIRST_VESA, | ||
291 | .xmode_n = 0x200, | ||
292 | }; | ||
diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c new file mode 100644 index 000000000000..aef02f9ec0c1 --- /dev/null +++ b/arch/x86/boot/video-vga.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video-vga.c | ||
13 | * | ||
14 | * Common all-VGA modes | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include "video.h" | ||
19 | |||
20 | static struct mode_info vga_modes[] = { | ||
21 | { VIDEO_80x25, 80, 25 }, | ||
22 | { VIDEO_8POINT, 80, 50 }, | ||
23 | { VIDEO_80x43, 80, 43 }, | ||
24 | { VIDEO_80x28, 80, 28 }, | ||
25 | { VIDEO_80x30, 80, 30 }, | ||
26 | { VIDEO_80x34, 80, 34 }, | ||
27 | { VIDEO_80x60, 80, 60 }, | ||
28 | }; | ||
29 | |||
30 | static struct mode_info ega_modes[] = { | ||
31 | { VIDEO_80x25, 80, 25 }, | ||
32 | { VIDEO_8POINT, 80, 43 }, | ||
33 | }; | ||
34 | |||
35 | static struct mode_info cga_modes[] = { | ||
36 | { VIDEO_80x25, 80, 25 }, | ||
37 | }; | ||
38 | |||
39 | __videocard video_vga; | ||
40 | |||
41 | /* Set basic 80x25 mode */ | ||
42 | static u8 vga_set_basic_mode(void) | ||
43 | { | ||
44 | u16 ax; | ||
45 | u8 rows; | ||
46 | u8 mode; | ||
47 | |||
48 | #ifdef CONFIG_VIDEO_400_HACK | ||
49 | if (adapter >= ADAPTER_VGA) { | ||
50 | asm volatile(INT10 | ||
51 | : : "a" (0x1202), "b" (0x0030) | ||
52 | : "ecx", "edx", "esi", "edi"); | ||
53 | } | ||
54 | #endif | ||
55 | |||
56 | ax = 0x0f00; | ||
57 | asm volatile(INT10 | ||
58 | : "+a" (ax) | ||
59 | : : "ebx", "ecx", "edx", "esi", "edi"); | ||
60 | |||
61 | mode = (u8)ax; | ||
62 | |||
63 | set_fs(0); | ||
64 | rows = rdfs8(0x484); /* rows minus one */ | ||
65 | |||
66 | #ifndef CONFIG_VIDEO_400_HACK | ||
67 | if ((ax == 0x5003 || ax == 0x5007) && | ||
68 | (rows == 0 || rows == 24)) | ||
69 | return mode; | ||
70 | #endif | ||
71 | |||
72 | if (mode != 3 && mode != 7) | ||
73 | mode = 3; | ||
74 | |||
75 | /* Set the mode */ | ||
76 | ax = mode; | ||
77 | asm volatile(INT10 | ||
78 | : "+a" (ax) | ||
79 | : : "ebx", "ecx", "edx", "esi", "edi"); | ||
80 | do_restore = 1; | ||
81 | return mode; | ||
82 | } | ||
83 | |||
84 | static void vga_set_8font(void) | ||
85 | { | ||
86 | /* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */ | ||
87 | |||
88 | /* Set 8x8 font */ | ||
89 | asm volatile(INT10 : : "a" (0x1112), "b" (0)); | ||
90 | |||
91 | /* Use alternate print screen */ | ||
92 | asm volatile(INT10 : : "a" (0x1200), "b" (0x20)); | ||
93 | |||
94 | /* Turn off cursor emulation */ | ||
95 | asm volatile(INT10 : : "a" (0x1201), "b" (0x34)); | ||
96 | |||
97 | /* Cursor is scan lines 6-7 */ | ||
98 | asm volatile(INT10 : : "a" (0x0100), "c" (0x0607)); | ||
99 | } | ||
100 | |||
101 | static void vga_set_14font(void) | ||
102 | { | ||
103 | /* Set 9x14 font - 80x28 on VGA */ | ||
104 | |||
105 | /* Set 9x14 font */ | ||
106 | asm volatile(INT10 : : "a" (0x1111), "b" (0)); | ||
107 | |||
108 | /* Turn off cursor emulation */ | ||
109 | asm volatile(INT10 : : "a" (0x1201), "b" (0x34)); | ||
110 | |||
111 | /* Cursor is scan lines 11-12 */ | ||
112 | asm volatile(INT10 : : "a" (0x0100), "c" (0x0b0c)); | ||
113 | } | ||
114 | |||
115 | static void vga_set_80x43(void) | ||
116 | { | ||
117 | /* Set 80x43 mode on VGA (not EGA) */ | ||
118 | |||
119 | /* Set 350 scans */ | ||
120 | asm volatile(INT10 : : "a" (0x1201), "b" (0x30)); | ||
121 | |||
122 | /* Reset video mode */ | ||
123 | asm volatile(INT10 : : "a" (0x0003)); | ||
124 | |||
125 | vga_set_8font(); | ||
126 | } | ||
127 | |||
128 | /* I/O address of the VGA CRTC */ | ||
129 | u16 vga_crtc(void) | ||
130 | { | ||
131 | return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4; | ||
132 | } | ||
133 | |||
134 | static void vga_set_480_scanlines(int end) | ||
135 | { | ||
136 | u16 crtc; | ||
137 | u8 csel; | ||
138 | |||
139 | crtc = vga_crtc(); | ||
140 | |||
141 | out_idx(0x0c, crtc, 0x11); /* Vertical sync end, unlock CR0-7 */ | ||
142 | out_idx(0x0b, crtc, 0x06); /* Vertical total */ | ||
143 | out_idx(0x3e, crtc, 0x07); /* Vertical overflow */ | ||
144 | out_idx(0xea, crtc, 0x10); /* Vertical sync start */ | ||
145 | out_idx(end, crtc, 0x12); /* Vertical display end */ | ||
146 | out_idx(0xe7, crtc, 0x15); /* Vertical blank start */ | ||
147 | out_idx(0x04, crtc, 0x16); /* Vertical blank end */ | ||
148 | csel = inb(0x3cc); | ||
149 | csel &= 0x0d; | ||
150 | csel |= 0xe2; | ||
151 | outb(csel, 0x3cc); | ||
152 | } | ||
153 | |||
154 | static void vga_set_80x30(void) | ||
155 | { | ||
156 | vga_set_480_scanlines(0xdf); | ||
157 | } | ||
158 | |||
159 | static void vga_set_80x34(void) | ||
160 | { | ||
161 | vga_set_14font(); | ||
162 | vga_set_480_scanlines(0xdb); | ||
163 | } | ||
164 | |||
165 | static void vga_set_80x60(void) | ||
166 | { | ||
167 | vga_set_8font(); | ||
168 | vga_set_480_scanlines(0xdf); | ||
169 | } | ||
170 | |||
171 | static int vga_set_mode(struct mode_info *mode) | ||
172 | { | ||
173 | /* Set the basic mode */ | ||
174 | vga_set_basic_mode(); | ||
175 | |||
176 | /* Override a possibly broken BIOS */ | ||
177 | force_x = mode->x; | ||
178 | force_y = mode->y; | ||
179 | |||
180 | switch (mode->mode) { | ||
181 | case VIDEO_80x25: | ||
182 | break; | ||
183 | case VIDEO_8POINT: | ||
184 | vga_set_8font(); | ||
185 | break; | ||
186 | case VIDEO_80x43: | ||
187 | vga_set_80x43(); | ||
188 | break; | ||
189 | case VIDEO_80x28: | ||
190 | vga_set_14font(); | ||
191 | break; | ||
192 | case VIDEO_80x30: | ||
193 | vga_set_80x30(); | ||
194 | break; | ||
195 | case VIDEO_80x34: | ||
196 | vga_set_80x34(); | ||
197 | break; | ||
198 | case VIDEO_80x60: | ||
199 | vga_set_80x60(); | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Note: this probe includes basic information required by all | ||
208 | * systems. It should be executed first, by making sure | ||
209 | * video-vga.c is listed first in the Makefile. | ||
210 | */ | ||
211 | static int vga_probe(void) | ||
212 | { | ||
213 | static const char *card_name[] = { | ||
214 | "CGA/MDA/HGC", "EGA", "VGA" | ||
215 | }; | ||
216 | static struct mode_info *mode_lists[] = { | ||
217 | cga_modes, | ||
218 | ega_modes, | ||
219 | vga_modes, | ||
220 | }; | ||
221 | static int mode_count[] = { | ||
222 | sizeof(cga_modes)/sizeof(struct mode_info), | ||
223 | sizeof(ega_modes)/sizeof(struct mode_info), | ||
224 | sizeof(vga_modes)/sizeof(struct mode_info), | ||
225 | }; | ||
226 | u8 vga_flag; | ||
227 | |||
228 | asm(INT10 | ||
229 | : "=b" (boot_params.screen_info.orig_video_ega_bx) | ||
230 | : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */ | ||
231 | : "ecx", "edx", "esi", "edi"); | ||
232 | |||
233 | /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */ | ||
234 | if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) { | ||
235 | /* EGA/VGA */ | ||
236 | asm(INT10 | ||
237 | : "=a" (vga_flag) | ||
238 | : "a" (0x1a00) | ||
239 | : "ebx", "ecx", "edx", "esi", "edi"); | ||
240 | |||
241 | if (vga_flag == 0x1a) { | ||
242 | adapter = ADAPTER_VGA; | ||
243 | boot_params.screen_info.orig_video_isVGA = 1; | ||
244 | } else { | ||
245 | adapter = ADAPTER_EGA; | ||
246 | } | ||
247 | } else { | ||
248 | adapter = ADAPTER_CGA; | ||
249 | } | ||
250 | |||
251 | video_vga.modes = mode_lists[adapter]; | ||
252 | video_vga.card_name = card_name[adapter]; | ||
253 | return mode_count[adapter]; | ||
254 | } | ||
255 | |||
256 | __videocard video_vga = | ||
257 | { | ||
258 | .card_name = "VGA", | ||
259 | .probe = vga_probe, | ||
260 | .set_mode = vga_set_mode, | ||
261 | }; | ||
diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c new file mode 100644 index 000000000000..e4ba897bf9a3 --- /dev/null +++ b/arch/x86/boot/video.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video.c | ||
13 | * | ||
14 | * Select video mode | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | #include "video.h" | ||
19 | #include "vesa.h" | ||
20 | |||
21 | /* | ||
22 | * Mode list variables | ||
23 | */ | ||
24 | static struct card_info cards[]; /* List of cards to probe for */ | ||
25 | |||
26 | /* | ||
27 | * Common variables | ||
28 | */ | ||
29 | int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ | ||
30 | u16 video_segment; | ||
31 | int force_x, force_y; /* Don't query the BIOS for cols/rows */ | ||
32 | |||
33 | int do_restore = 0; /* Screen contents changed during mode flip */ | ||
34 | int graphic_mode; /* Graphic mode with linear frame buffer */ | ||
35 | |||
36 | static void store_cursor_position(void) | ||
37 | { | ||
38 | u16 curpos; | ||
39 | u16 ax, bx; | ||
40 | |||
41 | ax = 0x0300; | ||
42 | bx = 0; | ||
43 | asm(INT10 | ||
44 | : "=d" (curpos), "+a" (ax), "+b" (bx) | ||
45 | : : "ecx", "esi", "edi"); | ||
46 | |||
47 | boot_params.screen_info.orig_x = curpos; | ||
48 | boot_params.screen_info.orig_y = curpos >> 8; | ||
49 | } | ||
50 | |||
51 | static void store_video_mode(void) | ||
52 | { | ||
53 | u16 ax, page; | ||
54 | |||
55 | /* N.B.: the saving of the video page here is a bit silly, | ||
56 | since we pretty much assume page 0 everywhere. */ | ||
57 | ax = 0x0f00; | ||
58 | asm(INT10 | ||
59 | : "+a" (ax), "=b" (page) | ||
60 | : : "ecx", "edx", "esi", "edi"); | ||
61 | |||
62 | /* Not all BIOSes are clean with respect to the top bit */ | ||
63 | boot_params.screen_info.orig_video_mode = ax & 0x7f; | ||
64 | boot_params.screen_info.orig_video_page = page >> 8; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Store the video mode parameters for later usage by the kernel. | ||
69 | * This is done by asking the BIOS except for the rows/columns | ||
70 | * parameters in the default 80x25 mode -- these are set directly, | ||
71 | * because some very obscure BIOSes supply insane values. | ||
72 | */ | ||
73 | static void store_mode_params(void) | ||
74 | { | ||
75 | u16 font_size; | ||
76 | int x, y; | ||
77 | |||
78 | /* For graphics mode, it is up to the mode-setting driver | ||
79 | (currently only video-vesa.c) to store the parameters */ | ||
80 | if (graphic_mode) | ||
81 | return; | ||
82 | |||
83 | store_cursor_position(); | ||
84 | store_video_mode(); | ||
85 | |||
86 | if (boot_params.screen_info.orig_video_mode == 0x07) { | ||
87 | /* MDA, HGC, or VGA in monochrome mode */ | ||
88 | video_segment = 0xb000; | ||
89 | } else { | ||
90 | /* CGA, EGA, VGA and so forth */ | ||
91 | video_segment = 0xb800; | ||
92 | } | ||
93 | |||
94 | set_fs(0); | ||
95 | font_size = rdfs16(0x485); /* Font size, BIOS area */ | ||
96 | boot_params.screen_info.orig_video_points = font_size; | ||
97 | |||
98 | x = rdfs16(0x44a); | ||
99 | y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; | ||
100 | |||
101 | if (force_x) | ||
102 | x = force_x; | ||
103 | if (force_y) | ||
104 | y = force_y; | ||
105 | |||
106 | boot_params.screen_info.orig_video_cols = x; | ||
107 | boot_params.screen_info.orig_video_lines = y; | ||
108 | } | ||
109 | |||
110 | /* Probe the video drivers and have them generate their mode lists. */ | ||
111 | static void probe_cards(int unsafe) | ||
112 | { | ||
113 | struct card_info *card; | ||
114 | static u8 probed[2]; | ||
115 | |||
116 | if (probed[unsafe]) | ||
117 | return; | ||
118 | |||
119 | probed[unsafe] = 1; | ||
120 | |||
121 | for (card = video_cards; card < video_cards_end; card++) { | ||
122 | if (card->unsafe == unsafe) { | ||
123 | if (card->probe) | ||
124 | card->nmodes = card->probe(); | ||
125 | else | ||
126 | card->nmodes = 0; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* Test if a mode is defined */ | ||
132 | int mode_defined(u16 mode) | ||
133 | { | ||
134 | struct card_info *card; | ||
135 | struct mode_info *mi; | ||
136 | int i; | ||
137 | |||
138 | for (card = video_cards; card < video_cards_end; card++) { | ||
139 | mi = card->modes; | ||
140 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
141 | if (mi->mode == mode) | ||
142 | return 1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* Set mode (without recalc) */ | ||
150 | static int raw_set_mode(u16 mode, u16 *real_mode) | ||
151 | { | ||
152 | int nmode, i; | ||
153 | struct card_info *card; | ||
154 | struct mode_info *mi; | ||
155 | |||
156 | /* Drop the recalc bit if set */ | ||
157 | mode &= ~VIDEO_RECALC; | ||
158 | |||
159 | /* Scan for mode based on fixed ID, position, or resolution */ | ||
160 | nmode = 0; | ||
161 | for (card = video_cards; card < video_cards_end; card++) { | ||
162 | mi = card->modes; | ||
163 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
164 | int visible = mi->x || mi->y; | ||
165 | |||
166 | if ((mode == nmode && visible) || | ||
167 | mode == mi->mode || | ||
168 | mode == (mi->y << 8)+mi->x) { | ||
169 | *real_mode = mi->mode; | ||
170 | return card->set_mode(mi); | ||
171 | } | ||
172 | |||
173 | if (visible) | ||
174 | nmode++; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /* Nothing found? Is it an "exceptional" (unprobed) mode? */ | ||
179 | for (card = video_cards; card < video_cards_end; card++) { | ||
180 | if (mode >= card->xmode_first && | ||
181 | mode < card->xmode_first+card->xmode_n) { | ||
182 | struct mode_info mix; | ||
183 | *real_mode = mix.mode = mode; | ||
184 | mix.x = mix.y = 0; | ||
185 | return card->set_mode(&mix); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | /* Otherwise, failure... */ | ||
190 | return -1; | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * Recalculate the vertical video cutoff (hack!) | ||
195 | */ | ||
196 | static void vga_recalc_vertical(void) | ||
197 | { | ||
198 | unsigned int font_size, rows; | ||
199 | u16 crtc; | ||
200 | u8 pt, ov; | ||
201 | |||
202 | set_fs(0); | ||
203 | font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ | ||
204 | rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ | ||
205 | |||
206 | rows *= font_size; /* Visible scan lines */ | ||
207 | rows--; /* ... minus one */ | ||
208 | |||
209 | crtc = vga_crtc(); | ||
210 | |||
211 | pt = in_idx(crtc, 0x11); | ||
212 | pt &= ~0x80; /* Unlock CR0-7 */ | ||
213 | out_idx(pt, crtc, 0x11); | ||
214 | |||
215 | out_idx((u8)rows, crtc, 0x12); /* Lower height register */ | ||
216 | |||
217 | ov = in_idx(crtc, 0x07); /* Overflow register */ | ||
218 | ov &= 0xbd; | ||
219 | ov |= (rows >> (8-1)) & 0x02; | ||
220 | ov |= (rows >> (9-6)) & 0x40; | ||
221 | out_idx(ov, crtc, 0x07); | ||
222 | } | ||
223 | |||
224 | /* Set mode (with recalc if specified) */ | ||
225 | static int set_mode(u16 mode) | ||
226 | { | ||
227 | int rv; | ||
228 | u16 real_mode; | ||
229 | |||
230 | /* Very special mode numbers... */ | ||
231 | if (mode == VIDEO_CURRENT_MODE) | ||
232 | return 0; /* Nothing to do... */ | ||
233 | else if (mode == NORMAL_VGA) | ||
234 | mode = VIDEO_80x25; | ||
235 | else if (mode == EXTENDED_VGA) | ||
236 | mode = VIDEO_8POINT; | ||
237 | |||
238 | rv = raw_set_mode(mode, &real_mode); | ||
239 | if (rv) | ||
240 | return rv; | ||
241 | |||
242 | if (mode & VIDEO_RECALC) | ||
243 | vga_recalc_vertical(); | ||
244 | |||
245 | /* Save the canonical mode number for the kernel, not | ||
246 | an alias, size specification or menu position */ | ||
247 | boot_params.hdr.vid_mode = real_mode; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static unsigned int get_entry(void) | ||
252 | { | ||
253 | char entry_buf[4]; | ||
254 | int i, len = 0; | ||
255 | int key; | ||
256 | unsigned int v; | ||
257 | |||
258 | do { | ||
259 | key = getchar(); | ||
260 | |||
261 | if (key == '\b') { | ||
262 | if (len > 0) { | ||
263 | puts("\b \b"); | ||
264 | len--; | ||
265 | } | ||
266 | } else if ((key >= '0' && key <= '9') || | ||
267 | (key >= 'A' && key <= 'Z') || | ||
268 | (key >= 'a' && key <= 'z')) { | ||
269 | if (len < sizeof entry_buf) { | ||
270 | entry_buf[len++] = key; | ||
271 | putchar(key); | ||
272 | } | ||
273 | } | ||
274 | } while (key != '\r'); | ||
275 | putchar('\n'); | ||
276 | |||
277 | if (len == 0) | ||
278 | return VIDEO_CURRENT_MODE; /* Default */ | ||
279 | |||
280 | v = 0; | ||
281 | for (i = 0; i < len; i++) { | ||
282 | v <<= 4; | ||
283 | key = entry_buf[i] | 0x20; | ||
284 | v += (key > '9') ? key-'a'+10 : key-'0'; | ||
285 | } | ||
286 | |||
287 | return v; | ||
288 | } | ||
289 | |||
290 | static void display_menu(void) | ||
291 | { | ||
292 | struct card_info *card; | ||
293 | struct mode_info *mi; | ||
294 | char ch; | ||
295 | int i; | ||
296 | |||
297 | puts("Mode: COLSxROWS:\n"); | ||
298 | |||
299 | ch = '0'; | ||
300 | for (card = video_cards; card < video_cards_end; card++) { | ||
301 | mi = card->modes; | ||
302 | for (i = 0; i < card->nmodes; i++, mi++) { | ||
303 | int visible = mi->x && mi->y; | ||
304 | u16 mode_id = mi->mode ? mi->mode : | ||
305 | (mi->y << 8)+mi->x; | ||
306 | |||
307 | if (!visible) | ||
308 | continue; /* Hidden mode */ | ||
309 | |||
310 | printf("%c %04X %3dx%-3d %s\n", | ||
311 | ch, mode_id, mi->x, mi->y, card->card_name); | ||
312 | |||
313 | if (ch == '9') | ||
314 | ch = 'a'; | ||
315 | else if (ch == 'z' || ch == ' ') | ||
316 | ch = ' '; /* Out of keys... */ | ||
317 | else | ||
318 | ch++; | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | #define H(x) ((x)-'a'+10) | ||
324 | #define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) | ||
325 | |||
326 | static unsigned int mode_menu(void) | ||
327 | { | ||
328 | int key; | ||
329 | unsigned int sel; | ||
330 | |||
331 | puts("Press <ENTER> to see video modes available, " | ||
332 | "<SPACE> to continue, or wait 30 sec\n"); | ||
333 | |||
334 | kbd_flush(); | ||
335 | while (1) { | ||
336 | key = getchar_timeout(); | ||
337 | if (key == ' ' || key == 0) | ||
338 | return VIDEO_CURRENT_MODE; /* Default */ | ||
339 | if (key == '\r') | ||
340 | break; | ||
341 | putchar('\a'); /* Beep! */ | ||
342 | } | ||
343 | |||
344 | |||
345 | for (;;) { | ||
346 | display_menu(); | ||
347 | |||
348 | puts("Enter a video mode or \"scan\" to scan for " | ||
349 | "additional modes: "); | ||
350 | sel = get_entry(); | ||
351 | if (sel != SCAN) | ||
352 | return sel; | ||
353 | |||
354 | probe_cards(1); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | #ifdef CONFIG_VIDEO_RETAIN | ||
359 | /* Save screen content to the heap */ | ||
360 | struct saved_screen { | ||
361 | int x, y; | ||
362 | int curx, cury; | ||
363 | u16 *data; | ||
364 | } saved; | ||
365 | |||
366 | static void save_screen(void) | ||
367 | { | ||
368 | /* Should be called after store_mode_params() */ | ||
369 | saved.x = boot_params.screen_info.orig_video_cols; | ||
370 | saved.y = boot_params.screen_info.orig_video_lines; | ||
371 | saved.curx = boot_params.screen_info.orig_x; | ||
372 | saved.cury = boot_params.screen_info.orig_y; | ||
373 | |||
374 | if (heap_free() < saved.x*saved.y*sizeof(u16)+512) | ||
375 | return; /* Not enough heap to save the screen */ | ||
376 | |||
377 | saved.data = GET_HEAP(u16, saved.x*saved.y); | ||
378 | |||
379 | set_fs(video_segment); | ||
380 | copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); | ||
381 | } | ||
382 | |||
383 | static void restore_screen(void) | ||
384 | { | ||
385 | /* Should be called after store_mode_params() */ | ||
386 | int xs = boot_params.screen_info.orig_video_cols; | ||
387 | int ys = boot_params.screen_info.orig_video_lines; | ||
388 | int y; | ||
389 | addr_t dst = 0; | ||
390 | u16 *src = saved.data; | ||
391 | u16 ax, bx, dx; | ||
392 | |||
393 | if (graphic_mode) | ||
394 | return; /* Can't restore onto a graphic mode */ | ||
395 | |||
396 | if (!src) | ||
397 | return; /* No saved screen contents */ | ||
398 | |||
399 | /* Restore screen contents */ | ||
400 | |||
401 | set_fs(video_segment); | ||
402 | for (y = 0; y < ys; y++) { | ||
403 | int npad; | ||
404 | |||
405 | if (y < saved.y) { | ||
406 | int copy = (xs < saved.x) ? xs : saved.x; | ||
407 | copy_to_fs(dst, src, copy*sizeof(u16)); | ||
408 | dst += copy*sizeof(u16); | ||
409 | src += saved.x; | ||
410 | npad = (xs < saved.x) ? 0 : xs-saved.x; | ||
411 | } else { | ||
412 | npad = xs; | ||
413 | } | ||
414 | |||
415 | /* Writes "npad" blank characters to | ||
416 | video_segment:dst and advances dst */ | ||
417 | asm volatile("pushw %%es ; " | ||
418 | "movw %2,%%es ; " | ||
419 | "shrw %%cx ; " | ||
420 | "jnc 1f ; " | ||
421 | "stosw \n\t" | ||
422 | "1: rep;stosl ; " | ||
423 | "popw %%es" | ||
424 | : "+D" (dst), "+c" (npad) | ||
425 | : "bdS" (video_segment), | ||
426 | "a" (0x07200720)); | ||
427 | } | ||
428 | |||
429 | /* Restore cursor position */ | ||
430 | ax = 0x0200; /* Set cursor position */ | ||
431 | bx = 0; /* Page number (<< 8) */ | ||
432 | dx = (saved.cury << 8)+saved.curx; | ||
433 | asm volatile(INT10 | ||
434 | : "+a" (ax), "+b" (bx), "+d" (dx) | ||
435 | : : "ecx", "esi", "edi"); | ||
436 | } | ||
437 | #else | ||
438 | #define save_screen() ((void)0) | ||
439 | #define restore_screen() ((void)0) | ||
440 | #endif | ||
441 | |||
442 | void set_video(void) | ||
443 | { | ||
444 | u16 mode = boot_params.hdr.vid_mode; | ||
445 | |||
446 | RESET_HEAP(); | ||
447 | |||
448 | store_mode_params(); | ||
449 | save_screen(); | ||
450 | probe_cards(0); | ||
451 | |||
452 | for (;;) { | ||
453 | if (mode == ASK_VGA) | ||
454 | mode = mode_menu(); | ||
455 | |||
456 | if (!set_mode(mode)) | ||
457 | break; | ||
458 | |||
459 | printf("Undefined video mode number: %x\n", mode); | ||
460 | mode = ASK_VGA; | ||
461 | } | ||
462 | vesa_store_edid(); | ||
463 | store_mode_params(); | ||
464 | |||
465 | if (do_restore) | ||
466 | restore_screen(); | ||
467 | } | ||
diff --git a/arch/x86/boot/video.h b/arch/x86/boot/video.h new file mode 100644 index 000000000000..b92447d51213 --- /dev/null +++ b/arch/x86/boot/video.h | |||
@@ -0,0 +1,152 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/video.h | ||
13 | * | ||
14 | * Header file for the real-mode video probing code | ||
15 | */ | ||
16 | |||
17 | #ifndef BOOT_VIDEO_H | ||
18 | #define BOOT_VIDEO_H | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | |||
22 | /* Enable autodetection of SVGA adapters and modes. */ | ||
23 | #undef CONFIG_VIDEO_SVGA | ||
24 | |||
25 | /* Enable autodetection of VESA modes */ | ||
26 | #define CONFIG_VIDEO_VESA | ||
27 | |||
28 | /* Retain screen contents when switching modes */ | ||
29 | #define CONFIG_VIDEO_RETAIN | ||
30 | |||
31 | /* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */ | ||
32 | #undef CONFIG_VIDEO_400_HACK | ||
33 | |||
34 | /* This code uses an extended set of video mode numbers. These include: | ||
35 | * Aliases for standard modes | ||
36 | * NORMAL_VGA (-1) | ||
37 | * EXTENDED_VGA (-2) | ||
38 | * ASK_VGA (-3) | ||
39 | * Video modes numbered by menu position -- NOT RECOMMENDED because of lack | ||
40 | * of compatibility when extending the table. These are between 0x00 and 0xff. | ||
41 | */ | ||
42 | #define VIDEO_FIRST_MENU 0x0000 | ||
43 | |||
44 | /* Standard BIOS video modes (BIOS number + 0x0100) */ | ||
45 | #define VIDEO_FIRST_BIOS 0x0100 | ||
46 | |||
47 | /* VESA BIOS video modes (VESA number + 0x0200) */ | ||
48 | #define VIDEO_FIRST_VESA 0x0200 | ||
49 | |||
50 | /* Video7 special modes (BIOS number + 0x0900) */ | ||
51 | #define VIDEO_FIRST_V7 0x0900 | ||
52 | |||
53 | /* Special video modes */ | ||
54 | #define VIDEO_FIRST_SPECIAL 0x0f00 | ||
55 | #define VIDEO_80x25 0x0f00 | ||
56 | #define VIDEO_8POINT 0x0f01 | ||
57 | #define VIDEO_80x43 0x0f02 | ||
58 | #define VIDEO_80x28 0x0f03 | ||
59 | #define VIDEO_CURRENT_MODE 0x0f04 | ||
60 | #define VIDEO_80x30 0x0f05 | ||
61 | #define VIDEO_80x34 0x0f06 | ||
62 | #define VIDEO_80x60 0x0f07 | ||
63 | #define VIDEO_GFX_HACK 0x0f08 | ||
64 | #define VIDEO_LAST_SPECIAL 0x0f09 | ||
65 | |||
66 | /* Video modes given by resolution */ | ||
67 | #define VIDEO_FIRST_RESOLUTION 0x1000 | ||
68 | |||
69 | /* The "recalculate timings" flag */ | ||
70 | #define VIDEO_RECALC 0x8000 | ||
71 | |||
72 | /* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ | ||
73 | #ifdef CONFIG_VIDEO_RETAIN | ||
74 | void store_screen(void); | ||
75 | #define DO_STORE() store_screen() | ||
76 | #else | ||
77 | #define DO_STORE() ((void)0) | ||
78 | #endif /* CONFIG_VIDEO_RETAIN */ | ||
79 | |||
80 | /* | ||
81 | * Mode table structures | ||
82 | */ | ||
83 | |||
84 | struct mode_info { | ||
85 | u16 mode; /* Mode number (vga= style) */ | ||
86 | u8 x, y; /* Width, height */ | ||
87 | }; | ||
88 | |||
89 | struct card_info { | ||
90 | const char *card_name; | ||
91 | int (*set_mode)(struct mode_info *mode); | ||
92 | int (*probe)(void); | ||
93 | struct mode_info *modes; | ||
94 | int nmodes; /* Number of probed modes so far */ | ||
95 | int unsafe; /* Probing is unsafe, only do after "scan" */ | ||
96 | u16 xmode_first; /* Unprobed modes to try to call anyway */ | ||
97 | u16 xmode_n; /* Size of unprobed mode range */ | ||
98 | }; | ||
99 | |||
100 | #define __videocard struct card_info __attribute__((section(".videocards"))) | ||
101 | extern struct card_info video_cards[], video_cards_end[]; | ||
102 | |||
103 | int mode_defined(u16 mode); /* video.c */ | ||
104 | |||
105 | /* Basic video information */ | ||
106 | #define ADAPTER_CGA 0 /* CGA/MDA/HGC */ | ||
107 | #define ADAPTER_EGA 1 | ||
108 | #define ADAPTER_VGA 2 | ||
109 | |||
110 | extern int adapter; | ||
111 | extern u16 video_segment; | ||
112 | extern int force_x, force_y; /* Don't query the BIOS for cols/rows */ | ||
113 | extern int do_restore; /* Restore screen contents */ | ||
114 | extern int graphic_mode; /* Graphics mode with linear frame buffer */ | ||
115 | |||
116 | /* | ||
117 | * int $0x10 is notorious for touching registers it shouldn't. | ||
118 | * gcc doesn't like %ebp being clobbered, so define it as a push/pop | ||
119 | * sequence here. | ||
120 | * | ||
121 | * A number of systems, including the original PC can clobber %bp in | ||
122 | * certain circumstances, like when scrolling. There exists at least | ||
123 | * one Trident video card which could clobber DS under a set of | ||
124 | * circumstances that we are unlikely to encounter (scrolling when | ||
125 | * using an extended graphics mode of more than 800x600 pixels), but | ||
126 | * it's cheap insurance to deal with that here. | ||
127 | */ | ||
128 | #define INT10 "pushl %%ebp; pushw %%ds; int $0x10; popw %%ds; popl %%ebp" | ||
129 | |||
130 | /* Accessing VGA indexed registers */ | ||
131 | static inline u8 in_idx(u16 port, u8 index) | ||
132 | { | ||
133 | outb(index, port); | ||
134 | return inb(port+1); | ||
135 | } | ||
136 | |||
137 | static inline void out_idx(u8 v, u16 port, u8 index) | ||
138 | { | ||
139 | outw(index+(v << 8), port); | ||
140 | } | ||
141 | |||
142 | /* Writes a value to an indexed port and then reads the port again */ | ||
143 | static inline u8 tst_idx(u8 v, u16 port, u8 index) | ||
144 | { | ||
145 | out_idx(port, index, v); | ||
146 | return in_idx(port, index); | ||
147 | } | ||
148 | |||
149 | /* Get the I/O port of the VGA CRTC */ | ||
150 | u16 vga_crtc(void); /* video-vga.c */ | ||
151 | |||
152 | #endif /* BOOT_VIDEO_H */ | ||
diff --git a/arch/x86/boot/voyager.c b/arch/x86/boot/voyager.c new file mode 100644 index 000000000000..61c8fe0453be --- /dev/null +++ b/arch/x86/boot/voyager.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* -*- linux-c -*- ------------------------------------------------------- * | ||
2 | * | ||
3 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | ||
5 | * | ||
6 | * This file is part of the Linux kernel, and is made available under | ||
7 | * the terms of the GNU General Public License version 2. | ||
8 | * | ||
9 | * ----------------------------------------------------------------------- */ | ||
10 | |||
11 | /* | ||
12 | * arch/i386/boot/voyager.c | ||
13 | * | ||
14 | * Get the Voyager config information | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | #ifdef CONFIG_X86_VOYAGER | ||
20 | |||
21 | int query_voyager(void) | ||
22 | { | ||
23 | u8 err; | ||
24 | u16 es, di; | ||
25 | /* Abuse the apm_bios_info area for this */ | ||
26 | u8 *data_ptr = (u8 *)&boot_params.apm_bios_info; | ||
27 | |||
28 | data_ptr[0] = 0xff; /* Flag on config not found(?) */ | ||
29 | |||
30 | asm("pushw %%es ; " | ||
31 | "int $0x15 ; " | ||
32 | "setc %0 ; " | ||
33 | "movw %%es, %1 ; " | ||
34 | "popw %%es" | ||
35 | : "=q" (err), "=r" (es), "=D" (di) | ||
36 | : "a" (0xffc0)); | ||
37 | |||
38 | if (err) | ||
39 | return -1; /* Not Voyager */ | ||
40 | |||
41 | set_fs(es); | ||
42 | copy_from_fs(data_ptr, di, 7); /* Table is 7 bytes apparently */ | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | #endif /* CONFIG_X86_VOYAGER */ | ||