/*
 * kexec: Linux boots Linux
 *
 * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xmission.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation (version 2 of the License).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#if 1
#define TTYS0_BASE	0x3f8
#define TTYS0_RBR (TTYS0_BASE+0x00)
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_LSR (TTYS0_BASE+0x05)

	
	/* uses:	ax, dx */
#define TTYS0_TX_AL		\
	mov	%al, %ah	; \
9:	mov	$TTYS0_LSR, %dx	; \
	inb	%dx, %al	; \
	test	$0x20, %al	; \
	je	9b		; \
	mov	$TTYS0_TBR, %dx	; \
	mov	%ah, %al	; \
	outb	%al, %dx	; \
9:	mov	$TTYS0_LSR, %dx	; \
	inb	%dx, %al	; \
	test	$0x40, %al	; \
	jz	9b
	


	/* uses:	ax, dx */
#define TTYS0_TX_CHAR(byte)	\
	mov	byte, %al	; \
	TTYS0_TX_AL

	/* uses:	ax, dx */
#define TTYS0_TX_HEX32(lword)	\
	mov	lword, %eax	; \
	shr	$28, %eax	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$24, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$20, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$16, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$12, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$8, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$4, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL


#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ;  TTYS0_TX_CHAR($'\r') ;  TTYS0_TX_CHAR($'\n')
#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x) ;  TTYS0_TX_CHAR($'\r') ;  TTYS0_TX_CHAR($'\n')
#else
#define DEBUG_CHAR(x)
#define DEBUG_TX_HEX32(x)
#endif
	
#undef i386	
	.text
	.globl entry16, entry16_regs
	.arch i386
	.balign 16
entry16:
	.code32

DEBUG_CHAR('a')
	/* Setup the classic BIOS interrupt table at 0x0 */
	lidt	idtptr

DEBUG_CHAR('b')
	/* Provide us with 16bit segments that we can use */
	lgdt	gdt

DEBUG_CHAR('c')
	/* Note we don't disable the a20 line, (this shouldn't be required)
	 * The code to do it is in kexec_test and it is a real pain.
	 * I will worry about that when I need it.
	 */
	
	/* Load 16bit data segments, to ensure the segment limits are set */
	movl	$0x10, %eax
	movl	%eax, %ds
	movl	%eax, %es
	movl	%eax, %ss
	movl	%eax, %fs
	movl	%eax, %gs

DEBUG_CHAR('d')

	/* switch to 16bit mode */
	ljmp	$0x08, $1f - entry16
1:
	.code16
DEBUG_CHAR('e')
	/* Disable Paging and protected mode */
	/* clear the PG & PE bits of CR0 */
	movl	%cr0,%eax
	andl	$~((1 << 31)|(1<<0)),%eax
	movl	%eax,%cr0

DEBUG_CHAR('f')
	/* make intersegment jmp to flush the processor pipeline
	 * and reload %cs:%eip (to clear upper 16 bits of %eip).
	 */
	ljmp	*(realptr - entry16)
3:
DEBUG_CHAR('g')
	/* we are in real mode now
	 * set up the real mode segment registers : %ds, $ss, %es
	 */
	/* Setup the data segment */
	movw	%cs, %ax
	movw	%ax, %ds

DEBUG_CHAR('h')
	/* Load the registers */
	movl	eax - entry16, %eax
	movl	ebx - entry16, %ebx
	movl	ecx - entry16, %ecx
	movl	edx - entry16, %edx
	movl	esi - entry16, %esi
	movl	edi - entry16, %esi
	movl	esp - entry16, %esp
	movl	ebp - entry16, %ebp
	movw	es  - entry16, %es
	movw	ss  - entry16, %ss
	movw	fs  - entry16, %fs
	movw	gs  - entry16, %gs
	movw	ds  - entry16, %ds

	/* Jump to the kernel entrypoint */
	ljmp	%cs:*(realdest - entry16)

	.balign 4
entry16_regs:	
eax:	.long	0x00000000
ebx:	.long	0x00000000
ecx:	.long	0x00000000
edx:	.long	0x00000000
esi:	.long	0x00000000
edi:	.long	0x00000000
esp:	.long	0x00000000
ebp:	.long	0x00000000
ds:	.word	0x0000
es:	.word	0x0000
ss:	.word	0x0000
fs:	.word	0x0000
gs:	.word	0x0000
realdest:
ip:	.word	0x0000
cs:	.word	0x0000
	.size entry16_regs, . - entry16_regs	

	.balign 16
