summaryrefslogtreecommitdiffstats
path: root/src/arch/x86/include/librm.h
blob: 597c65e6af6d58baf4ca64a58e8a1c6ffdbf9754 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#ifndef LIBRM_H
#define LIBRM_H

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

/* Segment selectors as used in our protected-mode GDTs.
 *
 * Don't change these unless you really know what you're doing.
 */
#define VIRTUAL_CS 0x08
#define VIRTUAL_DS 0x10
#define PHYSICAL_CS 0x18
#define PHYSICAL_DS 0x20
#define REAL_CS 0x28
#define REAL_DS 0x30
#define P2R_DS 0x38
#define LONG_CS 0x40

/* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS
 *
 * In a 64-bit build, we set the bases of VIRTUAL_CS and VIRTUAL_DS
 * such that truncating a .textdata symbol value to 32 bits gives a
 * valid 32-bit virtual address.
 *
 * The C code is compiled with -mcmodel=kernel and so we must place
 * all .textdata symbols within the negative 2GB of the 64-bit address
 * space.  Consequently, all .textdata symbols will have the MSB set
 * after truncation to 32 bits.  This means that a straightforward
 * R_X86_64_32 relocation record for the symbol will fail, since the
 * truncated symbol value will not correctly zero-extend to the
 * original 64-bit value.
 *
 * Using an R_X86_64_32S relocation record would work, but there is no
 * (sensible) way to generate these relocation records within 32-bit
 * or 16-bit code.
 *
 * The simplest solution is to generate an R_X86_64_32 relocation
 * record with an addend of (-0xffffffff00000000).  Since all
 * .textdata symbols are within the negative 2GB of the 64-bit address
 * space, this addend acts to effectively truncate the symbol to 32
 * bits, thereby matching the semantics of the R_X86_64_32 relocation
 * records generated for 32-bit and 16-bit code.
 *
 * In a 32-bit build, this problem does not exist, and we can just use
 * the .textdata symbol values directly.
 */
#ifdef __x86_64__
#define VIRTUAL(address) ( (address) - 0xffffffff00000000 )
#else
#define VIRTUAL(address) (address)
#endif

#ifdef ASSEMBLY

/**
 * Call C function from real-mode code
 *
 * @v function		C function
 */
.macro virtcall function
	pushl	$VIRTUAL(\function)
	call	virt_call
.endm

#else /* ASSEMBLY */

#ifdef UACCESS_LIBRM
#define UACCESS_PREFIX_librm
#else
#define UACCESS_PREFIX_librm __librm_
#endif

/**
 * Call C function from real-mode code
 *
 * @v function		C function
 */
#define VIRT_CALL( function )						\
	"pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t"		\
	"call virt_call\n\t"

/* Variables in librm.S */
extern const unsigned long virt_offset;

/**
 * Convert physical address to user pointer
 *
 * @v phys_addr		Physical address
 * @ret userptr		User pointer
 */
static inline __always_inline userptr_t
UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) {

	/* In a 64-bit build, any valid physical address is directly
	 * usable as a virtual address, since the low 4GB is
	 * identity-mapped.
	 */
	if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
		return phys_addr;

	/* In a 32-bit build, subtract virt_offset */
	return ( phys_addr - virt_offset );
}

/**
 * Convert user buffer to physical address
 *
 * @v userptr		User pointer
 * @v offset		Offset from user pointer
 * @ret phys_addr	Physical address
 */
static inline __always_inline unsigned long
UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) {
	unsigned long addr = ( userptr + offset );

	/* In a 64-bit build, any virtual address in the low 4GB is
	 * directly usable as a physical address, since the low 4GB is
	 * identity-mapped.
	 */
	if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
	     ( addr <= 0xffffffffUL ) )
		return addr;

	/* In a 32-bit build or in a 64-bit build with a virtual
	 * address above 4GB: add virt_offset
	 */
	return ( addr + virt_offset );
}

static inline __always_inline userptr_t
UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) {
	return trivial_virt_to_user ( addr );
}

static inline __always_inline void *
UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) {
	return trivial_user_to_virt ( userptr, offset );
}

static inline __always_inline userptr_t
UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) {
	return trivial_userptr_add ( userptr, offset );
}

static inline __always_inline off_t
UACCESS_INLINE ( librm, userptr_sub ) ( userptr_t userptr,
					userptr_t subtrahend ) {
	return trivial_userptr_sub ( userptr, subtrahend );
}

static inline __always_inline void
UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off,
					userptr_t src, off_t src_off,
					size_t len ) {
	trivial_memcpy_user ( dest, dest_off, src, src_off, len );
}

