/* $NetBSD: $ */

/*-
  * Copyright (c) 2009 UPMC/LIP6
  * All rights reserved.
  * This software is distributed under the following condiions
  * compliant with the NetBSD foundation policy.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */

#include "opt_cputype.h"
#include "opt_multiprocessor.h"
#include <sys/cdefs.h>

#include <mips/asm.h>
#include <mips/cpuregs.h>
#include <mips/trap.h>

#include <machine/bootinfo.h>

#include "assym.h"

#define _RELOC(x)       ((x) - KERNBASE)
#define RELOC(x)        _RELOC(_C_LABEL(x))

/* #define PUTC_ADDR	0xbfc00010 */
/* #define PUTC_ADDR	0x00000010 */

#ifdef PUTC_ADDR
#define PUTC(x) \
	la	k0, PUTC_ADDR; \
	lw	k0, 0(k0); \
	jalr	k0; \
	li	a0, x
#else
#define PUTC(x)
#endif

	.set	noreorder

	.text

	.globl	start
	.globl	_C_LABEL(kernel_text)
# void start(void *bi)
start:
_C_LABEL(kernel_text):
	move	s0, a0

	PUTC('k')

	mtc0	zero, MIPS_COP_0_STATUS	#disable interrupts
	COP0_SYNC
	PUTC('c')

