/******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/

#include <rtas.h>		
			
/*
Function:	
	Input:
		r3:   Destination to copy rtas code to
		r4:   Configuration	
	Output:		
		r3:   Entry point for rtas calls	
Decription: Called by OpenFirmware to instantiate rtas, needs to copy
	itself to destination, also do a relocations.
	
*/

.extern	rtas_entry
.extern .stack
.extern _got
.extern _got_end
.extern __bss_start
.extern __bss_end
.extern rtas_config


	.section        ".rtasstart","ax";
	.align	3
	.globl _rtas_start
_rtas_start:
	mflr	r10		# save link register
	bcl	20,31,.over	# branch (always) to .over 

.base:
	.align  3

/* Our Open Firmware needs to know the size of the RTAS binary and the
 * size & address of the RTAS function jump table. SLOF always looks for this
 * information in the following three quads here at the very beginning of the
 * RTAS binary at offset 8. So DO NOT DELETE/MOVE them! */

._rtas_size:		.quad	_rtas_end-_rtas_start
._ptr_to_func_tab:	.quad	rtas_func_tab-_rtas_start
._ptr_to_func_tab_size:	.quad	rtas_func_tab_size-_rtas_start

/* The other variables are not accessed by SLOF anymore: */

._rel_offset:		.quad   _reloc_table_start-_rtas_start
._rel_end_offset:	.quad   _reloc_table_end-_rtas_start
._bss_offset:		.quad   __bss_start-_rtas_start
._bss_end_offset:	.quad   __bss_end-_rtas_start
._rtas_entry_offset:	.quad   rtas_entry-_rtas_start
._rtas_config_offset:	.quad   rtas_config-_rtas_start
._rtas_stack:	        .quad   .stack-_rtas_start+RTAS_STACKSIZE-0x60
._rtas_toc:	        .quad   _got-_rtas_start

.over:	
	mflr r8			# gpr 8 is the base
	addi r8,r8,_rtas_start-.base # points to _rtas_start
	mr r11,r4		# Save config value	
	
# Copy rtas code
	
	ld r5,._rtas_size-_rtas_start(r8) 
	mr r4,r8		# Start of rtas
	addi r6,r3,-8		# Destination
	addi r4,r4,-8		# Source
	srdi r5,r5,3		# Count in quads
	mtctr r5
0:				
	ldu r0,8(r4)		
	stdu r0,8(r6)
	bdnz 0b		

# Clear bss

	ld r4,._bss_offset-_rtas_start(r8)
	ld r5,._bss_end_offset-_rtas_start(r8)
	li r0,0
	add r6,r3,r4		# Address bss in copied code
	addi r6,r6,-8
	sub r5,r5,r4		# Calculate bss size
	srdi r5,r5,3		# Count in quads
	mtctr r5	
0:	
	stdu r0,8(r6)
	bdnz 0b

# Relocate got

	ld	r4, ._rel_offset-_rtas_start(r8)
	ld	r5, ._rel_end_offset-_rtas_start(r8)
	sub	r5, r5,r4	# Calculate reloc table size
	cmpdi	r5, 0		# No reloc table ?
	beq	1f

	add	r4, r4, r3	# Calculate reloc table address
	addi	r4, r4, -4
	srdi	r5, r5, 2	# Count in words	
	mtctr	r5
0:	
	lwzu	r6, 4(r4)	# Load offset out of reloc table
	ldx	r0, r6, r3	# Load value 	
	add	r0, r0, r3	# Add relocation offset = load address
	stdx	r0, r6, r3
	bdnz	0b		
1:			

# Save config data

	ld r5,._rtas_config_offset-_rtas_start(r8)
	add r5,r5,r3
	std r11,0(r5)
	
# Flush to memory
	
	mr r4,r3		# Destination address
	ld r5,._rtas_size-_rtas_start(r8) 
		
	add r5,r5,r4
	addi r5,r5,127
	rlwinm r4,r4,0,0,24
	rlwinm r5,r5,0,0,24
	sub r5,r5,r4
	srwi r5,r5,7
	mtctr r5
0:
	dcbst 0,r4
	sync
	icbi 0,r4
	sync
	isync
	addi r4,r4,128
	bdnz 0b

# Call init function
	mfmsr	r11			# Switch to 64 bit mode
	mr	r7,r11
	rotldi	r11,r11,1
	ori	r11,r11,1
	rotldi	r11,r11,63
	mtmsrd	r11
	isync
	mr	r9,r1			# save old stack pointer
	ld	r1,._rtas_stack-_rtas_start(r8)	# load new stack pointer
	add	r1,r1,r3
	std	r9,0(r1)		# save stack pointer
	std	r2,64(r1)		# save toc
	std	r7,72(r1)		# save old msr value

	ld	r2,._rtas_toc-_rtas_start(r8)	# load got pointer
	add	r2,r2,r3

	bl	save_regs_r3_r12
	bl	.rtas_init
	bl	restore_regs_r3_r12

	ld	r11,72(r1)		# restore msr value	
	ld	r2,64(r1)		# restore toc
	ld	r1,0(r1)		# get old stack

	mtmsrd	r11			# restore msr
	isync


# Return rtas entry

	ld r4,._rtas_entry_offset-_rtas_start(r8)
	add r3,r3,r4
	mtlr	r10
	blr