/*
 * Copyright (C) 2014-2018 Daniel Rossier <daniel.rossier@heig-vd.ch>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <generated/asm-offsets.h>

#include <avz/domain.h>

#include <asm/mmu.h>
#include <asm/processor.h>

.extern hypervisor_stack

.align  5
ENTRY(ret_from_hypercall)

	cpsid	i

	@ Preserve the return value in the stack frame of the domain (G-stack)
	curdom	r8, r9
	ldr		r9, [r8, #OFFSET_G_SP]
	str		r0, [r9, #OFFSET_R0]

	b	ret_to_user

.align  5
@ Hypercall generated by the domain
ENTRY(hypercall_entry)

	disable_irq

	tst		sp, #0x7	@ 8-bytes aligned
	strne	sp, [sp, #(OFFSET_SP-SVC_STACK_FRAME_SIZE - 4)]	@ save sp
	subne	sp, sp, #4
	streq	sp, [sp, #(OFFSET_SP-SVC_STACK_FRAME_SIZE)]	@ save sp

	@ Alignment guard
	tst		sp, #0x7		@ 8-bytes aligned
	bne		__stack_alignment_fault

	sub		sp, sp, #SVC_STACK_FRAME_SIZE

	stmia	sp, {r0 - r12}

	str		lr, [sp, #OFFSET_PC]
	str		lr, [sp, #OFFSET_LR]
	str		r11, [sp, #OFFSET_PSR]  		  @ r11 was set by the guest before trampoline

	@ Switch from G-stack to H-stack
	current_cpu r10

	ldr		r8, .LCpseudo_usr_mode
	mov		r9, #0
	str		r9, [r8, r10, lsl #2]

	mov		r8, sp
	ldr 	r9, .LChypervisor_stack   @ Get the running hypervisor SVC stack
	ldr		sp, [r9, r10, lsl #2]

	curdom	r9, r10
	str		r8, [r9, #OFFSET_G_SP]


process_hypercalls:

	adr	lr, ret_from_hypercall
	adr	tbl, hypercall_table
	add	tbl, tbl, scno, lsl #2

	ldr	pc, [tbl]

	@ r0 contains the return value of the hypercall

@ This is the entry point in case an hypercall is issued from the hypervisor
__do_hypercall_from_avz:

	adr lr, __end_hypercall_from_avz

	adr	tbl, hypercall_table
	add	tbl, tbl, scno, lsl #2

	ldr	pc, [tbl]

	@ r0 contains the return value of the hypercall

@ Fast return path to the caller (in avz)
__end_hypercall_from_avz:

	@ Load and skip r0 since it has the return value
	ldr		lr, [sp, #OFFSET_PC]
	add		sp, sp, #4

	ldmia	sp, {r1 - r12}
	add		sp, sp, #SVC_STACK_FRAME_SIZE

	mov		pc, lr

/*
 * hypercall_table
 */
.type hypercall_table, #object
ENTRY(hypercall_table)

__hypercall_start:
	.long do_event_channel_op
	.long do_console_io
	.long do_physdev_op
	.long do_sched_op
	.long do_domctl
	.long do_soo_hypercall

__hypercall_end:

.LCpseudo_usr_mode:
	.word	pseudo_usr_mode

.LChypervisor_stack:
	.word	hypervisor_stack