#ifndef VCACHE
	li	t0, (VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0
#endif
	PUTC('s')

	/* intialize stack */
	la	sp, RELOC(tmpsk)
#ifdef __GP_SUPPORT__
	la	gp, RELOC(_gp)
#endif
	mfc0	t0, MIPS_COP_0_PRID #read product revision ID
	sw	t0, RELOC(mips_options)+MO_CPU_ID

	PUTC('m')

	# open-code memset(edata, 0, end - edata)
	# we can't call memset because the stack is not ready yet:
	# it's in the area to be zeroed out
	la	a0, RELOC(edata)
	la	a1, RELOC(end)
	subu	a1, a1, a0
	beq	a1, zero, 2f
	 li	a2, 4
1:
	sw	zero, 0(a0)
	subu	a1, a1, a2
	bne	a1, zero, 1b
	 addu	a0, a0, a2
2:
	PUTC('A')

	move	a1, s0
	la 	a0, RELOC(bootinfo)
	jal	_C_LABEL(memcpy) # memcpy(&bootinfo, bi, BOOTINFO_SIZE)
	li	a2, BOOTINFO_SIZE

	la 	a1, RELOC(bootinfo)
	jal	lookup_bootinfo
	la 	a0, BTINFO_CONS

	bne	v0, zero, 1f
	nop
	la	a0, nocons
	j	lopanic
	nop

1:
	lw	t1, BICONS_ADDR(v0)
	la	t0, RELOC(mtty_consolep)
	sw	t1, 0(t0)

	jal	early_putc
	li	a0, 'C'

	#build L1 PTE
	move	a1, zero
	la	a0, RELOC(kernelpd)
	jal	_C_LABEL(memset) # memset(kernelpd, 0, PAGE_SIZE * 2)
	li	a2, PAGE_SIZE * 2

	/* important registers
	 * s0: kernel mapping size (in page)
	 * s1: kernel virtual start
	 * s2: kernel physical start
	 * s3: kernelpd address
	 */
	la	s1, start
	li	t0, PTE1_FRAME
	and	s1, s1, t0 # align s1 to 2M
	la	s2, RELOC(start)
	la	s0, end
	subu	s0, s0, s1  # s0 - s1 -> s0 size of mapping
	li	t0, ((1 << PTE1_SHIFT) - 1)
	addu	s0, t0, s0 # round up
	srl	s0, s0, PTE1_SHIFT # number of pages
	jal	early_putc
	addiu	a0, s0, '0';
	la	s3, RELOC(kernelpd)
	move	a0, s2
	move	a1, s0
	move	a2, s2
	jal	pmap_bootstrap_map  # map(s2, s0, s2, s3)
	move	a3, s3
	
	jal	early_putc
	li	a0, 'x'
	move	a0, s2
	move	a1, s0
	move	a2, s1
	jal	pmap_bootstrap_map # map(s2, s0, s1, s3)
	move	a3, s3

	jal	early_putc
	li	a0, 'y'

	la	t0, RELOC(mtty_consolep)
	lw	a0, 0(t0)
	la	t0, RELOC(mtty_consolev)
	lw	a2, 0(t0)
	li	a1, 1
	jal	pmap_bootstrap_map # map(mtty_consolep, 1, mtty_consolev, s3)
	move	a3, s3

	jal	early_putc
	li	a0, 'B'

	/* enable MMU */
	la	a0, RELOC(kernelpd);
	srl	a0, a0, VC_PTPR_PASHIFT
	move	s1, a0
	jal	_C_LABEL(printx)
	nop
	mtc2	s1, $VC_PTPR, 0
	li	t0, (VC_TLB_EN_ITLB | VC_TLB_EN_DTLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0

	/* MMU running, jump to virtual address */
	la	t0, _C_LABEL(virtal)
	jr	t0
	nop
virtal:
	la	sp, tmpsk
#ifdef __GP_SUPPORT__
        la      gp, _gp
#endif
	/* now running in virtual mode, remove identity mapping */
	move	a0, zero
	move	a1, s0
	move	a2, s2
	la	a3, _C_LABEL(kernelpd)
	jal	pmap_bootstrap_map # pmap_bootstrap_map(0, s0, s2, &kernelpd)
	nop

	/* set curlwp and curcpu */
	la	MIPS_CURLWP, _C_LABEL(lwp0)
	la	t0, _C_LABEL(cpu_info_store)
	sw	MIPS_CURLWP, CPU_INFO_CURLWP(t0)
	mtc0	MIPS_CURLWP, MIPS_COP_0_TCCONTEXT
	sw	t0, L_CPU(MIPS_CURLWP)

	/* finish low-level init */
	jal mach_init # mach_init(void)
	nop

	/* switch to lwp0 stack now ? should do it before ? */
	lw      sp, L_PCB(MIPS_CURLWP)
	nop
	addu    sp, sp, USPACE - TF_SIZ - CALLFRAME_SIZ
	jal	_C_LABEL(main)	# main(void)
	nop
	PANIC("main() returned")

/* void early_putc(const char c)
 * simple putc routine for early debug
 */
	.globl	_C_LABEL(early_putc)
_C_LABEL(early_putc):
	la	k1, RELOC(mtty_consolep)
	lw	k1, 0(k1)
	bne	k1, zero, 1f
	nop
#ifdef PUTC_ADDR
	la	k1, PUTC_ADDR
	lw	k1, 0(k1)
	jr	k1
	nop
#endif
	jr ra
	nop
1:
	jr ra
	sw	a0, 0(k1)

/*
 * non-boot CPUs trampoline page. All CPUs are asked to jump there
 * (in physical mode) via a write to the appropriate XICU register.
 * Then the CPU switch to virual mode using the temporary PD in cputramp_args
 * and immediatly switch to proc0's PD. It then process with cpu_attach.
 */
	.align (PAGE_SHIFT)
	.globl	_C_LABEL(cputramp)
_C_LABEL(cputramp):
	mtc0	zero, MIPS_COP_0_STATUS	#disable interrupts
	COP0_SYNC
#ifdef __GP_SUPPORT__
	la	gp, RELOC(_gp)
#endif
	/* we've been woken up: boostrap, finish cpu init and go to idle loop */

	la	s0, RELOC(cputramp_args)
	/* enable MMU */
	lw	s1, CTR_BOOTPD(s0)
	srl	s1, s1, VC_PTPR_PASHIFT
	mtc2	s1, $VC_PTPR, 0
	li	a0, (VC_TLB_EN_ITLB | VC_TLB_EN_DTLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	a0, $VC_TLB_EN, 0
	nop

	/* MMU running, jump to virtual address */
	la	t0, _C_LABEL(secvirt)
	jr	t0
	nop
secvirt:
	jal	mttycn_putc # a0 unused
	li	a1, 'V'
	/* running at virtual; setup stack */
	la	s0, _C_LABEL(cputramp_args)
	lw	a0, CTR_STACK(s0)
	move	sp, a0
#ifdef __GP_SUPPORT__
        la      gp, _gp
#endif
	lw	a0, CTR_CI(s0)
	/* set curlwp to idle lwp */
	lw	MIPS_CURLWP, CPU_INFO_IDLELWP(a0)
	sw	MIPS_CURLWP, CPU_INFO_CURLWP(a0)
	mtc0	MIPS_CURLWP, MIPS_COP_0_TCCONTEXT
	/* finish CPU attach */
	jal	_C_LABEL(tsarmips_cpu_attach_common)
	nop
	/* init bottom of stack frame */
	move    ra, zero
	sw	ra, CALLFRAME_RA(sp)
	/* and go idle */
#ifdef MULTIPROCESSOR
	lw	a0, CTR_CI(s0)
	j	_C_LABEL(cpu_hatch)
#else
1:
	j	1b
#endif

	.set at
	.globl _C_LABEL(verylocore)
_C_LABEL(verylocore):

/*
 * u_int32_t mips_cp0_ebase_read(void)
 *
 *      Return the current value of the CP0 Ebase register.
 */
LEAF(mips_cp0_ebase_read)
        mfc0    v0, MIPS_COP_0_EBASE
	j       ra
	nop
END(mips_cp0_ebase_read)

/*
 * void mips_cp0_ebase_write(u_int32_t)
 * 
 *      Set the value of the CP0 Ebase register.
 *
 */
LEAF(mips_cp0_ebase_write)
        mtc0    a0, MIPS_COP_0_EBASE
        COP0_SYNC
        nop
        nop
        j       ra
        nop
END(mips_cp0_ebase_write)

/*
 * void tsarmips_memcpy_paddr(paddr_t dst, paddr_t src, size_t len)
 *
 *      memory to memory copy. assume line-aligned.
 */
LEAF(tsarmips_memcpy_paddr)
        move 	t0, a0
	or	t0, t0, a2
	lw	v0, 16(sp)	# load len in v0
	or	t0, t0, v0
        andi	t0, t0, 63
	bnez	t0, 6f		# is everything properly aligned ?
	 nop
	/* disable DTLB */
	li	t0, (VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0
1:
	/* load half a line */
	mtc2	a3, $VC_DATA_PADDR_EXT, 0
	lw	t0, 0(a2)
	lw	t1, 4(a2)
	lw	t2, 8(a2)
	lw	t3, 12(a2)
	lw	t4, 16(a2)
	lw	t5, 20(a2)
	lw	t6, 24(a2)
	lw	t7, 28(a2)
	/* and save it */
	mtc2	a1, $VC_DATA_PADDR_EXT, 0
	sw	t0, 0(a0)
	sw	t1, 4(a0)
	sw	t2, 8(a0)
	sw	t3, 12(a0)
	sw	t4, 16(a0)
	sw	t5, 20(a0)
	sw	t6, 24(a0)
	sw	t7, 28(a0)
	/* increment pointers */
	addiu	t0, a0, 32
	addiu	t2, a2, 32
	sltu	t6, t0, a0
	sltu	t7, t2, a2
	addu	a1, a1, t6
	addu	a3, a3, t7
	move	a0, t0
	addiu	v0, v0, -32
	bnez 	v0, 1b
	  move	a2, t2

	/* enable DTLB again */
	li	t0, (VC_TLB_EN_ITLB | VC_TLB_EN_DTLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0
	jr      ra
	 nop
6:
	la	a0, tsarmips_memcpy_paddr_str
	la	t0, panic
	jr	t0
	 nop
END(tsarmips_memcpy_paddr)

/*
 * void tsarmips_memcpy_paddr2vaddr(vaddr_t dst, paddr_t src, size_t len)
 *
 *      memory to memory copy. assume line-aligned.
 */
LEAF(tsarmips_memcpy_paddr2vaddr)
	lw	v0, 16(sp)	# load len in v0
        move 	t0, a0
	or	t0, t0, a2
	or	t0, t0, v0
        andi	t0, t0, 63
	bnez	t0, 6f		# is everything properly aligned ?
	 nop
	li	v1, (VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	li	t9, (VC_TLB_EN_DTLB | VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)

1:
	/* disable DTLB */
	mtc2	v1, $VC_TLB_EN, 0
	/* load half a line */
	mtc2	a3, $VC_DATA_PADDR_EXT, 0
	lw	t0, 0(a2)
	lw	t1, 4(a2)
	lw	t2, 8(a2)
	lw	t3, 12(a2)
	lw	t4, 16(a2)
	lw	t5, 20(a2)
	lw	t6, 24(a2)
	lw	t7, 28(a2)
	/* enable DTLB */
	mtc2	t9, $VC_TLB_EN, 0
	/* and save it */
	sw	t0, 0(a0)
	sw	t1, 4(a0)
	sw	t2, 8(a0)
	sw	t3, 12(a0)
	sw	t4, 16(a0)
	sw	t5, 20(a0)
	sw	t6, 24(a0)
	sw	t7, 28(a0)
	/* increment pointers */
	addiu	t2, a2, 32
	addiu	a0, a0, 32
	sltu	t7, t2, a2
	addu	a3, a3, t7
	addiu	v0, v0, -32
	bnez 	v0, 1b
	  move	a2, t2

	jr      ra
	 nop
6:
	la	a0, tsarmips_memcpy_paddr2vaddr_str
	la	t0, panic
	jr	t0
	 nop
END(tsarmips_memcpy_paddr2vaddr)

/*
 * void tsarmips_memcpy_vaddr2paddr(paddr_t dst, vaddr_t src, size_t len)
 *
 *      memory to memory copy. assume line-aligned.
 */
LEAF(tsarmips_memcpy_vaddr2paddr)
        move 	t0, a0
	or	t0, t0, a2
	or	t0, t0, a3
        andi	t0, t0, 63
	bnez	t0, 6f		# is everything properly aligned ?
	 nop
	li	v1, (VC_TLB_EN_DTLB | VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	li	t9, (VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
1:
	/* enable DTLB */
	mtc2	v1, $VC_TLB_EN, 0
	/* load half a line */
	lw	t0, 0(a2)
	lw	t1, 4(a2)
	lw	t2, 8(a2)
	lw	t3, 12(a2)
	lw	t4, 16(a2)
	lw	t5, 20(a2)
	lw	t6, 24(a2)
	lw	t7, 28(a2)
	/* disable DTLB */
	mtc2	t9, $VC_TLB_EN, 0
	/* and save it */
	mtc2	a1, $VC_DATA_PADDR_EXT, 0
	sw	t0, 0(a0)
	sw	t1, 4(a0)
	sw	t2, 8(a0)
	sw	t3, 12(a0)
	sw	t4, 16(a0)
	sw	t5, 20(a0)
	sw	t6, 24(a0)
	sw	t7, 28(a0)
	/* increment pointers */
	addiu	t0, a0, 32
	addiu	a2, a2, 32
	sltu	t6, t0, a0
	addu	a1, a1, t6
	addiu	a3, a3, -32
	bnez 	a3, 1b
	 move	a0, t0

	/* enable DTLB again and return */
	jr      ra
	 mtc2	v1, $VC_TLB_EN, 0
6:
	la	a0, tsarmips_memcpy_vaddr2paddr_str
	la	t0, panic
	jr	t0
	 nop
END(tsarmips_memcpy_vaddr2paddr)

/*
 * void tsarmips_memset_paddr(paddr_t src, int val, size_t len)
 *
 *      memory set. assume line-aligned.
 */
LEAF(tsarmips_memset_paddr)
        move 	t0, a0
	or	t0, t0, a3
        andi	t0, t0, 63
	bnez	t0, 6f		# is everything properly aligned ?
	 nop
	/* disable DTLB */
	li	t0, (VC_TLB_EN_ITLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0
1:
	/* store a full line */
	mtc2	a1, $VC_DATA_PADDR_EXT, 0
	sw	a2, 0(a0)
	sw	a2, 4(a0)
	sw	a2, 8(a0)
	sw	a2, 12(a0)
	sw	a2, 16(a0)
	sw	a2, 20(a0)
	sw	a2, 24(a0)
	sw	a2, 28(a0)
	sw	a2, 32(a0)
	sw	a2, 36(a0)
	sw	a2, 40(a0)
	sw	a2, 44(a0)
	sw	a2, 48(a0)
	sw	a2, 52(a0)
	sw	a2, 56(a0)
	sw	a2, 60(a0)
	/* increment pointers */
	addiu	t0, a0, 64
	sltu	t6, t0, a0
	addiu	a3, a3, -64
	addu	a1, a1, t6
	bnez 	a3, 1b
	 move	a0, t0

	/* enable DTLB again */
	li	t0, (VC_TLB_EN_ITLB | VC_TLB_EN_DTLB | VC_TLB_EN_ICACHE | VC_TLB_EN_DCACHE)
	mtc2	t0, $VC_TLB_EN, 0
	j       ra
	 nop
6:
	la	a0, tsarmips_memset_paddr_str
	la	t0, panic
	jr	t0
	 nop
END(tsarmips_memset_paddr)

nocons:	.asciiz	"no console info"
tsarmips_memcpy_paddr_str:
	.asciiz "tsarmips_memcpy_paddr: not aligned"
tsarmips_memcpy_paddr2vaddr_str:
	.asciiz "tsarmips_memcpy_paddr2vaddr: not aligned"
tsarmips_memcpy_vaddr2paddr_str:
	.asciiz "tsarmips_memcpy_vaddr2paddr: not aligned"
tsarmips_memset_paddr_str:
	.asciiz "tsarmips_memset_paddr: not aligned"

	.sdata
	.globl  _C_LABEL(esym)
_C_LABEL(esym):
	.word 0

	.bss
        .space 508
tmpsk:  .space 4
	.globl _C_LABEL(bootinfo)
_C_LABEL(bootinfo):
	.space BOOTINFO_SIZE

	.globl _C_LABEL(kernelpd)
	.align (PAGE_SHIFT + 1)
_C_LABEL(kernelpd):
	.space PAGE_SIZE * 2
