/*
 * Copyright (C) 2015, Intel Corporation. All rights reserved.
 *
 * 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.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
 * COPYRIGHT HOLDER 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.
 */

#ifndef CPU_X86_MM_STACKS_H_
#define CPU_X86_MM_STACKS_H_

#include "prot-domains.h"

#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__NONE
#define STACKS_SIZE_INT 0
#else
/**
 * The necessary amount of space for the interrupt and exception stacks is
 * determined by the amount of data pushed on the stack by the CPU when
 * delivering an interrupt or exception, and by the additional data pushed
 * on the stack by the interrupt dispatcher.  See interrupt.h for more details.
 */
#define STACKS_SIZE_INT (14 * 4)
#endif

#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__PAGING
/**
 * The system call and return dispatchers use this stack, so its size was
 * determined by observing their behavior.  It is possible that the dispatchers
 * could overflow the stack and overwrite data on the other stacks.  An
 * alternative design that would facilitate detection of such overflows would
 * place the exception handler stack on a separate page surrounded by guard
 * bands, but that would consume a substantial amount of additional memory.
 *
 * All stack sizes should be a multiple of 4 to accommodate a 4-byte alignment.
 */
#ifdef __clang__
#define STACKS_SIZE_EXC 512
#else
#define STACKS_SIZE_EXC 256
#endif
#elif X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__SWSEG
#ifdef __clang__
#define STACKS_SIZE_EXC 512
#else
#define STACKS_SIZE_EXC 256
#endif
#elif X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__TSS
/**
 * This should be large enough to execute the exception handler with the
 * largest stack requirement: double_fault_handler:
 *  - 1 word for the return address from calling double_fault_handler
 *  - 1 word for the saved frame pointer in double_fault_handler
 *  - 2 words that GCC has been observed to skip on the stack to align it
 *    to a preferred boundary
 *  - 1 word for the return address for calling halt
 */
#define STACKS_SIZE_EXC (STACKS_SIZE_INT + (6 * 4))
#else
#define STACKS_SIZE_EXC STACKS_SIZE_INT
#endif
/**
 * The combined size of the stacks should be an even multiple of the 4K page
 * size so that they precisely fill some number of pages when paging-based
 * protection domains are in use.  The stacks are arranged contiguously by
 * the linker scripts.  See those and README.md for more details.
 */
#define STACKS_SIZE_MAIN (8192 - (STACKS_SIZE_INT + STACKS_SIZE_EXC))

#if !__ASSEMBLER__
/**
 * Stack for exception handlers.  Also used for system call and return
 * dispatchers when paging-based protection domains are enabled.
 */
extern uint8_t stacks_exc[STACKS_SIZE_EXC];
/** Stack for interrupt handlers. */
extern uint8_t stacks_int[STACKS_SIZE_INT];
/** Main C stack. */
extern uint8_t stacks_main[STACKS_SIZE_MAIN];

#define STACKS_INIT_TOP \
  ((uintptr_t)stacks_main + STACKS_SIZE_MAIN - \
   (PROT_DOMAINS_INIT_RET_ADDR_CNT * sizeof(uintptr_t)))

#endif

#endif /* CPU_X86_MM_STACKS_H_ */