realptr:		
	.word	3b - entry16
	.word	0x0000
	
	.data
	.balign 4

idtptr:
	/* 256 entry idt at 0 */
	.word	0x400 - 1
	.word	0, 0

gdt:
	/* 0x00 unusable segment so used as the gdt ptr */
	.word gdt_end - gdt - 1
	.long gdt
	.word 0

	/* 0x08 16 bit real mode code segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x9b, 0x00, 0x00

	/* 0x10 16 bit real mode data segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x93, 0x00, 0x00
gdt_end:
/*
 * kexec: Linux boots Linux
 *
 * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xmission.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation (version 2 of the License).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#if 1
#define TTYS0_BASE	0x3f8
#define TTYS0_RBR (TTYS0_BASE+0x00)
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_LSR (TTYS0_BASE+0x05)

	
	/* uses:	ax, dx */
#define TTYS0_TX_AL		\
	mov	%al, %ah	; \
9:	mov	$TTYS0_LSR, %dx	; \
	inb	%dx, %al	; \
	test	$0x20, %al	; \
	je	9b		; \
	mov	$TTYS0_TBR, %dx	; \
	mov	%ah, %al	; \
	outb	%al, %dx	; \
9:	mov	$TTYS0_LSR, %dx	; \
	inb	%dx, %al	; \
	test	$0x40, %al	; \
	jz	9b
	


	/* uses:	ax, dx */
#define TTYS0_TX_CHAR(byte)	\
	mov	byte, %al	; \
	TTYS0_TX_AL

	/* uses:	ax, dx */
#define TTYS0_TX_HEX32(lword)	\
	mov	lword, %eax	; \
	shr	$28, %eax	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$24, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$20, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$16, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$12, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$8, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	shr	$4, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL		; \
				; \
	mov	lword, %eax	; \
	and	$0x0f, %al	; \
	add	$'0', %al	; \
	cmp	$'9', %al	; \
	jle	9f		; \
	add	$39, %al	; \
9:				; \
	TTYS0_TX_AL


#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ;  TTYS0_TX_CHAR($'\r') ;  TTYS0_TX_CHAR($'\n')
#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x) ;  TTYS0_TX_CHAR($'\r') ;  TTYS0_TX_CHAR($'\n')
#else
#define DEBUG_CHAR(x)
#define DEBUG_TX_HEX32(x)
#endif

.data
	.globl setup16_debug_start, setup16_debug_end, setup16_debug_size, setup16_debug_align
	.globl setup16_debug_regs
	.globl setup16_debug_kernel_pre_protected
	.globl setup16_debug_first_code32
	.globl setup16_debug_old_code32
setup16_debug_start:
_reloc = .
	.balign 16
	.code32
DEBUG_CHAR('a')
	/* Compute where I am running at */
	call	1f
1:	popl	%ebx
	subl	$(1b - _reloc), %ebx

	/* Remember where I am running at */
	movl	%ebx, location - _reloc(%ebx)
	
DEBUG_CHAR('b')
	/* Fixup my real mode segment */
	movl	%ebx, %eax
	shrl	$4, %eax
	movw	%ax, 2 + realptr - _reloc(%ebx)

DEBUG_CHAR('c')
	/* Fixup the gdt */
	movl	%ebx, %eax
	shll	$16, %eax
	
	movl	%ebx, %ecx
	shrl	$16, %ecx
	andl	$0xff, %ecx

	movl	%ebx, %edx
	andl	$0xff000000, %edx
	orl	%edx, %ecx
	
	addl	%ebx, gdtaddr - _reloc(%ebx)
	addl	%ebx, debug_gdtaddr - _reloc(%ebx)
	orl	%eax, 0x08 + gdt - _reloc(%ebx)
	orl	%ecx, 0x0c + gdt - _reloc(%ebx)
	orl	%eax, 0x10 + gdt - _reloc(%ebx)
	orl	%ecx, 0x14 + gdt - _reloc(%ebx)


	