static inline __always_inline void
UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off,
					 userptr_t src, off_t src_off,
					 size_t len ) {
	trivial_memmove_user ( dest, dest_off, src, src_off, len );
}

static inline __always_inline int
UACCESS_INLINE ( librm, memcmp_user ) ( userptr_t first, off_t first_off,
					userptr_t second, off_t second_off,
					size_t len ) {
	return trivial_memcmp_user ( first, first_off, second, second_off, len);
}

static inline __always_inline void
UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset,
					int c, size_t len ) {
	trivial_memset_user ( buffer, offset, c, len );
}

static inline __always_inline size_t
UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) {
	return trivial_strlen_user ( buffer, offset );
}

static inline __always_inline off_t
UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset,
					int c, size_t len ) {
	return trivial_memchr_user ( buffer, offset, c, len );
}


/******************************************************************************
 *
 * Access to variables in .data16 and .text16
 *
 */

extern char * const data16;
extern char * const text16;

#define __data16( variable )						\
	__attribute__ (( section ( ".data16" ) ))			\
	_data16_ ## variable __asm__ ( #variable )

#define __data16_array( variable, array )				\
	__attribute__ (( section ( ".data16" ) ))			\
	_data16_ ## variable array __asm__ ( #variable )

#define __bss16( variable )						\
	__attribute__ (( section ( ".bss16" ) ))			\
	_data16_ ## variable __asm__ ( #variable )

#define __bss16_array( variable, array )				\
	__attribute__ (( section ( ".bss16" ) ))			\
	_data16_ ## variable array __asm__ ( #variable )

#define __text16( variable )						\
	__attribute__ (( section ( ".text16.data" ) ))			\
	_text16_ ## variable __asm__ ( #variable )

#define __text16_array( variable, array )				\
	__attribute__ (( section ( ".text16.data" ) ))			\
	_text16_ ## variable array __asm__ ( #variable )

