Commit 3d8a95c7 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] boot code for PC9800 systems

parent af618365
#
# arch/i386/boot/Makefile
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1994 by Linus Torvalds
#
# ROOT_DEV specifies the default root-device when making the image.
# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
# the default of FLOPPY is used by 'build'.
ROOT_DEV := CURRENT
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
# The number is the same as you would ordinarily press at bootup.
SVGA_MODE := -DSVGA_MODE=NORMAL_VGA
# If you want the RAM disk device, define this to be the size in blocks.
#RAMDISK := -DRAMDISK=512
EXTRA_TARGETS := vmlinux.bin bootsect bootsect.o \
setup setup.o zImage bzImage
subdir- := compressed
host-progs := tools/build
# ---------------------------------------------------------------------------
$(obj)/zImage: IMAGE_OFFSET := 0x1000
$(obj)/zImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK)
$(obj)/bzImage: IMAGE_OFFSET := 0x100000
$(obj)/bzImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__
$(obj)/bzImage: BUILDFLAGS := -b
quiet_cmd_image = BUILD $@
cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/bootsect $(obj)/setup \
$(obj)/vmlinux.bin $(ROOT_DEV) > $@
$(obj)/zImage $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \
$(obj)/vmlinux.bin $(obj)/tools/build FORCE
$(call if_changed,image)
@echo 'Kernel: $@ is ready'
$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext
$(obj)/setup $(obj)/bootsect: %: %.o FORCE
$(call if_changed,ld)
$(obj)/compressed/vmlinux: FORCE
$(Q)$(MAKE) -f scripts/Makefile.build obj=$(obj)/compressed \
IMAGE_OFFSET=$(IMAGE_OFFSET) $@
zdisk: $(BOOTIMAGE)
dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0
zlilo: $(BOOTIMAGE)
if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz
cp System.map $(INSTALL_PATH)/
if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
install: $(BOOTIMAGE)
sh $(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
/*
* bootsect.S - boot sector for NEC PC-9800 series
*
* Linux/98 project at Kyoto University Microcomputer Club (KMC)
* FUJITA Norimasa, TAKAI Kousuke 1997-1998
* rewritten by TAKAI Kousuke (as86 -> gas), Nov 1999
*
* Based on:
* bootsect.S Copyright (C) 1991, 1992 Linus Torvalds
* modified by Drew Eckhardt
* modified by Bruce Evans (bde)
*
* bootsect.S is loaded at 0x1FC00 or 0x1FE00 by the bios-startup routines,
* and moves itself out of the way to address 0x90000, and jumps there.
*
* It then loads 'setup' directly after itself (0x90200), and the system
* at 0x10000, using BIOS interrupts.
*
* NOTE! currently system is at most (8*65536-4096) bytes long. This should
* be no problem, even in the future. I want to keep it simple. This 508 kB
* kernel size should be enough, especially as this doesn't contain the
* buffer cache as in minix (and especially now that the kernel is
* compressed :-)
*
* The loader has been made as simple as possible, and continuous
* read errors will result in a unbreakable loop. Reboot by hand. It
* loads pretty fast by getting whole tracks at a time whenever possible.
*/
#include <linux/config.h> /* for CONFIG_ROOT_RDONLY */
#include <asm/boot.h>
SETUPSECTS = 4 /* default nr of setup-sectors */
BOOTSEG = 0x1FC0 /* original address of boot-sector */
INITSEG = DEF_INITSEG /* we move boot here - out of the way */
SETUPSEG = DEF_SETUPSEG /* setup starts here */
SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
/* to be loaded */
ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif
/* normal/hireso text VRAM segments */
#define NORMAL_TEXT 0xa000
#define HIRESO_TEXT 0xe000
/* bios work area addresses */
#define EXPMMSZ 0x0401
#define BIOS_FLAG 0x0501
#define DISK_BOOT 0x0584
.code16
.text
.global _start
_start:
#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
int $0x3
#endif
jmp real_start
.ascii "Linux 98"
.word 0
real_start:
xorw %di, %di /* %di = 0 */
movw %di, %ss /* %ss = 0 */
movw $0x03F0, %sp
pushw %cx /* for hint */
movw $0x0A00, %ax /* normal mode defaults (80x25) */
testb $0x08, %ss:BIOS_FLAG /* check hi-reso bit */
jnz set_crt_mode
/*
* Hi-Reso (high-resolution) machine.
*
* Some hi-reso machines have no RAMs on bank 8/A (0x080000 - 0x0BFFFF).
* On such machines we get two RAM banks from top of protect menory and
* map them on bank 8/A.
* These work-around must be done before moving myself on INITSEG (0x090000-).
*/
movw $(HIRESO_TEXT >> 8), %cs:(vram + 1) /* text VRAM segment */
/* set memory window */
movb $0x08, %al
outb %al, $0x91 /* map native RAM (if any) */
movb $0x0A, %al
outb %al, $0x93
/* check bank ram A */
pushw $0xA500
popw %ds
movw (%di), %cx /* %si == 0 from entry */
notw %cx
movw %cx, (%di)
movw $0x43F, %dx /* cache flush for 486 and up. */
movb $0xA0, %al
outb %al, %dx
cmpw %cx, (%di)
je hireso_done
/*
* Write test failed; we have no native RAM on 080000h - 0BFFFFh.
* Take 256KB of RAM from top of protected memory.
*/
movb %ss:EXPMMSZ, %al
subb $2, %al /* reduce 2 x 128KB */
movb %al, %ss:EXPMMSZ
addb %al, %al
addb $0x10, %al
outb %al, $0x91
addb $2, %al
outb %al, $0x93
hireso_done:
movb $0x10, %al /* CRT mode 80x31, %ah still 0Ah */
set_crt_mode:
int $0x18 /* set CRT mode */
movb $0x0C, %ah /* turn on text displaying */
int $0x18
xorw %dx, %dx /* position cursor to home */
movb $0x13, %ah
int $0x18
movb $0x11, %ah /* turn cursor displaying on */
int $0x18
/* move 1 kilobytes from [BOOTSEG:0000h] to [INITSEG:0000h] */
cld
xorw %si, %si
pushw $INITSEG
popw %es
movw $512, %cx /* %di == 0 from entry */
rep
cs
movsw
ljmp $INITSEG, $go
go:
pushw %cs
popw %ds /* %ds = %cs */
popw %dx /* %dh = saved %ch passed from BIOS */
movb %ss:DISK_BOOT, %al
andb $0xf0, %al /* %al = Device Address */
movb $18, %ch /* 18 secs/track, 512 b/sec (1440 KB) */
cmpb $0x30, %al
je try512
cmpb $0x90, %al /* 1 MB I/F, 1 MB floppy */
je try1.2M
cmpb $0xf0, %al /* 640 KB I/F, 1 MB floppy */
je try1.2M
movb $9, %ch /* 9 secs/track, 512 b/sec ( 720 KB) */
cmpb $0x10, %al /* 1 MB I/F, 640 KB floppy */
je try512
cmpb $0x70, %al /* 640 KB I/F, 640 KB floppy */
jne error /* unknown device? */
/* XXX: Does it make sense to support 8 secs/track, 512 b/sec
(640 KB) floppy? */
try512: movb $2, %cl /* 512 b/sec */
lasttry:call tryload
/*
* Display error message and halt
*/
error: movw $error_msg, %si
call print
wait_reboot:
movb $0x0, %ah
int $0x18 /* wait keyboard input */
1: movb $0, %al
outb %al, $0xF0 /* reset CPU */
jmp 1b /* just in case... */
try1.2M:cmpb $2, %dh
je try2HC
movw $0x0803, %cx /* 8 secs/track, 1024 b/sec (1232 KB) */
call tryload
movb $15, %ch /* 15 secs/track, 512 b/sec (1200 KB) */
jmp try512
try2HC: movw $0x0F02, %cx /* 15 secs/track, 512 b/sec (1200 KB) */
call tryload
movw $0x0803, %cx /* 8 secs/track, 1024 b/sec (1232 KB) */
jmp lasttry
/*
* Try to load SETUP and SYSTEM provided geometry information in %cx.
* This routine *will not* return on successful load...
*/
tryload:
movw %cx, sectlen
movb %ss:DISK_BOOT, %al
movb $0x7, %ah /* recalibrate the drive */
int $0x1b
jc error /* recalibration should succeed */
/*
* Load SETUP into memory. It is assumed that SETUP fits into
* first cylinder (2 tracks, 9KB on 2DD, 15-18KB on 2HD).
*/
movb $0, %bl
movb setup_sects, %bh
incb %bh
shlw %bx /* %bx = (setup_sects + 1) * 512 */
movw $128, %bp
shlw %cl, %bp /* %bp = <sector size> */
subw %bp, %bx /* length to load */
movw $0x0002, %dx /* head 0, sector 2 */
movb %cl, %ch /* `N' for sector address */
movb $0, %cl /* cylinder 0 */
pushw %cs
popw %es /* %es = %cs (= INITSEG) */
movb $0xd6, %ah /* read, multi-track, MFM */
int $0x1b /* load it! */
jc read_error
movw $loading_msg, %si
call print
movw $SYSSEG, %ax
movw %ax, %es /* %es = SYSSEG */
/*
* This routine loads the system at address 0x10000, making sure
* no 64kB boundaries are crossed. We try to load it as fast as
* possible, loading whole tracks whenever we can.
*
* in: es - starting address segment (normally 0x1000)
*/
movb %ch, %cl
addb $7, %cl /* %cl = log2 <sector_size> */
shrw %cl, %bx /* %bx = # of phys. sectors in SETUP */
addb %bl, %dl /* %dl = start sector # of SYSTEM */
decb %dl /* %dl is 0-based in below loop */
rp_read_newseg:
xorw %bp, %bp /* = starting address within segment */
#ifdef __BIG_KERNEL__
bootsect_kludge = 0x220 /* 0x200 (size of bootsector) + 0x20 (offset */
lcall *bootsect_kludge /* of bootsect_kludge in setup.S */
#else
movw %es, %ax
subw $SYSSEG, %ax
#endif
cmpw syssize, %ax
ja boot /* done! */
rp_read:
movb sectors, %al
addb %al, %al
movb %al, %ch /* # of sectors on both surface */
subb %dl, %al /* # of sectors left on this track */
movb $0, %ah
shlw %cl, %ax /* # of bytes left on this track */
movw %ax, %bx /* transfer length */
addw %bp, %ax /* cross 64K boundary? */
jnc 1f /* ok. */
jz 1f /* also ok. */
/*
* Oops, we are crossing 64K boundary...
* Adjust transfer length to make transfer fit in the boundary.
*
* Note: sector size is assumed to be a measure of 65536.
*/
xorw %bx, %bx
subw %bp, %bx
1: pushw %dx
movw $dot_msg, %si /* give progress message */
call print
xchgw %ax, %dx
movb $0, %ah
divb sectors
xchgb %al, %ah
xchgw %ax, %dx /* %dh = head # / %dl = sector # */
incb %dl /* fix %dl to 1-based */
pushw %cx
movw cylinder, %cx
movb $0xd6, %ah /* read, multi-track, seek, MFM */
movb %ss:DISK_BOOT, %al
int $0x1b
popw %cx
popw %dx
jc read_error
movw %bx, %ax /* # of bytes just read */
shrw %cl, %ax /* %ax = # of sectors just read */
addb %al, %dl /* advance sector # */
cmpb %ch, %dl /* %ch = # of sectors/cylinder */
jb 2f
incb cylinder /* next cylinder */
xorb %dl, %dl /* sector 0 */
2: addw %bx, %bp /* advance offset pointer */
jnc rp_read
/* offset pointer wrapped; advance segment pointer. */
movw %es, %ax
addw $0x1000, %ax
movw %ax, %es
jmp rp_read_newseg
read_error:
ret
boot: movw %cs, %ax /* = INITSEG */
/* movw %ax, %ds */
movw %ax, %ss
movw $0x4000, %sp /* 0x4000 is arbitrary value >=
* length of bootsect + length of
* setup + room for stack;
* PC-9800 never have BIOS workareas
* on high memory.
*/
/*
* After that we check which root-device to use. If the device is
* not defined, /dev/fd0 (2, 0) will be used.
*/
cmpw $0, root_dev
jne 3f
movb $2, root_dev+1
3:
/*
* After that (everything loaded), we jump to the setup-routine
* loaded directly after the bootblock:
*/
ljmp $SETUPSEG, $0
/*
* Subroutine for print string on console.
* %cs:%si - pointer to message
*/
print:
pushaw
pushw %ds
pushw %es
pushw %cs
popw %ds
lesw curpos, %di /* %es:%di = current text VRAM addr. */
1: xorw %ax, %ax
lodsb
testb %al, %al
jz 2f /* end of string */
stosw /* character code */
movb $0xE1, %es:0x2000-2(%di) /* character attribute */
jmp 1b
2: movw %di, %dx
movb $0x13, %ah
int $0x18 /* move cursor to current point */
popw %es
popw %ds
popaw
ret
loading_msg:
.string "Loading"
dot_msg:
.string "."
error_msg:
.string "Read Error!"
.org 490
curpos: .word 160 /* current cursor position */
vram: .word NORMAL_TEXT /* text VRAM segment */
cylinder: .byte 0 /* current cylinder (lower byte) */
sectlen: .byte 0 /* (log2 of <sector size>) - 7 */
sectors: .byte 0x0F /* default is 2HD (15 sector/track) */
# XXX: This is a fairly snug fit.
.org 497
setup_sects: .byte SETUPSECTS
root_flags: .word ROOT_RDONLY
syssize: .word SYSSIZE
swap_dev: .word SWAP_DEV
ram_size: .word RAMDISK
vid_mode: .word SVGA_MODE
root_dev: .word ROOT_DEV
boot_flag: .word 0xAA55
#
# linux/arch/i386/boot/compressed/Makefile
#
# create a compressed vmlinux image from the original vmlinux
#
EXTRA_TARGETS := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o
EXTRA_AFLAGS := -traditional
LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32
$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
$(call if_changed,ld)
@:
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
$(call if_changed,gzip)
LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
$(call if_changed,ld)
/*
* linux/boot/head.S
*
* Copyright (C) 1991, 1992, 1993 Linus Torvalds
*/
/*
* head.S contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory. [According to comments etc elsewhere on a compressed
* kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC]
*
* Page 0 is deliberately kept safe, since System Management Mode code in
* laptops may need to access the BIOS data stored there. This is also
* useful for future device drivers that either access the BIOS via VM86
* mode.
*/
/*
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
*/
.text
#include <linux/linkage.h>
#include <asm/segment.h>
.globl startup_32
startup_32:
cld
cli
movl $(__BOOT_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
lss stack_start,%esp
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/
pushl $0
popfl
/*
* Clear BSS
*/
xorl %eax,%eax
movl $_edata,%edi
movl $_end,%ecx
subl %edi,%ecx
cld
rep
stosb
/*
* Do the decompression, and jump to the new kernel..
*/
subl $16,%esp # place for structure on the stack
movl %esp,%eax
pushl %esi # real mode pointer as second arg
pushl %eax # address of structure as first arg
call decompress_kernel
orl %eax,%eax
jnz 3f
popl %esi # discard address
popl %esi # real mode pointer
xorl %ebx,%ebx
ljmp $(__BOOT_CS), $0x100000
/*
* We come here, if we were loaded high.
* We need to move the move-in-place routine down to 0x1000
* and then start it with the buffer addresses in registers,
* which we got from the stack.
*/
3:
movl $move_routine_start,%esi
movl $0x1000,%edi
movl $move_routine_end,%ecx
subl %esi,%ecx
addl $3,%ecx
shrl $2,%ecx
cld
rep
movsl
popl %esi # discard the address
popl %ebx # real mode pointer
popl %esi # low_buffer_start
popl %ecx # lcount
popl %edx # high_buffer_start
popl %eax # hcount
movl $0x100000,%edi
cli # make sure we don't get interrupted
ljmp $(__BOOT_CS), $0x1000 # and jump to the move routine
/*
* Routine (template) for moving the decompressed kernel in place,
* if we were high loaded. This _must_ PIC-code !
*/
move_routine_start:
movl %ecx,%ebp
shrl $2,%ecx
rep
movsl
movl %ebp,%ecx
andl $3,%ecx
rep
movsb
movl %edx,%esi
movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0
addl $3,%ecx
shrl $2,%ecx
rep
movsl
movl %ebx,%esi # Restore setup pointer
xorl %ebx,%ebx
ljmp $(__BOOT_CS), $0x100000
move_routine_end:
/*
* misc.c
*
* This is a collection of several routines from gzip-1.0.3
* adapted for Linux.
*
* malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
* puts by Nick Holloway 1993, better puts by Martin Mares 1995
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
*/
#include <linux/linkage.h>
#include <linux/vmalloc.h>
#include <linux/tty.h>
#include <asm/io.h>
#ifdef STANDARD_MEMORY_BIOS_CALL
#undef STANDARD_MEMORY_BIOS_CALL
#endif
/*
* gzip declarations
*/
#define OF(args) args
#define STATIC static
#undef memset
#undef memcpy
/*
* Why do we do this? Don't ask me..
*
* Incomprehensible are the ways of bootloaders.
*/
static void* memset(void *, int, size_t);
static void* memcpy(void *, __const void *, size_t);
#define memzero(s, n) memset ((s), 0, (n))
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define WSIZE 0x8000 /* Window size must be at least 32k, */
/* and a power of two */
static uch *inbuf; /* input buffer */
static uch window[WSIZE]; /* Sliding window buffer */
static unsigned insize = 0; /* valid bytes in inbuf */
static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
static unsigned outcnt = 0; /* bytes in output buffer */
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
#define RESERVED 0xC0 /* bit 6,7: reserved */
#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
/* Diagnostic functions */
#ifdef DEBUG
# define Assert(cond,msg) {if(!(cond)) error(msg);}
# define Trace(x) fprintf x
# define Tracev(x) {if (verbose) fprintf x ;}
# define Tracevv(x) {if (verbose>1) fprintf x ;}
# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
#else
# define Assert(cond,msg)
# define Trace(x)
# define Tracev(x)
# define Tracevv(x)
# define Tracec(c,x)
# define Tracecv(c,x)
#endif
static int fill_inbuf(void);
static void flush_window(void);
static void error(char *m);
static void gzip_mark(void **);
static void gzip_release(void **);
/*
* This is set up by the setup-routine at boot-time
*/
static unsigned char *real_mode; /* Pointer to real-mode data */
#define EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
#ifndef STANDARD_MEMORY_BIOS_CALL
#define ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
#endif
#define SCREEN_INFO (*(struct screen_info *)(real_mode+0))
extern char input_data[];
extern int input_len;
static long bytes_out = 0;
static uch *output_data;
static unsigned long output_ptr = 0;
static void *malloc(int size);
static void free(void *where);
static void puts(const char *);
extern int end;
static long free_mem_ptr = (long)&end;
static long free_mem_end_ptr;
#define INPLACE_MOVE_ROUTINE 0x1000
#define LOW_BUFFER_START 0x2000
#define LOW_BUFFER_MAX 0x90000
#define HEAP_SIZE 0x3000
static unsigned int low_buffer_end, low_buffer_size;
static int high_loaded =0;
static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;
static char *vidmem = (char *)0xa0000;
static int lines, cols;
#ifdef CONFIG_X86_NUMAQ
static void * xquad_portio = NULL;
#endif
#include "../../../../lib/inflate.c"
static void *malloc(int size)
{
void *p;
if (size <0) error("Malloc error\n");
if (free_mem_ptr <= 0) error("Memory error\n");
free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
p = (void *)free_mem_ptr;
free_mem_ptr += size;
if (free_mem_ptr >= free_mem_end_ptr)
error("\nOut of memory\n");
return p;
}
static void free(void *where)
{ /* Don't care */
}
static void gzip_mark(void **ptr)
{
*ptr = (void *) free_mem_ptr;
}
static void gzip_release(void **ptr)
{
free_mem_ptr = (long) *ptr;
}
static void scroll(void)
{
int i;
memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
vidmem[i] = ' ';
}
static void puts(const char *s)
{
int x,y,pos;
char c;
x = SCREEN_INFO.orig_x;
y = SCREEN_INFO.orig_y;
while ( ( c = *s++ ) != '\0' ) {
if ( c == '\n' ) {
x = 0;
if ( ++y >= lines ) {
scroll();
y--;
}
} else {
vidmem [ ( x + cols * y ) * 2 ] = c;
if ( ++x >= cols ) {
x = 0;
if ( ++y >= lines ) {
scroll();
y--;
}
}
}
}
SCREEN_INFO.orig_x = x;
SCREEN_INFO.orig_y = y;
pos = x + cols * y; /* Update cursor position */
while (!(inb_p(0x60) & 4));
outb_p(0x49, 0x62);
outb_p(pos & 0xff, 0x60);
outb_p((pos >> 8) & 0xff, 0x60);
}
static void* memset(void* s, int c, size_t n)
{
int i;
char *ss = (char*)s;
for (i=0;i<n;i++) ss[i] = c;
return s;
}
static void* memcpy(void* __dest, __const void* __src,
size_t __n)
{
int i;
char *d = (char *)__dest, *s = (char *)__src;
for (i=0;i<__n;i++) d[i] = s[i];
return __dest;
}
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
* and at least one byte is really needed.
*/
static int fill_inbuf(void)
{
if (insize != 0) {
error("ran out of input data\n");
}
inbuf = input_data;
insize = input_len;
inptr = 1;
return inbuf[0];
}
/* ===========================================================================
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
* (Used for the decompressed data only.)
*/
static void flush_window_low(void)
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, *out, ch;
in = window;
out = &output_data[output_ptr];
for (n = 0; n < outcnt; n++) {
ch = *out++ = *in++;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
output_ptr += (ulg)outcnt;
outcnt = 0;
}
static void flush_window_high(void)
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, ch;
in = window;
for (n = 0; n < outcnt; n++) {
ch = *output_data++ = *in++;
if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
outcnt = 0;
}
static void flush_window(void)
{
if (high_loaded) flush_window_high();
else flush_window_low();
}
static void error(char *x)
{
puts("\n\n");
puts(x);
puts("\n\n -- System halted");
while(1); /* Halt */
}
#define STACK_SIZE (4096)
long user_stack [STACK_SIZE];
struct {
long * a;
short b;
} stack_start = { & user_stack [STACK_SIZE] , __BOOT_DS };
static void setup_normal_output_buffer(void)
{
#ifdef STANDARD_MEMORY_BIOS_CALL
if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n");
#else
if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n");
#endif
output_data = (char *)0x100000; /* Points to 1M */
free_mem_end_ptr = (long)real_mode;
}
struct moveparams {
uch *low_buffer_start; int lcount;
uch *high_buffer_start; int hcount;
};
static void setup_output_buffer_if_we_run_high(struct moveparams *mv)
{
high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE);
#ifdef STANDARD_MEMORY_BIOS_CALL
if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n");
#else
if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n");
#endif
mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START;
low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX
? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff;
low_buffer_size = low_buffer_end - LOW_BUFFER_START;
high_loaded = 1;
free_mem_end_ptr = (long)high_buffer_start;
if ( (0x100000 + low_buffer_size) > ((ulg)high_buffer_start)) {
high_buffer_start = (uch *)(0x100000 + low_buffer_size);
mv->hcount = 0; /* say: we need not to move high_buffer */
}
else mv->hcount = -1;
mv->high_buffer_start = high_buffer_start;
}
static void close_output_buffer_if_we_run_high(struct moveparams *mv)
{
if (bytes_out > low_buffer_size) {
mv->lcount = low_buffer_size;
if (mv->hcount)
mv->hcount = bytes_out - low_buffer_size;
} else {
mv->lcount = bytes_out;
mv->hcount = 0;
}
}
asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode)
{
real_mode = rmode;
vidmem = (char *)(((unsigned int)SCREEN_INFO.orig_video_page) << 4);
lines = SCREEN_INFO.orig_video_lines;
cols = SCREEN_INFO.orig_video_cols;
if (free_mem_ptr < 0x100000) setup_normal_output_buffer();
else setup_output_buffer_if_we_run_high(mv);
makecrc();
puts("Uncompressing Linux... ");
gunzip();
puts("Ok, booting the kernel.\n");
if (high_loaded) close_output_buffer_if_we_run_high(mv);
return high_loaded;
}
/* We don't actually check for stack overflows this early. */
__asm__(".globl mcount ; mcount: ret\n");
SECTIONS
{
.data : {
input_len = .;
LONG(input_data_end - input_data) input_data = .;
*(.data)
input_data_end = .;
}
}
#!/bin/sh
#
# arch/i386/boot/install.sh
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1995 by Linus Torvalds
#
# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
#
# "make install" script for i386 architecture
#
# Arguments:
# $1 - kernel version
# $2 - kernel image file
# $3 - kernel map file
# $4 - default install path (blank if root directory)
#
# User may have a custom install script
if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi
if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi
# Default install - same as make zlilo
if [ -f $4/vmlinuz ]; then
mv $4/vmlinuz $4/vmlinuz.old
fi
if [ -f $4/System.map ]; then
mv $4/System.map $4/System.old
fi
cat $2 > $4/vmlinuz
cp $3 $4/System.map
if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
/*
* setup.S Copyright (C) 1991, 1992 Linus Torvalds
*
* setup.s is responsible for getting the system data from the BIOS,
* and putting them into the appropriate places in system memory.
* both setup.s and system has been loaded by the bootblock.
*
* This code asks the bios for memory/disk/other parameters, and
* puts them in a "safe" place: 0x90000-0x901FF, ie where the
* boot-block used to be. It is then up to the protected mode
* system to read them from there before the area is overwritten
* for buffer-blocks.
*
* Move PS/2 aux init code to psaux.c
* (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
*
* some changes and additional features by Christoph Niemann,
* March 1993/June 1994 (Christoph.Niemann@linux.org)
*
* add APM BIOS checking by Stephen Rothwell, May 1994
* (sfr@canb.auug.org.au)
*
* High load stuff, initrd support and position independency
* by Hans Lermen & Werner Almesberger, February 1996
* <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
*
* Video handling moved to video.S by Martin Mares, March 1996
* <mj@k332.feld.cvut.cz>
*
* Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
* parsons) to avoid loadlin confusion, July 1997
*
* Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
* <stiker@northlink.com>
*
* Fix to work around buggy BIOSes which dont use carry bit correctly
* and/or report extended memory in CX/DX for e801h memory size detection
* call. As a result the kernel got wrong figures. The int15/e801h docs
* from Ralf Brown interrupt list seem to indicate AX/BX should be used
* anyway. So to avoid breaking many machines (presumably there was a reason
* to orginally use CX/DX instead of AX/BX), we do a kludge to see
* if CX/DX have been changed in the e801 call and if so use AX/BX .
* Michael Miller, April 2001 <michaelm@mjmm.org>
*
* New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
* by Robert Schwebel, December 2001 <robert@schwebel.de>
*
* Heavily modified for NEC PC-9800 series by Kyoto University Microcomputer
* Club (KMC) Linux/98 project <seraphim@kmc.kyoto-u.ac.jp>, 1997-1999
*/
#include <linux/config.h>
#include <asm/segment.h>
#include <linux/version.h>
#include <linux/compile.h>
#include <asm/boot.h>
#include <asm/e820.h>
#include <asm/page.h>
/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A
#define HIRESO_TEXT 0xe000
#define NORMAL_TEXT 0xa000
#define BIOS_FLAG2 0x0400
#define BIOS_FLAG5 0x0458
#define RDISK_EQUIP 0x0488
#define BIOS_FLAG 0x0501
#define KB_SHFT_STS 0x053a
#define DISK_EQUIP 0x055c
INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way
SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment
# ... and the former contents of CS
DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020
.code16
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
start:
jmp trampoline
# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
.ascii "HdrS" # header signature
.word 0x0203 # header version number (>= 0x0105)
# or else old loadlin-1.5 will fail)
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
start_sys_seg: .word SYSSEG
.word kernel_version # pointing to kernel version string
# above section of header is compatible
# with loadlin-1.5 (header v1.5). Don't
# change it.
type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
# Bootlin, SYSLX, bootsect...)
# See Documentation/i386/boot.txt for
# assigned ids
# flags, unused bits must be zero (RFU) bit within loadflags
loadflags:
LOADED_HIGH = 1 # If set, the kernel is loaded high
CAN_USE_HEAP = 0x80 # If set, the loader also has set
# heap_end_ptr to tell how much
# space behind setup.S can be used for
# heap purposes.
# Only the loader knows what is free
#ifndef __BIG_KERNEL__
.byte 0
#else
.byte LOADED_HIGH
#endif
setup_move_size: .word 0x8000 # size to move, when setup is not
# loaded at 0x90000. We will move setup
# to 0x90000 then just before jumping
# into the kernel. However, only the
# loader knows how much data behind
# us also needs to be loaded.
code32_start: # here loaders can put a different
# start address for 32-bit code.
#ifndef __BIG_KERNEL__
.long 0x1000 # 0x1000 = default for zImage
#else
.long 0x100000 # 0x100000 = default for big kernel
#endif
ramdisk_image: .long 0 # address of loaded ramdisk image
# Here the loader puts the 32-bit
# address where it loaded the image.
# This only will be read by the kernel.
ramdisk_size: .long 0 # its size in bytes
bootsect_kludge:
.word bootsect_helper, SETUPSEG
heap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later)
# space from here (exclusive) down to
# end of setup code can be used by setup
# for local heap purposes.
pad1: .word 0
cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
# If nonzero, a 32-bit pointer
# to the kernel command line.
# The command line should be
# located between the start of
# setup and the end of low
# memory (0xa0000), or it may
# get overwritten before it
# gets read. If this field is
# used, there is no longer
# anything magical about the
# 0x90000 segment; the setup
# can be located anywhere in
# low memory 0x10000 or higher.
ramdisk_max: .long __MAXMEM-1 # (Header version 0x0203 or later)
# The highest safe address for
# the contents of an initrd
trampoline: call start_of_setup
.space 1024
# End of setup header #####################################################
start_of_setup:
# Set %ds = %cs, we know that SETUPSEG = %cs at this point
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
# Check signature at end of setup
cmpw $SIG1, setup_sig1
jne bad_sig
cmpw $SIG2, setup_sig2
jne bad_sig
jmp good_sig1
# Routine to print asciiz string at ds:si
prtstr:
lodsb
andb %al, %al
jz fin
call prtchr
jmp prtstr
fin: ret
no_sig_mess: .string "No setup signature found ..."
good_sig1:
jmp good_sig
# We now have to find the rest of the setup code/data
bad_sig:
movw %cs, %ax # SETUPSEG
subw $DELTA_INITSEG, %ax # INITSEG
movw %ax, %ds
xorb %bh, %bh
movb (497), %bl # get setup sect from bootsect
subw $4, %bx # LILO loads 4 sectors of setup
shlw $8, %bx # convert to words (1sect=2^8 words)
movw %bx, %cx
shrw $3, %bx # convert to segment
addw $SYSSEG, %bx
movw %bx, %cs:start_sys_seg
# Move rest of setup code/data to here
movw $2048, %di # four sectors loaded by LILO
subw %si, %si
pushw %cs
popw %es
movw $SYSSEG, %ax
movw %ax, %ds
rep
movsw
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
cmpw $SIG1, setup_sig1
jne no_sig
cmpw $SIG2, setup_sig2
jne no_sig
jmp good_sig
no_sig:
lea no_sig_mess, %si
call prtstr
no_sig_loop:
hlt
jmp no_sig_loop
good_sig:
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %ds
# Check if an old loader tries to load a big-kernel
testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel?
jz loader_ok # No, no danger for old loaders.
cmpb $0, %cs:type_of_loader # Do we have a loader that
# can deal with us?
jnz loader_ok # Yes, continue.
pushw %cs # No, we have an old loader,
popw %ds # die.
lea loader_panic_mess, %si
call prtstr
jmp no_sig_loop
loader_panic_mess: .string "Wrong loader, giving up..."
loader_ok:
# Get memory size (extended mem, kB)
# On PC-9800, memory size detection is done completely in 32-bit
# kernel initialize code (kernel/setup.c).
pushw %es
xorl %eax, %eax
movw %ax, %es
movb %al, (E820NR) # PC-9800 has no E820
movb %es:(0x401), %al
shll $7, %eax
addw $1024, %ax
movw %ax, (2)
movl %eax, (0x1e0)
movw %es:(0x594), %ax
shll $10, %eax
addl %eax, (0x1e0)
popw %es
# Check for video adapter and its parameters and allow the
# user to browse video modes.
call video # NOTE: we need %ds pointing
# to bootsector
# Get text video mode
movb $0x0B, %ah
int $0x18 # CRT mode sense
movw $(20 << 8) + 40, %cx
testb $0x10, %al
jnz 3f
movb $20, %ch
testb $0x01, %al
jnz 1f
movb $25, %ch
jmp 1f
3: # If bit 4 was 1, it means either 1) 31 lines for hi-reso mode,
# or 2) 30 lines for PC-9821.
movb $31, %ch # hireso mode value
pushw $0
popw %es
testb $0x08, %es:BIOS_FLAG
jnz 1f
movb $30, %ch
1: # Now we got # of rows in %ch
movb %ch, (14)
testb $0x02, %al
jnz 2f
movb $80, %cl
2: # Now we got # of columns in %cl
movb %cl, (7)
# Next, get horizontal frequency if supported
movw $0x3100, %ax
int $0x18 # Call CRT bios
movb %al, (6) # If 31h is unsupported, %al remains 0
# Get hd0-3 data...
pushw %ds # aka INITSEG
popw %es
xorw %ax, %ax
movw %ax, %ds
cld
movw $0x0080, %di
movb DISK_EQUIP+1, %ah
movb $0x80, %al
get_hd_info:
shrb %ah
pushw %ax
jnc 1f
movb $0x84, %ah
int $0x1b
jnc 2f # Success
1: xorw %cx, %cx # `0 cylinders' means no drive
2: # Attention! Work area (drive_info) is arranged for PC-9800.
movw %cx, %ax # # of cylinders
stosw
movw %dx, %ax # # of sectors / # of heads
stosw
movw %bx, %ax # sector size in bytes
stosw
popw %ax
incb %al
cmpb $0x84, %al
jb get_hd_info
# Get fd data...
movw DISK_EQUIP, %ax
andw $0xf00f, %ax
orb %al, %ah
movb RDISK_EQUIP, %al
notb %al
andb %al, %ah # ignore all `RAM drive'
movb $0x30, %al
get_fd_info:
shrb %ah
pushw %ax
jnc 1f
movb $0xc4, %ah
int $0x1b
movb %ah, %al
andb $4, %al # 1.44MB support flag
shrb %al
addb $2, %al # %al = 2 (1.2MB) or 4 (1.44MB)
jmp 2f
1: movb $0, %al # no drive
2: stosb
popw %ax
incb %al
testb $0x04, %al
jz get_fd_info
addb $(0xb0 - 0x34), %al
jnc get_fd_info # check FDs on 640KB I/F
pushw %es
popw %ds # %ds got bootsector again
#if 0
mov $0, (0x1ff) # default is no pointing device
#endif
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
# Then check for an APM BIOS...
# %ds points to the bootsector
movw $0, 0x40 # version = 0 means no APM BIOS
movw $0x09a00, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x1f
jc done_apm_bios # Nope, no APM BIOS
cmpw $0x0504d, %bx # Check for "PM" signature
jne done_apm_bios # No signature, no APM BIOS
testb $0x02, %cl # Is 32 bit supported?
je done_apm_bios # No 32-bit, no (good) APM BIOS
movw $0x09a04, %ax # Disconnect first just in case
xorw %bx, %bx
int $0x1f # ignore return code
movw $0x09a03, %ax # 32 bit connect
xorl %ebx, %ebx
int $0x1f
jc no_32_apm_bios # Ack, error.
movw %ax, (66) # BIOS code segment
movl %ebx, (68) # BIOS entry point offset
movw %cx, (72) # BIOS 16 bit code segment
movw %dx, (74) # BIOS data segment
movl %esi, (78) # BIOS code segment length
movw %di, (82) # BIOS data segment length
# Redo the installation check as the 32 bit connect
# modifies the flags returned on some BIOSs
movw $0x09a00, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x1f
jc apm_disconnect # error -> shouldn't happen
cmpw $0x0504d, %bx # check for "PM" signature
jne apm_disconnect # no sig -> shouldn't happen
movw %ax, (64) # record the APM BIOS version
movw %cx, (76) # and flags
jmp done_apm_bios
apm_disconnect: # Tidy up
movw $0x09a04, %ax # Disconnect
xorw %bx, %bx
int $0x1f # ignore return code
jmp done_apm_bios
no_32_apm_bios:
andw $0xfffd, (76) # remove 32 bit support bit
done_apm_bios:
#endif
# Pass cursor position to kernel...
movw %cs:cursor_address, %ax
shrw %ax # cursor_address is 2 bytes unit
movb $80, %cl
divb %cl
xchgb %al, %ah # (0) = %al = X, (1) = %ah = Y
movw %ax, (0)
#if 0
movw $msg_cpos, %si
call prtstr_cs
call prthex
call prtstr_cs
movw %ds, %ax
call prthex
call prtstr_cs
movb $0x11, %ah
int $0x18
movb $0, %ah
int $0x18
.section .rodata, "a"
msg_cpos: .string "Cursor position: 0x"
.string ", %ds:0x"
.string "\r\n"
.previous
#endif
# Now we want to move to protected mode ...
cmpw $0, %cs:realmode_swtch
jz rmodeswtch_normal
lcall *%cs:realmode_swtch
jmp rmodeswtch_end
rmodeswtch_normal:
pushw %cs
call default_switch
rmodeswtch_end:
# we get the code32 start address and modify the below 'jmpi'
# (loader may have changed it)
movl %cs:code32_start, %eax
movl %eax, %cs:code32
# Now we move the system to its rightful place ... but we check if we have a
# big-kernel. In that case we *must* not move it ...
testb $LOADED_HIGH, %cs:loadflags
jz do_move0 # .. then we have a normal low
# loaded zImage
# .. or else we have a high
# loaded bzImage
jmp end_move # ... and we skip moving
do_move0:
movw $0x100, %ax # start of destination segment
movw %cs, %bp # aka SETUPSEG
subw $DELTA_INITSEG, %bp # aka INITSEG
movw %cs:start_sys_seg, %bx # start of source segment
cld
do_move:
movw %ax, %es # destination segment
incb %ah # instead of add ax,#0x100
movw %bx, %ds # source segment
addw $0x100, %bx
subw %di, %di
subw %si, %si
movw $0x800, %cx
rep
movsw
cmpw %bp, %bx # assume start_sys_seg > 0x200,
# so we will perhaps read one
# page more than needed, but
# never overwrite INITSEG
# because destination is a
# minimum one page below source
jb do_move
end_move:
# then we load the segment descriptors
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
# Check whether we need to be downward compatible with version <=201
cmpl $0, cmd_line_ptr
jne end_move_self # loader uses version >=202 features
cmpb $0x20, type_of_loader
je end_move_self # bootsect loader, we know of it
# Boot loader does not support boot protocol version 2.02.
# If we have our code not at 0x90000, we need to move it there now.
# We also then need to move the params behind it (commandline)
# Because we would overwrite the code on the current IP, we move
# it in two steps, jumping high after the first one.
movw %cs, %ax
cmpw $SETUPSEG, %ax
je end_move_self
cli # make sure we really have
# interrupts disabled !
# because after this the stack
# should not be used
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ss, %dx
cmpw %ax, %dx
jb move_self_1
addw $INITSEG, %dx
subw %ax, %dx # this will go into %ss after
# the move
move_self_1:
movw %ax, %ds
movw $INITSEG, %ax # real INITSEG
movw %ax, %es
movw %cs:setup_move_size, %cx
std # we have to move up, so we use
# direction down because the
# areas may overlap
movw %cx, %di
decw %di
movw %di, %si
subw $move_self_here+0x200, %cx
rep
movsb
ljmp $SETUPSEG, $move_self_here
move_self_here:
movw $move_self_here+0x200, %cx
rep
movsb
movw $SETUPSEG, %ax
movw %ax, %ds
movw %dx, %ss
end_move_self: # now we are at the right place
lidt idt_48 # load idt with 0,0
xorl %eax, %eax # Compute gdt_base
movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
shll $4, %eax
addl $gdt, %eax
movl %eax, (gdt_48+2)
lgdt gdt_48 # load gdt with whatever is
# appropriate
# that was painless, now we enable A20
outb %al, $0xf2 # A20 on
movb $0x02, %al
outb %al, $0xf6 # also A20 on; making ITF's
# way our model
# PC-9800 seems to enable A20 at the moment of `outb';
# so we don't wait unlike IBM PCs (see ../setup.S).
# enable DMA to access memory over 0x100000 (1MB).
movw $0x439, %dx
inb %dx, %al
andb $(~4), %al
outb %al, %dx
# Set DMA to increment its bank address automatically at 16MB boundary.
# Initial setting is 64KB boundary mode so that we can't run DMA crossing
# physical address 0xXXXXFFFF.
movb $0x0c, %al
outb %al, $0x29 # ch. 0
movb $0x0d, %al
outb %al, $0x29 # ch. 1
movb $0x0e, %al
outb %al, $0x29 # ch. 2
movb $0x0f, %al
outb %al, $0x29 # ch. 3
movb $0x50, %al
outb %al, $0x11 # reinitialize DMAC
# make sure any possible coprocessor is properly reset..
movb $0, %al
outb %al, $0xf8
outb %al, $0x5f # delay
# well, that went ok, I hope. Now we mask all interrupts - the rest
# is done in init_IRQ().
movb $0xFF, %al # mask all interrupts for now
outb %al, $0x0A
outb %al, $0x5f # delay
movb $0x7F, %al # mask all irq's but irq7 which
outb %al, $0x02 # is cascaded
# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
# need no steenking BIOS anyway (except for the initial loading :-).
# The BIOS-routine wants lots of unnecessary data, and it's less
# "interesting" anyway. This is how REAL programmers do it.
#
# Well, now's the time to actually move into protected mode. To make
# things as simple as possible, we do no register set-up or anything,
# we let the gnu-compiled 32-bit programs do that. We just jump to
# absolute address 0x1000 (or the loader supplied one),
# in 32-bit protected mode.
#
# Note that the short jump isn't strictly needed, although there are
# reasons why it might be a good idea. It won't hurt in any case.
movw $1, %ax # protected mode (PE) bit
lmsw %ax # This is it!
jmp flush_instr
flush_instr:
xorw %bx, %bx # Flag to indicate a boot
xorl %esi, %esi # Pointer to real-mode code
movw %cs, %si
subw $DELTA_INITSEG, %si
shll $4, %esi # Convert to 32-bit pointer
# NOTE: For high loaded big kernels we need a
# jmpi 0x100000,__BOOT_CS
#
# but we yet haven't reloaded the CS register, so the default size
# of the target offset still is 16 bit.
# However, using an operand prefix (0x66), the CPU will properly
# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
# Manual, Mixing 16-bit and 32-bit code, page 16-6)
.byte 0x66, 0xea # prefix + jmpi-opcode
code32: .long 0x1000 # will be set to 0x100000
# for big kernels
.word __BOOT_CS
# Here's a bunch of information about your current kernel..
kernel_version: .ascii UTS_RELEASE
.ascii " ("
.ascii LINUX_COMPILE_BY
.ascii "@"
.ascii LINUX_COMPILE_HOST
.ascii ") "
.ascii UTS_VERSION
.byte 0
# This is the default real mode switch routine.
# to be called just before protected mode transition
default_switch:
cli # no interrupts allowed !
outb %al, $0x50 # disable NMI for bootup
# sequence
lret
# This routine only gets called, if we get loaded by the simple
# bootsect loader _and_ have a bzImage to load.
# Because there is no place left in the 512 bytes of the boot sector,
# we must emigrate to code space here.
bootsect_helper:
cmpw $0, %cs:bootsect_es
jnz bootsect_second
movb $0x20, %cs:type_of_loader
movw %es, %ax
shrw $4, %ax
movb %ah, %cs:bootsect_src_base+2
movw %es, %ax
movw %ax, %cs:bootsect_es
subw $SYSSEG, %ax
lret # nothing else to do for now
bootsect_second:
pushw %bx
pushw %cx
pushw %si
pushw %di
testw %bp, %bp # 64K full ?
jne bootsect_ex
xorw %cx, %cx # zero means full 64K
pushw %cs
popw %es
movw $bootsect_gdt, %bx
xorw %si, %si # source address
xorw %di, %di # destination address
movb $0x90, %ah
int $0x1f
jc bootsect_panic # this, if INT1F fails
movw %cs:bootsect_es, %es # we reset %es to always point
incb %cs:bootsect_dst_base+2 # to 0x10000
bootsect_ex:
movb %cs:bootsect_dst_base+2, %ah
shlb $4, %ah # we now have the number of
# moved frames in %ax
xorb %al, %al
popw %di
popw %si
popw %cx
popw %bx
lret
bootsect_gdt:
.word 0, 0, 0, 0
.word 0, 0, 0, 0
bootsect_src:
.word 0xffff
bootsect_src_base:
.byte 0x00, 0x00, 0x01 # base = 0x010000
.byte 0x93 # typbyte
.word 0 # limit16,base24 =0
bootsect_dst:
.word 0xffff
bootsect_dst_base:
.byte 0x00, 0x00, 0x10 # base = 0x100000
.byte 0x93 # typbyte
.word 0 # limit16,base24 =0
.word 0, 0, 0, 0 # BIOS CS
.word 0, 0, 0, 0 # BIOS DS
bootsect_es:
.word 0
bootsect_panic:
pushw %cs
popw %ds
cld
leaw bootsect_panic_mess, %si
call prtstr
bootsect_panic_loop:
jmp bootsect_panic_loop
bootsect_panic_mess:
.string "INT1F refuses to access high mem, giving up."
# This routine prints one character (in %al) on console.
# PC-9800 doesn't have BIOS-function to do it like IBM PC's INT 10h - 0Eh,
# so we hardcode `prtchr' subroutine here.
prtchr:
pushaw
pushw %es
cmpb $0, %cs:prtchr_initialized
jnz prtchr_ok
xorw %cx, %cx
movw %cx, %es
testb $0x8, %es:BIOS_FLAG
jz 1f
movb $(HIRESO_TEXT >> 8), %cs:cursor_address+3
movw $(80 * 31 * 2), %cs:max_cursor_offset
1: pushw %ax
call get_cursor_position
movw %ax, %cs:cursor_address
popw %ax
movb $1, %cs:prtchr_initialized
prtchr_ok:
lesw %cs:cursor_address, %di
movw $160, %bx
movb $0, %ah
cmpb $13, %al
je do_cr
cmpb $10, %al
je do_lf
# normal (printable) character
stosw
movb $0xe1, %es:0x2000-2(%di)
jmp 1f
do_cr: movw %di, %ax
divb %bl # %al = Y, %ah = X * 2
mulb %bl
movw %ax, %dx
jmp 2f
do_lf: addw %bx, %di
1: movw %cs:max_cursor_offset, %cx
cmpw %cx, %di
movw %di, %dx
jb 2f
# cursor reaches bottom of screen; scroll it
subw %bx, %dx
xorw %di, %di
movw %bx, %si
cld
subw %bx, %cx
shrw %cx
pushw %cx
rep; es; movsw
movb $32, %al # clear bottom line characters
movb $80, %cl
rep; stosw
movw $0x2000, %di
popw %cx
leaw (%bx,%di), %si
rep; es; movsw
movb $0xe1, %al # clear bottom line attributes
movb $80, %cl
rep; stosw
2: movw %dx, %cs:cursor_address
movb $0x13, %ah # move cursor to right position
int $0x18
popw %es
popaw
ret
cursor_address:
.word 0
.word NORMAL_TEXT
max_cursor_offset:
.word 80 * 25 * 2 # for normal 80x25 mode
# putstr may called without running through start_of_setup (via bootsect_panic)
# so we should initialize ourselves on demand.
prtchr_initialized:
.byte 0
# This routine queries GDC (graphic display controller) for current cursor
# position. Cursor position is returned in %ax (CPU offset address).
get_cursor_position:
1: inb $0x60, %al
outb %al, $0x5f # delay
outb %al, $0x5f # delay
testb $0x04, %al # Is FIFO empty?
jz 1b # no -> wait until empty
movb $0xe0, %al # CSRR command
outb %al, $0x62 # command write
outb %al, $0x5f # delay
outb %al, $0x5f # delay
2: inb $0x60, %al
outb %al, $0x5f # delay
outb %al, $0x5f # delay
testb $0x01, %al # Is DATA READY?
jz 2b # no -> wait until ready
inb $0x62, %al # read xAD (L)
outb %al, $0x5f # delay
outb %al, $0x5f # delay
movb %al, %ah
inb $0x62, %al # read xAD (H)
outb %al, $0x5f # delay
outb %al, $0x5f # delay
xchgb %al, %ah # correct byte order
pushw %ax
inb $0x62, %al # read yAD (L)
outb %al, $0x5f # delay
outb %al, $0x5f # delay
inb $0x62, %al # read yAD (M)
outb %al, $0x5f # delay
outb %al, $0x5f # delay
inb $0x62, %al # read yAD (H)
# yAD is not our interest,
# so discard it.
popw %ax
addw %ax, %ax # convert to CPU address
ret
# Descriptor tables
#
# NOTE: The intel manual says gdt should be sixteen bytes aligned for
# efficiency reasons. However, there are machines which are known not
# to boot with misaligned GDTs, so alter this at your peril! If you alter
# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
# empty GDT entries (one for NULL and one reserved).
#
# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
# true for the Voyager Quad CPU card which will not boot without
# This directive. 16 byte aligment is recommended by intel.
#
.align 16
gdt:
.fill GDT_ENTRY_BOOT_CS,8,0
.word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
.word 0 # base address = 0
.word 0x9A00 # code read/exec
.word 0x00CF # granularity = 4096, 386
# (+5th nibble of limit)
.word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
.word 0 # base address = 0
.word 0x9200 # data read/write
.word 0x00CF # granularity = 4096, 386
# (+5th nibble of limit)
gdt_end:
.align 4
.word 0 # alignment byte
idt_48:
.word 0 # idt limit = 0
.word 0, 0 # idt base = 0L
.word 0 # alignment byte
gdt_48:
.word gdt_end - gdt - 1 # gdt limit
.word 0, 0 # gdt base (filled in later)
# Include video setup & detection code
#include "video.S"
# Setup signature -- must be last
setup_sig1: .word SIG1
setup_sig2: .word SIG2
# After this point, there is some free space which is used by the video mode
# handling code to store the temporary mode table (not used by the kernel).
modelist:
.text
endtext:
.data
enddata:
.bss
endbss:
/*
* $Id: build.c,v 1.5 1997/05/19 12:29:58 mj Exp $
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1997 Martin Mares
*/
/*
* This file builds a disk-image from three different files:
*
* - bootsect: exactly 512 bytes of 8086 machine code, loads the rest
* - setup: 8086 machine code, sets up system parm
* - system: 80386 code for actual system
*
* It does some checking that all files are of the correct type, and
* just writes the result to stdout, removing headers and padding to
* the right amount. It also writes some system data to stderr.
*/
/*
* Changes by tytso to allow root device specification
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
* Cross compiling fixes by Gertjan van Wingerde, July 1996
* Rewritten by Martin Mares, April 1997
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/boot.h>
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long u32;
#define DEFAULT_MAJOR_ROOT 0
#define DEFAULT_MINOR_ROOT 0
/* Minimal number of setup sectors (see also bootsect.S) */
#define SETUP_SECTS 4
byte buf[1024];
int fd;
int is_big_kernel;
void die(const char * str, ...)
{
va_list args;
va_start(args, str);
vfprintf(stderr, str, args);
fputc('\n', stderr);
exit(1);
}
void file_open(const char *name)
{
if ((fd = open(name, O_RDONLY, 0)) < 0)
die("Unable to open `%s': %m", name);
}
void usage(void)
{
die("Usage: build [-b] bootsect setup system [rootdev] [> image]");
}
int main(int argc, char ** argv)
{
unsigned int i, c, sz, setup_sectors;
u32 sys_size;
byte major_root, minor_root;
struct stat sb;
if (argc > 2 && !strcmp(argv[1], "-b"))
{
is_big_kernel = 1;
argc--, argv++;
}
if ((argc < 4) || (argc > 5))
usage();
if (argc > 4) {
if (!strcmp(argv[4], "CURRENT")) {
if (stat("/", &sb)) {
perror("/");
die("Couldn't stat /");
}
major_root = major(sb.st_dev);
minor_root = minor(sb.st_dev);
} else if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
die("Couldn't stat root device.");
}
major_root = major(sb.st_rdev);
minor_root = minor(sb.st_rdev);
} else {
major_root = 0;
minor_root = 0;
}
} else {
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
file_open(argv[1]);
i = read(fd, buf, sizeof(buf));
fprintf(stderr,"Boot sector %d bytes.\n",i);
if (i != 512)
die("Boot block must be exactly 512 bytes");
if (buf[510] != 0x55 || buf[511] != 0xaa)
die("Boot block hasn't got boot flag (0xAA55)");
buf[508] = minor_root;
buf[509] = major_root;
if (write(1, buf, 512) != 512)
die("Write call failed");
close (fd);
file_open(argv[2]); /* Copy the setup code */
for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c )
if (write(1, buf, c) != c)
die("Write call failed");
if (c != 0)
die("read-error on `setup'");
close (fd);
setup_sectors = (i + 511) / 512; /* Pad unused space with zeros */
if (!(setup_sectors & 1))
setup_sectors++; /* setup_sectors must be odd on NEC PC-9800 */
fprintf(stderr, "Setup is %d bytes.\n", i);
memset(buf, 0, sizeof(buf));
while (i < setup_sectors * 512) {
c = setup_sectors * 512 - i;
if (c > sizeof(buf))
c = sizeof(buf);
if (write(1, buf, c) != c)
die("Write call failed");
i += c;
}
file_open(argv[3]);
if (fstat (fd, &sb))
die("Unable to stat `%s': %m", argv[3]);
sz = sb.st_size;
fprintf (stderr, "System is %d kB\n", sz/1024);
sys_size = (sz + 15) / 16;
/* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */
if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE))
die("System is too big. Try using %smodules.",
is_big_kernel ? "" : "bzImage or ");
if (sys_size > 0xefff)
fprintf(stderr,"warning: kernel is too big for standalone boot "
"from floppy\n");
while (sz > 0) {
int l, n;
l = (sz > sizeof(buf)) ? sizeof(buf) : sz;
if ((n=read(fd, buf, l)) != l) {
if (n < 0)
die("Error reading %s: %m", argv[3]);
else
die("%s: Unexpected EOF", argv[3]);
}
if (write(1, buf, l) != l)
die("Write failed");
sz -= l;
}
close(fd);
if (lseek(1, 497, SEEK_SET) != 497) /* Write sizes to the bootsector */
die("Output: seek failed");
buf[0] = setup_sectors;
if (write(1, buf, 1) != 1)
die("Write of setup sector count failed");
if (lseek(1, 500, SEEK_SET) != 500)
die("Output: seek failed");
buf[0] = (sys_size & 0xff);
buf[1] = ((sys_size >> 8) & 0xff);
if (write(1, buf, 2) != 2)
die("Write of image length failed");
return 0; /* Everything is OK */
}
/* video.S
*
* Video mode setup, etc. for NEC PC-9800 series.
*
* Copyright (C) 1997,98,99 Linux/98 project <seraphim@kmc.kyoto-u.ac.jp>
*
* Based on the video.S for IBM PC:
* copyright (C) Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
/* Positions of various video parameters passed to the kernel */
/* (see also include/linux/tty.h) */
#define PARAM_CURSOR_POS 0x00
#define PARAM_VIDEO_PAGE 0x04
#define PARAM_VIDEO_MODE 0x06
#define PARAM_VIDEO_COLS 0x07
#define PARAM_VIDEO_EGA_BX 0x0a
#define PARAM_VIDEO_LINES 0x0e
#define PARAM_HAVE_VGA 0x0f
#define PARAM_FONT_POINTS 0x10
#define PARAM_VIDEO98_COMPAT 0x0a
#define PARAM_VIDEO98_HIRESO 0x0b
#define PARAM_VIDEO98_MACHTYPE 0x0c
#define PARAM_VIDEO98_LINES 0x0e
#define PARAM_VIDEO98_COLS 0x0f
# PARAM_LFB_* and PARAM_VESAPM_* are unused on PC-9800.
# This is the main entry point called by setup.S
# %ds *must* be pointing to the bootsector
video: xorw %ax, %ax
movw %ax, %es # %es = 0
movb %es:BIOS_FLAG, %al
movb %al, PARAM_VIDEO_MODE
movb $0, PARAM_VIDEO98_HIRESO # 0 = normal
movw $NORMAL_TEXT, PARAM_VIDEO_PAGE
testb $0x8, %al
movw $(80 * 256 + 25), %ax
jz 1f
# hireso machine.
movb $1, PARAM_VIDEO98_HIRESO # !0 = hi-reso
movb $(HIRESO_TEXT >> 8), PARAM_VIDEO_PAGE + 1
movw $(80 * 256 + 31), %ax
1: movw %ax, PARAM_VIDEO98_LINES # also sets VIDEO98_COLS
movb $0xc0, %ch # 400-line graphic mode
movb $0x42, %ah
int $0x18
movw $80, PARAM_VIDEO_COLS
movw $msg_probing, %si
call prtstr_cs
# Check vendor from font pattern of `A'...
1: inb $0x60, %al # wait V-sync
testb $0x20, %al
jnz 1b
2: inb $0x60, %al
testb $0x20, %al
jz 2b
movb $0x00, %al # select font of `A'
outb %al, $0xa1
movb $0x41, %al
outb %al, $0xa3
movw $8, %cx
movw PARAM_VIDEO_PAGE, %ax
cmpw $NORMAL_TEXT, %ax
je 3f
movb $24, %cl # for hi-reso machine
3: addw $0x400, %ax # %ax = CG window segment
pushw %ds
movw %ax, %ds
xorw %dx, %dx # get sum of `A' pattern...
xorw %si, %si
4: lodsw
addw %ax, %dx
loop 4b
popw %ds
movw %dx, %ax
movw $msg_nec, %si
xorw %bx, %bx # vendor info will go into %bx
testb $8, %es:BIOS_FLAG
jnz check_hireso_vendor
cmpw $0xc7f8, %ax
je 5f
jmp 6f
check_hireso_vendor:
cmpw $0x9639, %ax # XXX: NOT VERIFIED!!!
je 5f
6: incw %bx # compatible machine
movw $msg_compat, %si
5: movb %bl, PARAM_VIDEO98_COMPAT
call prtstr_cs
movw $msg_fontdata, %si
call prtstr_cs # " (CG sum of A = 0x"
movw %dx, %ax
call prthex
call prtstr_cs # ") PC-98"
movb $'0', %al
pushw %ds
pushw $0xf8e8
popw %ds
cmpw $0x2198, (0)
popw %ds
jne 7f
movb $'2', %al
7: call prtchr
call prtstr_cs # "1 "
movb $0, PARAM_VIDEO98_MACHTYPE
#if 0 /* XXX - This check is bogus? [0000:BIOS_FLAG2]-bit7 does NOT
indicate whether it is a note machine, but merely indicates
whether it has ``RAM drive''. */
# check note machine
testb $0x80, %es:BIOS_FLAG2
jnz is_note
pushw %ds
pushw $0xfd80
popw %ds
movb (4), %al
popw %ds
cmpb $0x20, %al # EPSON note A
je epson_note
cmpb $0x22, %al # EPSON note W
je epson_note
cmpb $0x27, %al # EPSON note AE
je epson_note
cmpb $0x2a, %al # EPSON note WR
jne note_done
epson_note:
movb $1, PARAM_VIDEO98_MACHTYPE
movw $msg_note, %si
call prtstr_cs
note_done:
#endif
# print h98 ? (only NEC)
cmpb $0, PARAM_VIDEO98_COMPAT
jnz 8f # not NEC -> not H98
testb $0x80, %es:BIOS_FLAG5
jz 8f # have NESA bus -> H98
movw $msg_h98, %si
call prtstr_cs
orb $2, PARAM_VIDEO98_MACHTYPE
8: testb $0x40, %es:BIOS_FLAG5
jz 9f
movw $msg_gs, %si
call prtstr_cs # only prints it :-)
9:
movw $msg_normal, %si # "normal"
testb $0x8, %es:BIOS_FLAG
jz 1f
movw $msg_hireso, %si
1: call prtstr_cs
movw $msg_sysclk, %si
call prtstr_cs
movb $'5', %al
testb $0x80, %es:BIOS_FLAG
jz 2f
movb $'8', %al
2: call prtchr
call prtstr_cs
#if 0
testb $0x40, %es:(0x45c)
jz no_30line # no 30-line support
movb %es:KB_SHFT_STS, %al
testb $0x01, %al # is SHIFT key pressed?
jz no_30line
testb $0x10, %al # is CTRL key pressed?
jnz line40
# switch to 30-line mode
movb $30, PARAM_VIDEO98_LINES
movw $msg_30line, %si
jmp 3f
line40:
movb $37, PARAM_VIDEO98_LINES
movw $40, PARAM_VIDEO_LINES
movw $msg_40line, %si
3: call prtstr_cs
movb $0x32, %bh
movw $0x300c, %ax
int $0x18 # switch video mode
movb $0x0c, %ah
int $0x18 # turn on text plane
movw %cs:cursor_address, %dx
movb $0x13, %ah
int $0x18 # move cursor to correct place
mov $0x11, %ah
int $0x18 # turn on text plane
call prtstr_cs # "Ok.\r\n"
no_30line:
#endif
ret
prtstr_cs:
pushw %ds
pushw %cs
popw %ds
call prtstr
popw %ds
ret
# prthex is for debugging purposes, and prints %ax in hexadecimal.
prthex: pushw %cx
movw $4, %cx
1: rolw $4, %ax
pushw %ax
andb $0xf, %al
cmpb $10, %al
sbbb $0x69, %al
das
call prtchr
popw %ax
loop 1b
popw %cx
ret
msg_probing: .string "Probing machine: "
msg_nec: .string "NEC"
msg_compat: .string "compatible"
msg_fontdata: .string " (CG sum of A = 0x"
.string ") PC-98"
.string "1 "
msg_gs: .string "(GS) "
msg_h98: .string "(H98) "
msg_normal: .string "normal"
msg_hireso: .string "Hi-reso"
msg_sysclk: .string " mode, system clock "
.string "MHz\r\n"
#if 0
msg_40line: # cpp will concat following lines, so the assembler can deal.
.ascii "\
Video mode will be adjusted to 37-line (so-called ``40-line'') mode later.\r\n\
THIS MODE MAY DAMAGE YOUR MONITOR PHYSICALLY. USE AT YOUR OWN RISK.\r\n"
msg_30line: .string "Switching video mode to 30-line (640x480) mode... "
.string "Ok.\r\n"
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment