mirror of https://github.com/ipxe/ipxe.git
478 lines
12 KiB
C
478 lines
12 KiB
C
#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
|
|
|
|
/* LINE_SYMBOL: declare a symbol for the current source code line */
|
|
#define LINE_SYMBOL _S2 ( OBJECT ) "__line_" _S2 ( __LINE__ ) ":"
|
|
|
|
/* TEXT16_CODE: declare a fragment of code that resides in .text16 */
|
|
#define TEXT16_CODE( asm_code_str ) \
|
|
".section \".text16\", \"ax\", @progbits\n\t" \
|
|
"\n" LINE_SYMBOL "\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"\
|
|
"\n" LINE_SYMBOL "\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 */
|