#define __use_data16( variable )					\
	( * ( ( typeof ( _data16_ ## variable ) * )			\
	      & ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) )

#define __use_text16( variable )					\
	( * ( ( typeof ( _text16_ ## variable ) * )			\
	      & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) )

#define __from_data16( pointer )					\
	( ( unsigned int )						\
	  ( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) )

#define __from_text16( pointer )					\
	( ( unsigned int )						\
	  ( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) )

/* Variables in librm.S, present in the normal data segment */
extern uint16_t rm_sp;
extern uint16_t rm_ss;
extern const uint16_t __text16 ( rm_cs );
#define rm_cs __use_text16 ( rm_cs )
extern const uint16_t __text16 ( rm_ds );
#define rm_ds __use_text16 ( rm_ds )

extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );
extern void remove_user_from_rm_stack ( userptr_t data, size_t size );

/* CODE_DEFAULT: restore default .code32/.code64 directive */
#ifdef __x86_64__
#define CODE_DEFAULT ".code64"
#else
#define CODE_DEFAULT ".code32"
#endif

/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
#define TEXT16_CODE( asm_code_str )			\
	".section \".text16\", \"ax\", @progbits\n\t"	\
	".code16\n\t"					\
	asm_code_str "\n\t"				\
	CODE_DEFAULT "\n\t"				\
	".previous\n\t"

/* REAL_CODE: declare a fragment of code that executes in real mode */
#define REAL_CODE( asm_code_str )			\
	"push $1f\n\t"					\
	"call real_call\n\t"				\
	TEXT16_CODE ( "\n1:\n\t"			\
		      asm_code_str			\
		      "\n\t"				\
		      "ret\n\t" )

/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
#define PHYS_CODE( asm_code_str )			\
	"push $1f\n\t"					\
	"call phys_call\n\t"				\
	".section \".text.phys\", \"ax\", @progbits\n\t"\
	".code32\n\t"					\
	"\n1:\n\t"					\
	asm_code_str					\
	"\n\t"						\
	"ret\n\t"					\
	CODE_DEFAULT "\n\t"				\
	".previous\n\t"

/** Number of interrupts */
#define NUM_INT 256

/** A 32-bit interrupt descriptor table register */
struct idtr32 {
	/** Limit */
	uint16_t limit;
	/** Base */
	uint32_t base;
} __attribute__ (( packed ));

/** A 64-bit interrupt descriptor table register */
struct idtr64 {
	/** Limit */
	uint16_t limit;
	/** Base */
	uint64_t base;
} __attribute__ (( packed ));

/** A 32-bit interrupt descriptor table entry */
struct interrupt32_descriptor {
	/** Low 16 bits of address */
	uint16_t low;
	/** Code segment */
	uint16_t segment;
	/** Unused */
	uint8_t unused;
	/** Type and attributes */
	uint8_t attr;
	/** High 16 bits of address */
	uint16_t high;
} __attribute__ (( packed ));

/** A 64-bit interrupt descriptor table entry */
struct interrupt64_descriptor {
	/** Low 16 bits of address */
	uint16_t low;
	/** Code segment */
	uint16_t segment;
	/** Unused */
	uint8_t unused;
	/** Type and attributes */
	uint8_t attr;
	/** Middle 16 bits of address */
	uint16_t mid;
	/** High 32 bits of address */
	uint32_t high;
	/** Reserved */
	uint32_t reserved;
} __attribute__ (( packed ));

/** Interrupt descriptor is present */
#define IDTE_PRESENT 0x80

/** Interrupt descriptor 32-bit interrupt gate type */
#define IDTE_TYPE_IRQ32 0x0e

/** Interrupt descriptor 64-bit interrupt gate type */
#define IDTE_TYPE_IRQ64 0x0e

/** An interrupt vector
 *
 * Each interrupt vector comprises an eight-byte fragment of code:
 *
 *   50			pushl %eax (or pushq %rax in long mode)
 *   b0 xx		movb $INT, %al
 *   e9 xx xx xx xx	jmp interrupt_wrapper
 */
struct interrupt_vector {
	/** "push" instruction */
	uint8_t push;
	/** "movb" instruction */
	uint8_t movb;
	/** Interrupt number */
	uint8_t intr;
	/** "jmp" instruction */
	uint8_t jmp;
	/** Interrupt wrapper address offset */
	uint32_t offset;
	/** Next instruction after jump */
	uint8_t next[0];
} __attribute__ (( packed ));

/** "push %eax" instruction */
#define PUSH_INSN 0x50

/** "movb" instruction */
#define MOVB_INSN 0xb0

/** "jmp" instruction */
#define JMP_INSN 0xe9

/** 32-bit interrupt wrapper stack frame */
struct interrupt_frame32 {
	uint32_t esp;
	uint32_t ss;
	uint32_t gs;
	uint32_t fs;
	uint32_t es;
	uint32_t ds;
	uint32_t ebp;
	uint32_t edi;
	uint32_t esi;
	uint32_t edx;
	uint32_t ecx;
	uint32_t ebx;
	uint32_t eax;
	uint32_t eip;
	uint32_t cs;
	uint32_t eflags;
} __attribute__ (( packed ));

/** 64-bit interrupt wrapper stack frame */
struct interrupt_frame64 {
	uint64_t r15;
	uint64_t r14;
	uint64_t r13;
	uint64_t r12;
	uint64_t r11;
	uint64_t r10;
	uint64_t r9;
	uint64_t r8;
	uint64_t rbp;
	uint64_t rdi;
	uint64_t rsi;
	uint64_t rdx;
	uint64_t rcx;
	uint64_t rbx;
	uint64_t rax;
	uint64_t rip;
	uint64_t cs;
	uint64_t rflags;
	uint64_t rsp;
	uint64_t ss;
} __attribute__ (( packed ));

extern void set_interrupt_vector ( unsigned int intr, void *vector );

/** A page table */
struct page_table {
	/** Page address and flags */
	uint64_t page[512];
};

/** Page flags */
enum page_flags {
	/** Page is present */
	PAGE_P = 0x01,
	/** Page is writable */
	PAGE_RW = 0x02,
	/** Page is accessible by user code */
	PAGE_US = 0x04,
	/** Page-level write-through */
	PAGE_PWT = 0x08,
	/** Page-level cache disable */
	PAGE_PCD = 0x10,
	/** Page is a large page */
	PAGE_PS = 0x80,
	/** Page is the last page in an allocation
	 *
	 * This bit is ignored by the hardware.  We use it to track
	 * the size of allocations made by ioremap().
	 */
	PAGE_LAST = 0x800,
};

/** The I/O space page table */
extern struct page_table io_pages;

/** I/O page size
 *
 * We choose to use 2MB pages for I/O space, to minimise the number of
 * page table entries required.
 */
#define IO_PAGE_SIZE 0x200000UL

/** I/O page base address
 *
 * We choose to place I/O space immediately above the identity-mapped
 * 32-bit address space.
 */
#define IO_BASE ( ( void * ) 0x100000000ULL )

#endif /* ASSEMBLY */

#endif /* LIBRM_H */