/* Real-mode interface
 */

#ifndef ASSEMBLY

#include "etherboot.h"
#include "segoff.h"

typedef union {
	struct {
		union {
			uint8_t l;
			uint8_t byte;
		};
		uint8_t h;
	} PACKED;
	uint16_t word;
} PACKED reg16_t;

typedef union {
	reg16_t w;
	uint32_t dword;
} PACKED reg32_t;

/* Macros to help with defining inline real-mode trampoline fragments.
 */
#ifdef VBOX
#ifdef __WIN32__
/* Switching to previous section is only supported with ELF format. */
#define RM_SWITCH_PREV_SEGMENT ".section .text"
#else /* __WIN32__ */
#define RM_SWITCH_PREV_SEGMENT ".previous"
#endif /* __WIN32__ */
#else /* !VBOX */
#define RM_XSTR(x) #x	/* Macro hackery needed to stringify       */
#define RM_STR(x) RM_XSTR(x)
#endif /* !VBOX */
#ifdef VBOX
#define	RM_FRAGMENT(name, asm_code_str)		\
	extern void name ( void );		\
	extern void name ## _end (void);	\
	__asm__(				\
		".section .text16\n\t"	\
		".code16\n\t"			\
		".arch i386\n\t"		\
		".globl " STRINGIFY(GSYM(name)) " \n\t"		\
		STRINGIFY(GSYM(name)) ":\n\t"			\
		asm_code_str "\n\t"		\
		".globl " STRINGIFY(GSYM(name ## _end)) "\n\t"	\
		STRINGIFY(GSYM(name ## _end)) ":\n\t"		\
		".code32\n\t"			\
		RM_SWITCH_PREV_SEGMENT "\n\t"			\
	)
#else /* !VBOX */
#define	RM_FRAGMENT(name, asm_code_str)		\
	extern void name ( void );		\
	extern void name ## _end (void);	\
	__asm__(				\
		".section \".text16\"\n\t"	\
		".code16\n\t"			\
		".arch i386\n\t"		\
		".globl " #name " \n\t"		\
		#name ":\n\t"			\
		asm_code_str "\n\t"		\
		".globl " #name "_end\n\t"	\
		#name "_end:\n\t"		\
		".code32\n\t"			\
		".previous\n\t"			\
	)
#endif /* !VBOX */

#define FRAGMENT_SIZE(fragment) ( (size_t) ( ( (void*) fragment ## _end )\
					     - ( (void*) (fragment) ) ) )

/* Data structures in _prot_to_real and _real_to_prot.  These
 * structures are accessed by assembly code as well as C code.
 */
typedef struct {
	uint32_t esp;
	uint16_t cs;
	uint16_t ss;
	uint32_t r2p_params;
} PACKED prot_to_real_params_t;

typedef struct {
	uint32_t ret_addr;
	uint32_t esp;
	uint32_t ebx;
	uint32_t esi;
	uint32_t edi;
	uint32_t ebp;
	uint32_t out_stack;
	uint32_t out_stack_len;
} PACKED real_to_prot_params_t;

/* Function prototypes: realmode.c
 */
#define real_call( fragment, in_stack, out_stack ) \
	_real_call ( fragment, FRAGMENT_SIZE(fragment), \
		     (void*)(in_stack), \
		     ( (in_stack) == NULL ? 0 : sizeof(*(in_stack)) ), \
		     (void*)(out_stack), \
		     ( (out_stack) == NULL ? 0 : sizeof(*(out_stack)) ) )
extern uint16_t _real_call ( void *fragment, int fragment_len,
			     void *in_stack, int in_stack_len,
			     void *out_stack, int out_stack_len );
/* Function prototypes: realmode_asm.S
 */
extern void rm_callback_interface;
extern uint16_t rm_callback_interface_size;
extern uint32_t rm_etherboot_location;
extern void _rm_in_call ( void );
extern void _rm_in_call_far ( void );

extern void _prot_to_real_prefix ( void );
extern void _prot_to_real_prefix_end ( void );
extern uint16_t prot_to_real_prefix_size;

extern void _real_to_prot_suffix ( void );
extern void _real_to_prot_suffix_end ( void );
extern uint16_t real_to_prot_suffix_size;

/* PXE assembler bits */
extern void pxe_callback_interface;
extern uint16_t pxe_callback_interface_size;
extern void _pxe_in_call_far ( void );
extern void _pxenv_in_call_far ( void );
extern void _pxe_intercept_int1a ( void );
extern segoff_t _pxe_intercepted_int1a;
extern segoff_t _pxe_pxenv_location;

/* Global variables
 */
extern uint32_t real_mode_stack;
extern size_t real_mode_stack_size;
extern int lock_real_mode_stack;


/* Function prototypes from basemem.c
 */
#ifdef LINUXBIOS
/* A silly hard code that let's the code compile and work.
 * When this becomes a problem feel free to implement
 * something better.
 */
static inline void allot_real_mode_stack(void) { real_mode_stack = 0x7c00; }
#else
void allot_real_mode_stack(void);
#endif

#endif /* ASSEMBLY */