DEBUG_CHAR('d')
	/* Setup the classic BIOS interrupt table at 0x0 */
	lidt	idtptr - _reloc(%ebx)
	
	/* Provide us with 16bit segments that we can use */
	lgdt	gdtptr - _reloc(%ebx)

	/* Note we don't disable the a20 line, (this shouldn't be required)
	 * The code to do it is in kexec_test and it is a real pain.
	 * I will worry about that when I need it.
	 */
	
	/* Load 16bit data segments, to ensure the segment limits are set */
	movl	$0x10, %eax
	movl	%eax, %ds
	movl	%eax, %es
	movl	%eax, %ss
	movl	%eax, %fs
	movl	%eax, %gs

	/* switch to 16bit mode */
	
	ljmp	$0x08, $2f - _reloc
2:
	.code16
DEBUG_CHAR('e')
	/* Disable Paging and protected mode */
	/* clear the PG & PE bits of CR0 */
	movl	%cr0,%eax
	andl	$~((1 << 31)|(1<<0)),%eax
	movl	%eax,%cr0

DEBUG_CHAR('f')
	/* make intersegment jmp to flush the processor pipeline
	 * and reload %cs:%eip (to clear upper 16 bits of %eip).
	 */
	ljmp	*(realptr - _reloc)
3:
DEBUG_CHAR('g')
	/* we are in real mode now
	 * set up the real mode segment registers : %ds, $ss, %es
	 */
	/* Setup the data segment */
	movw	%cs, %ax
	movw	%ax, %ds

DEBUG_CHAR('h')
	/* Load the registers */
	movl	eax - _reloc, %eax
	movl	ebx - _reloc, %ebx
	movl	ecx - _reloc, %ecx
	movl	edx - _reloc, %edx
	movl	esi - _reloc, %esi
	movl	edi - _reloc, %esi
	movl	esp - _reloc, %esp
	movl	ebp - _reloc, %ebp
	movw	es - _reloc, %es
	movw	ss - _reloc, %ss
	movw	fs - _reloc, %fs
	movw	gs - _reloc, %gs
	movw	ds - _reloc, %ds

	/* Jump to the kernel entrypoint */
	ljmp	%cs:*(realdest - _reloc)

setup16_debug_regs:	
eax:	.long	0x00000000
ebx:	.long	0x00000000
ecx:	.long	0x00000000
edx:	.long	0x00000000
esi:	.long	0x00000000
edi:	.long	0x00000000
esp:	.long	0x00000000
ebp:	.long	0x00000000
ds:	.word	0x0000
es:	.word	0x0000
ss:	.word	0x0000
fs:	.word	0x0000
gs:	.word	0x0000
realdest:
ip:	.word	0x0000
cs:	.word	0x0000

	.balign 16
realptr:		
	.word	3b - _reloc
	.word	0x0000
	
idtptr:
	/* 256 entry idt at 0 */
	.word	0x400 - 1
	.word	0, 0

gdtptr:
	.word	gdt_end - gdt - 1
gdtaddr:
	.long	gdt - _reloc

gdt:
	/* dummy */
	.word 0, 0, 0, 0

	/* 16 bit real mode code segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x9b, 0x00, 0x00

	/* 16 bit real mode data segment */
	.word	0xffff, 0x0000
	.byte	0x00, 0x93, 0x00, 0x00
gdt_end:

debug_gdt:
	/* 0x00 */
	.word	debug_gdt_end - debug_gdt - 1
debug_gdtaddr:	
	.long	debug_gdt - _reloc
	.word 0

	/* 0x08 */
	.word	0, 0, 0, 0	/* Nothing in the first gdt entry */

	/* 0x10 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x18 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF


	/* 0x20 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x28 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF

	
	/* 0x30 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x38 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF

	
	/* 0x40 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x48 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF

	
	/* 0x50 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x58 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF

	
	/* 0x60 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
	.word	0xFFFF,	0x00, 0x9A00, 0x00CF
	/* 0x68 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
	.word	0xFFFF, 0x0000, 0x9200, 0x00CF
debug_gdt_end:
	
		
setup16_debug_kernel_pre_protected:
	.code16
	DEBUG_CHAR('i')
	cli					# no interrupts allowed !
	movb	$0x80, %al			# disable NMI for bootup
						# sequence
	outb	%al, $0x70
	lret
setup16_debug_first_code32:
	.code32
	.byte	0xbf	/* movl $0x12345678, %edi */
location:
	.long	0x12345678
	DEBUG_CHAR('j')
	.byte	0xb8	/* movl $0x10000, %eax */
setup16_debug_old_code32:
	.long	0x10000
	jmp	%eax
setup16_debug_end:
setup16_debug_size:
	.long setup16_debug_end - setup16_debug_start
setup16_debug_align:
	.long 16
