summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/include/realmode.h
blob: fa8c7863954bd33b8dd538df8438a3c044c500df (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#ifndef REALMODE_H
#define REALMODE_H

#ifndef ASSEMBLY

#include "stdint.h"
#include "registers.h"
#include "io.h"

/*
 * Data structures and type definitions
 *
 */

/* Segment:offset structure.  Note that the order within the structure
 * is offset:segment.
 */
typedef struct {
	uint16_t offset;
	uint16_t segment;
} __attribute__ (( packed )) segoff_t;

/* Macro hackery needed to stringify bits of inline assembly */
#define RM_XSTR(x) #x
#define RM_STR(x) RM_XSTR(x)

/* Drag in the selected real-mode transition library header */
#ifdef KEEP_IT_REAL
#include "libkir.h"
#else
#include "librm.h"
#endif

/*
 * The API to some functions is identical between librm and libkir, so
 * they are documented here, even though the prototypes are in librm.h
 * and libkir.h.
 *
 */

/*
 * Declaration of variables in .data16
 *
 * To place a variable in the .data16 segment, declare it using the
 * pattern:
 *
 *   int __data16 ( foo );
 *   #define foo __use_data16 ( foo );
 *
 *   extern uint32_t __data16 ( bar );
 *   #define bar __use_data16 ( bar );
 *
 *   extern long __data16 ( baz ) = 0xff000000UL;
 *   #define bar __use_data16 ( baz );
 *
 * i.e. take a normal declaration, add __data16() around the variable
 * name, and add a line saying "#define <name> __use_data16 ( <name> )
 *
 * You can then access them just like any other variable, for example
 *
 *   int x = foo + bar;
 *
 * This magic is achieved at a cost of only around 7 extra bytes per
 * group of accesses to .data16 variables.  When using KEEP_IT_REAL,
 * there is no extra cost.
 *
 * You should place variables in .data16 when they need to be accessed
 * by real-mode code.  Real-mode assembly (e.g. as created by
 * REAL_EXEC()) can access these variables via the usual data segment.
 * You can therefore write something like
 *
 *   static uint16_t __data16 ( foo );
 *   #define foo __use_data16 ( foo )
 *
 *   int bar ( void ) {
 *     REAL_EXEC ( baz,
 *                 "int $0xff\n\t"
 *                 "movw %ax, foo",
 *                 ... );
 *     return foo;
 *   }
 *
 * Variables may also be placed in .text16 using __text16 and
 * __use_text16.  Some variables (e.g. chained interrupt vectors) fit
 * most naturally in .text16; most should be in .data16.
 */

/*
 * void copy_to_real ( uint16_t dest_seg, uint16_t dest_off,
 *		       void *src, size_t n )
 * void copy_from_real ( void *dest, uint16_t src_seg, uint16_t src_off,
 *			 size_t n )
 *
 * These functions can be used to copy data to and from arbitrary
 * locations in base memory.
 */

/*
 * put_real ( variable, uint16_t dest_seg, uint16_t dest_off )
 * get_real ( variable, uint16_t src_seg, uint16_t src_off )
 *
 * These macros can be used to read or write single variables to and
 * from arbitrary locations in base memory.  "variable" must be a
 * variable of either 1, 2 or 4 bytes in length.
 */

/*
 * REAL_CALL ( routine, num_out_constraints, out_constraints,
 *	       in_constraints, clobber )
 * REAL_EXEC ( name, asm_code_str, num_out_constraints, out_constraints,
 *	       in_constraints, clobber )
 *
 * If you have a pre-existing real-mode routine that you want to make
 * a far call to, use REAL_CALL.  If you have a code fragment that you
 * want to copy down to base memory, execute, and then remove, use
 * REAL_EXEC.
 *
 * out_constraints must be of the form OUT_CONSTRAINTS(constraints),
 * and in_constraints must be of the form IN_CONSTRAINTS(constraints),
 * where "constraints" is a constraints list as would be used in an
 * inline __asm__()
 *
 * clobber must be of the form CLOBBER ( clobber_list ), where
 * "clobber_list" is a clobber list as would be used in an inline
 * __asm__().
 *
 * These are best illustrated by example.  To write a character to the
 * console using INT 10, you would do something like:
 *
 *	REAL_EXEC ( rm_test_librm,
 *		    "int $0x10",
 *		    1,
 *		    OUT_CONSTRAINTS ( "=a" ( discard ) ),
 *		    IN_CONSTRAINTS ( "a" ( 0x0e00 + character ),
 *				     "b" ( 1 ) ),
 *		    CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
 *
 * IMPORTANT: gcc does not automatically assume that input operands
 * get clobbered.  The only way to specify that an input operand may
 * be modified is to also specify it as an output operand; hence the
 * "(discard)" in the above code.
 */

#warning "realmode.h contains placeholders for obsolete macros"


/* Just for now */
#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 )
#define OFFSET(x) ( virt_to_phys ( x ) & 0xf )
#define SEGOFF(x) { OFFSET(x), SEGMENT(x) }

/* To make basemem.c compile */
extern int lock_real_mode_stack;
extern char *real_mode_stack;
extern char real_mode_stack_size[];

#define RM_FRAGMENT(name,asm) \
	void name ( void ) {} \
	extern char name ## _size[];



#endif /* ASSEMBLY */

#endif /* REALMODE_H */