| #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 */ |