[librm] Provide symbols for inline code placed into other sections
[ipxe.git] / src / arch / x86 / include / librm.h
1 #ifndef LIBRM_H
2 #define LIBRM_H
3
4 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
5
6 /* Segment selectors as used in our protected-mode GDTs.
7 *
8 * Don't change these unless you really know what you're doing.
9 */
10 #define VIRTUAL_CS 0x08
11 #define VIRTUAL_DS 0x10
12 #define PHYSICAL_CS 0x18
13 #define PHYSICAL_DS 0x20
14 #define REAL_CS 0x28
15 #define REAL_DS 0x30
16 #define P2R_DS 0x38
17 #define LONG_CS 0x40
18
19 /* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS
20 *
21 * In a 64-bit build, we set the bases of VIRTUAL_CS and VIRTUAL_DS
22 * such that truncating a .textdata symbol value to 32 bits gives a
23 * valid 32-bit virtual address.
24 *
25 * The C code is compiled with -mcmodel=kernel and so we must place
26 * all .textdata symbols within the negative 2GB of the 64-bit address
27 * space. Consequently, all .textdata symbols will have the MSB set
28 * after truncation to 32 bits. This means that a straightforward
29 * R_X86_64_32 relocation record for the symbol will fail, since the
30 * truncated symbol value will not correctly zero-extend to the
31 * original 64-bit value.
32 *
33 * Using an R_X86_64_32S relocation record would work, but there is no
34 * (sensible) way to generate these relocation records within 32-bit
35 * or 16-bit code.
36 *
37 * The simplest solution is to generate an R_X86_64_32 relocation
38 * record with an addend of (-0xffffffff00000000). Since all
39 * .textdata symbols are within the negative 2GB of the 64-bit address
40 * space, this addend acts to effectively truncate the symbol to 32
41 * bits, thereby matching the semantics of the R_X86_64_32 relocation
42 * records generated for 32-bit and 16-bit code.
43 *
44 * In a 32-bit build, this problem does not exist, and we can just use
45 * the .textdata symbol values directly.
46 */
47 #ifdef __x86_64__
48 #define VIRTUAL(address) ( (address) - 0xffffffff00000000 )
49 #else
50 #define VIRTUAL(address) (address)
51 #endif
52
53 #ifdef ASSEMBLY
54
55 /**
56 * Call C function from real-mode code
57 *
58 * @v function C function
59 */
60 .macro virtcall function
61 pushl $VIRTUAL(\function)
62 call virt_call
63 .endm
64
65 #else /* ASSEMBLY */
66
67 #ifdef UACCESS_LIBRM
68 #define UACCESS_PREFIX_librm
69 #else
70 #define UACCESS_PREFIX_librm __librm_
71 #endif
72
73 /**
74 * Call C function from real-mode code
75 *
76 * @v function C function
77 */
78 #define VIRT_CALL( function ) \
79 "pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \
80 "call virt_call\n\t"
81
82 /* Variables in librm.S */
83 extern const unsigned long virt_offset;
84
85 /**
86 * Convert physical address to user pointer
87 *
88 * @v phys_addr Physical address
89 * @ret userptr User pointer
90 */
91 static inline __always_inline userptr_t
92 UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) {
93
94 /* In a 64-bit build, any valid physical address is directly
95 * usable as a virtual address, since the low 4GB is
96 * identity-mapped.
97 */
98 if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
99 return phys_addr;
100
101 /* In a 32-bit build, subtract virt_offset */
102 return ( phys_addr - virt_offset );
103 }
104
105 /**
106 * Convert user buffer to physical address
107 *
108 * @v userptr User pointer
109 * @v offset Offset from user pointer
110 * @ret phys_addr Physical address
111 */
112 static inline __always_inline unsigned long
113 UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) {
114 unsigned long addr = ( userptr + offset );
115
116 /* In a 64-bit build, any virtual address in the low 4GB is
117 * directly usable as a physical address, since the low 4GB is
118 * identity-mapped.
119 */
120 if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
121 ( addr <= 0xffffffffUL ) )
122 return addr;
123
124 /* In a 32-bit build or in a 64-bit build with a virtual
125 * address above 4GB: add virt_offset
126 */
127 return ( addr + virt_offset );
128 }
129
130 static inline __always_inline userptr_t
131 UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) {
132 return trivial_virt_to_user ( addr );
133 }
134
135 static inline __always_inline void *
136 UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) {
137 return trivial_user_to_virt ( userptr, offset );
138 }
139
140 static inline __always_inline userptr_t
141 UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) {
142 return trivial_userptr_add ( userptr, offset );
143 }
144
145 static inline __always_inline off_t
146 UACCESS_INLINE ( librm, userptr_sub ) ( userptr_t userptr,
147 userptr_t subtrahend ) {
148 return trivial_userptr_sub ( userptr, subtrahend );
149 }
150
151 static inline __always_inline void
152 UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off,
153 userptr_t src, off_t src_off,
154 size_t len ) {
155 trivial_memcpy_user ( dest, dest_off, src, src_off, len );
156 }
157
158 static inline __always_inline void
159 UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off,
160 userptr_t src, off_t src_off,
161 size_t len ) {
162 trivial_memmove_user ( dest, dest_off, src, src_off, len );
163 }
164
165 static inline __always_inline int
166 UACCESS_INLINE ( librm, memcmp_user ) ( userptr_t first, off_t first_off,
167 userptr_t second, off_t second_off,
168 size_t len ) {
169 return trivial_memcmp_user ( first, first_off, second, second_off, len);
170 }
171
172 static inline __always_inline void
173 UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset,
174 int c, size_t len ) {
175 trivial_memset_user ( buffer, offset, c, len );
176 }
177
178 static inline __always_inline size_t
179 UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) {
180 return trivial_strlen_user ( buffer, offset );
181 }
182
183 static inline __always_inline off_t
184 UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset,
185 int c, size_t len ) {
186 return trivial_memchr_user ( buffer, offset, c, len );
187 }
188
189
190 /******************************************************************************
191 *
192 * Access to variables in .data16 and .text16
193 *
194 */
195
196 extern char * const data16;
197 extern char * const text16;
198
199 #define __data16( variable ) \
200 __attribute__ (( section ( ".data16" ) )) \
201 _data16_ ## variable __asm__ ( #variable )
202
203 #define __data16_array( variable, array ) \
204 __attribute__ (( section ( ".data16" ) )) \
205 _data16_ ## variable array __asm__ ( #variable )
206
207 #define __bss16( variable ) \
208 __attribute__ (( section ( ".bss16" ) )) \
209 _data16_ ## variable __asm__ ( #variable )
210
211 #define __bss16_array( variable, array ) \
212 __attribute__ (( section ( ".bss16" ) )) \
213 _data16_ ## variable array __asm__ ( #variable )
214
215 #define __text16( variable ) \
216 __attribute__ (( section ( ".text16.data" ) )) \
217 _text16_ ## variable __asm__ ( #variable )
218
219 #define __text16_array( variable, array ) \
220 __attribute__ (( section ( ".text16.data" ) )) \
221 _text16_ ## variable array __asm__ ( #variable )
222
223 #define __use_data16( variable ) \
224 ( * ( ( typeof ( _data16_ ## variable ) * ) \
225 & ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) )
226
227 #define __use_text16( variable ) \
228 ( * ( ( typeof ( _text16_ ## variable ) * ) \
229 & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) )
230
231 #define __from_data16( pointer ) \
232 ( ( unsigned int ) \
233 ( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) )
234
235 #define __from_text16( pointer ) \
236 ( ( unsigned int ) \
237 ( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) )
238
239 /* Variables in librm.S, present in the normal data segment */
240 extern uint16_t rm_sp;
241 extern uint16_t rm_ss;
242 extern const uint16_t __text16 ( rm_cs );
243 #define rm_cs __use_text16 ( rm_cs )
244 extern const uint16_t __text16 ( rm_ds );
245 #define rm_ds __use_text16 ( rm_ds )
246
247 extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );
248 extern void remove_user_from_rm_stack ( userptr_t data, size_t size );
249
250 /* CODE_DEFAULT: restore default .code32/.code64 directive */
251 #ifdef __x86_64__
252 #define CODE_DEFAULT ".code64"
253 #else
254 #define CODE_DEFAULT ".code32"
255 #endif
256
257 /* LINE_SYMBOL: declare a symbol for the current source code line */
258 #define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) ":"
259
260 /* TEXT16_CODE: declare a fragment of code that resides in .text16 */
261 #define TEXT16_CODE( asm_code_str ) \
262 ".section \".text16\", \"ax\", @progbits\n\t" \
263 "\n" LINE_SYMBOL "\n\t" \
264 ".code16\n\t" \
265 asm_code_str "\n\t" \
266 CODE_DEFAULT "\n\t" \
267 ".previous\n\t"
268
269 /* REAL_CODE: declare a fragment of code that executes in real mode */
270 #define REAL_CODE( asm_code_str ) \
271 "push $1f\n\t" \
272 "call real_call\n\t" \
273 TEXT16_CODE ( "\n1:\n\t" \
274 asm_code_str \
275 "\n\t" \
276 "ret\n\t" )
277
278 /* PHYS_CODE: declare a fragment of code that executes in flat physical mode */
279 #define PHYS_CODE( asm_code_str ) \
280 "push $1f\n\t" \
281 "call phys_call\n\t" \
282 ".section \".text.phys\", \"ax\", @progbits\n\t"\
283 "\n" LINE_SYMBOL "\n\t" \
284 ".code32\n\t" \
285 "\n1:\n\t" \
286 asm_code_str \
287 "\n\t" \
288 "ret\n\t" \
289 CODE_DEFAULT "\n\t" \
290 ".previous\n\t"
291
292 /** Number of interrupts */
293 #define NUM_INT 256
294
295 /** A 32-bit interrupt descriptor table register */
296 struct idtr32 {
297 /** Limit */
298 uint16_t limit;
299 /** Base */
300 uint32_t base;
301 } __attribute__ (( packed ));
302
303 /** A 64-bit interrupt descriptor table register */
304 struct idtr64 {
305 /** Limit */
306 uint16_t limit;
307 /** Base */
308 uint64_t base;
309 } __attribute__ (( packed ));
310
311 /** A 32-bit interrupt descriptor table entry */
312 struct interrupt32_descriptor {
313 /** Low 16 bits of address */
314 uint16_t low;
315 /** Code segment */
316 uint16_t segment;
317 /** Unused */
318 uint8_t unused;
319 /** Type and attributes */
320 uint8_t attr;
321 /** High 16 bits of address */
322 uint16_t high;
323 } __attribute__ (( packed ));
324
325 /** A 64-bit interrupt descriptor table entry */
326 struct interrupt64_descriptor {
327 /** Low 16 bits of address */
328 uint16_t low;
329 /** Code segment */
330 uint16_t segment;
331 /** Unused */
332 uint8_t unused;
333 /** Type and attributes */
334 uint8_t attr;
335 /** Middle 16 bits of address */
336 uint16_t mid;
337 /** High 32 bits of address */
338 uint32_t high;
339 /** Reserved */
340 uint32_t reserved;
341 } __attribute__ (( packed ));
342
343 /** Interrupt descriptor is present */
344 #define IDTE_PRESENT 0x80
345
346 /** Interrupt descriptor 32-bit interrupt gate type */
347 #define IDTE_TYPE_IRQ32 0x0e
348
349 /** Interrupt descriptor 64-bit interrupt gate type */
350 #define IDTE_TYPE_IRQ64 0x0e
351
352 /** An interrupt vector
353 *
354 * Each interrupt vector comprises an eight-byte fragment of code:
355 *
356 * 50 pushl %eax (or pushq %rax in long mode)
357 * b0 xx movb $INT, %al
358 * e9 xx xx xx xx jmp interrupt_wrapper
359 */
360 struct interrupt_vector {
361 /** "push" instruction */
362 uint8_t push;
363 /** "movb" instruction */
364 uint8_t movb;
365 /** Interrupt number */
366 uint8_t intr;
367 /** "jmp" instruction */
368 uint8_t jmp;
369 /** Interrupt wrapper address offset */
370 uint32_t offset;
371 /** Next instruction after jump */
372 uint8_t next[0];
373 } __attribute__ (( packed ));
374
375 /** "push %eax" instruction */
376 #define PUSH_INSN 0x50
377
378 /** "movb" instruction */
379 #define MOVB_INSN 0xb0
380
381 /** "jmp" instruction */
382 #define JMP_INSN 0xe9
383
384 /** 32-bit interrupt wrapper stack frame */
385 struct interrupt_frame32 {
386 uint32_t esp;
387 uint32_t ss;
388 uint32_t gs;
389 uint32_t fs;
390 uint32_t es;
391 uint32_t ds;
392 uint32_t ebp;
393 uint32_t edi;
394 uint32_t esi;
395 uint32_t edx;
396 uint32_t ecx;
397 uint32_t ebx;
398 uint32_t eax;
399 uint32_t eip;
400 uint32_t cs;
401 uint32_t eflags;
402 } __attribute__ (( packed ));
403
404 /** 64-bit interrupt wrapper stack frame */
405 struct interrupt_frame64 {
406 uint64_t r15;
407 uint64_t r14;
408 uint64_t r13;
409 uint64_t r12;
410 uint64_t r11;
411 uint64_t r10;
412 uint64_t r9;
413 uint64_t r8;
414 uint64_t rbp;
415 uint64_t rdi;
416 uint64_t rsi;
417 uint64_t rdx;
418 uint64_t rcx;
419 uint64_t rbx;
420 uint64_t rax;
421 uint64_t rip;
422 uint64_t cs;
423 uint64_t rflags;
424 uint64_t rsp;
425 uint64_t ss;
426 } __attribute__ (( packed ));
427
428 extern void set_interrupt_vector ( unsigned int intr, void *vector );
429
430 /** A page table */
431 struct page_table {
432 /** Page address and flags */
433 uint64_t page[512];
434 };
435
436 /** Page flags */
437 enum page_flags {
438 /** Page is present */
439 PAGE_P = 0x01,
440 /** Page is writable */
441 PAGE_RW = 0x02,
442 /** Page is accessible by user code */
443 PAGE_US = 0x04,
444 /** Page-level write-through */
445 PAGE_PWT = 0x08,
446 /** Page-level cache disable */
447 PAGE_PCD = 0x10,
448 /** Page is a large page */
449 PAGE_PS = 0x80,
450 /** Page is the last page in an allocation
451 *
452 * This bit is ignored by the hardware. We use it to track
453 * the size of allocations made by ioremap().
454 */
455 PAGE_LAST = 0x800,
456 };
457
458 /** The I/O space page table */
459 extern struct page_table io_pages;
460
461 /** I/O page size
462 *
463 * We choose to use 2MB pages for I/O space, to minimise the number of
464 * page table entries required.
465 */
466 #define IO_PAGE_SIZE 0x200000UL
467
468 /** I/O page base address
469 *
470 * We choose to place I/O space immediately above the identity-mapped
471 * 32-bit address space.
472 */
473 #define IO_BASE ( ( void * ) 0x100000000ULL )
474
475 #endif /* ASSEMBLY */
476
477 #endif /* LIBRM_H */