david/ipxe
Archived
1
0

[uaccess] Formalise the uaccess API

The userptr_t is now the fundamental type that gets used for conversions.
For example, virt_to_phys() is implemented in terms of virt_to_user() and
user_to_phys().
This commit is contained in:
Michael Brown 2008-10-13 03:47:33 +01:00
parent 667819becc
commit 6554b79ff9
21 changed files with 569 additions and 760 deletions

View File

@ -2,7 +2,7 @@
* Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub.
*/
#include <virtaddr.h>
#include <librm.h>
#define SIZEOF_I386_REGS 32
#define SIZEOF_I386_FLAGS 4

View File

@ -19,7 +19,7 @@
#include <stddef.h>
#include <stdio.h>
#include <assert.h>
#include <virtaddr.h>
#include <gpxe/uaccess.h>
#include <gpxe/gdbstub.h>
#include <gdbmach.h>

View File

@ -16,6 +16,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gpxe/io.h>
#include <pic8259.h>
/** @file

View File

@ -1,325 +0,0 @@
#include "virtaddr.h"
.equ MSR_K6_EFER, 0xC0000080
.equ EFER_LME, 0x00000100
.equ X86_CR4_PAE, 0x00000020
.equ CR0_PG, 0x80000000
#ifdef GAS291
#define DATA32 data32;
#define ADDR32 addr32;
#define LJMPI(x) ljmp x
#else
#define DATA32 data32
#define ADDR32 addr32
/* newer GAS295 require #define LJMPI(x) ljmp *x */
#define LJMPI(x) ljmp x
#endif
/*
* NOTE: if you write a subroutine that is called from C code (gcc/egcs),
* then you only have to take care of %ebx, %esi, %edi and %ebp. These
* registers must not be altered under any circumstance. All other registers
* may be clobbered without any negative side effects. If you don't follow
* this rule then you'll run into strange effects that only occur on some
* gcc versions (because the register allocator may use different registers).
*
* All the data32 prefixes for the ljmp instructions are necessary, because
* the assembler emits code with a relocation address of 0. This means that
* all destinations are initially negative, which the assembler doesn't grok,
* because for some reason negative numbers don't fit into 16 bits. The addr32
* prefixes are there for the same reasons, because otherwise the memory
* references are only 16 bit wide. Theoretically they are all superfluous.
* One last note about prefixes: the data32 prefixes on all call _real_to_prot
* instructions could be removed if the _real_to_prot function is changed to
* deal correctly with 16 bit return addresses. I tried it, but failed.
*/
.text
.arch i386
.code32
/* This is a struct os_entry_regs */
.globl os_regs
os_regs: .space 56
/**************************************************************************
XSTART32 - Transfer control to the kernel just loaded
**************************************************************************/
.globl xstart32
xstart32:
/* Save the callee save registers */
movl %ebp, os_regs + 32
movl %esi, os_regs + 36
movl %edi, os_regs + 40
movl %ebx, os_regs + 44
/* save the return address */
popl %eax
movl %eax, os_regs + 48
/* save the stack pointer */
movl %esp, os_regs + 52
/* Get the new destination address */
popl %ecx
/* Store the physical address of xend on the stack */
movl $xend32, %ebx
addl virt_offset, %ebx
pushl %ebx
/* Store the destination address on the stack */
pushl $PHYSICAL_CS
pushl %ecx
/* Cache virt_offset */
movl virt_offset, %ebp
/* Switch to using physical addresses */
call _virt_to_phys
/* Save the target stack pointer */
movl %esp, os_regs + 12(%ebp)
leal os_regs(%ebp), %esp
/* Store the pointer to os_regs */
movl %esp, os_regs_ptr(%ebp)
/* Load my new registers */
popal
movl (-32 + 12)(%esp), %esp
/* Jump to the new kernel
* The lret switches to a flat code segment
*/
lret
.balign 4
.globl xend32
xend32:
/* Fixup %eflags */
nop
cli
cld
/* Load %esp with &os_regs + virt_offset */
.byte 0xbc /* movl $0, %esp */
os_regs_ptr:
.long 0
/* Save the result registers */
addl $32, %esp
pushal
/* Compute virt_offset */
movl %esp, %ebp
subl $os_regs, %ebp
/* Load the stack pointer and convert it to physical address */
movl 52(%esp), %esp
addl %ebp, %esp
/* Enable the virtual addresses */
leal _phys_to_virt(%ebp), %eax
call *%eax
/* Restore the callee save registers */
movl os_regs + 32, %ebp
movl os_regs + 36, %esi
movl os_regs + 40, %edi
movl os_regs + 44, %ebx
movl os_regs + 48, %edx
movl os_regs + 52, %esp
/* Get the C return value */
movl os_regs + 28, %eax
jmpl *%edx
#ifdef CONFIG_X86_64
.arch sledgehammer
/**************************************************************************
XSTART_lm - Transfer control to the kernel just loaded in long mode
**************************************************************************/
.globl xstart_lm
xstart_lm:
/* Save the callee save registers */
pushl %ebp
pushl %esi
pushl %edi
pushl %ebx
/* Cache virt_offset && (virt_offset & 0xfffff000) */
movl virt_offset, %ebp
movl %ebp, %ebx
andl $0xfffff000, %ebx
/* Switch to using physical addresses */
call _virt_to_phys
/* Initialize the page tables */
/* Level 4 */
leal 0x23 + pgt_level3(%ebx), %eax
leal pgt_level4(%ebx), %edi
movl %eax, (%edi)
/* Level 3 */
leal 0x23 + pgt_level2(%ebx), %eax
leal pgt_level3(%ebx), %edi
movl %eax, 0x00(%edi)
addl $4096, %eax
movl %eax, 0x08(%edi)
addl $4096, %eax
movl %eax, 0x10(%edi)
addl $4096, %eax
movl %eax, 0x18(%edi)
/* Level 2 */
movl $0xe3, %eax
leal pgt_level2(%ebx), %edi
leal 16384(%edi), %esi
pgt_level2_loop:
movl %eax, (%edi)
addl $8, %edi
addl $0x200000, %eax
cmp %esi, %edi
jne pgt_level2_loop
/* Point at the x86_64 page tables */
leal pgt_level4(%ebx), %edi
movl %edi, %cr3
/* Setup for the return from 64bit mode */
/* 64bit align the stack */
movl %esp, %ebx /* original stack pointer + 16 */
andl $0xfffffff8, %esp
/* Save original stack pointer + 16 */
pushl %ebx
/* Save virt_offset */
pushl %ebp
/* Setup for the jmp to 64bit long mode */
leal start_lm(%ebp), %eax
movl %eax, 0x00 + start_lm_addr(%ebp)
movl $LM_CODE_SEG, %eax
movl %eax, 0x04 + start_lm_addr(%ebp)
/* Setup for the jump out of 64bit long mode */
leal end_lm(%ebp), %eax
movl %eax, 0x00 + end_lm_addr(%ebp)
movl $FLAT_CODE_SEG, %eax
movl %eax, 0x04 + end_lm_addr(%ebp)
/* Enable PAE mode */
movl %cr4, %eax
orl $X86_CR4_PAE, %eax
movl %eax, %cr4
/* Enable long mode */
movl $MSR_K6_EFER, %ecx
rdmsr
orl $EFER_LME, %eax
wrmsr
/* Start paging, entering 32bit compatiblity mode */
movl %cr0, %eax
orl $CR0_PG, %eax
movl %eax, %cr0
/* Enter 64bit long mode */
ljmp *start_lm_addr(%ebp)
.code64
start_lm:
/* Load 64bit data segments */
movl $LM_DATA_SEG, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
andq $0xffffffff, %rbx
/* Get the address to jump to */
movl 20(%rbx), %edx
andq $0xffffffff, %rdx
/* Get the argument pointer */
movl 24(%rbx), %ebx
andq $0xffffffff, %rbx
/* Jump to the 64bit code */
call *%rdx
/* Preserve the result */
movl %eax, %edx
/* Fixup %eflags */
cli
cld
/* Switch to 32bit compatibility mode */
ljmp *end_lm_addr(%rip)
.code32
end_lm:
/* Disable paging */
movl %cr0, %eax
andl $~CR0_PG, %eax
movl %eax, %cr0
/* Disable long mode */
movl $MSR_K6_EFER, %ecx
rdmsr
andl $~EFER_LME, %eax
wrmsr
/* Disable PAE */
movl %cr4, %eax
andl $~X86_CR4_PAE, %eax
movl %eax, %cr4
/* Compute virt_offset */
popl %ebp
/* Compute the original stack pointer + 16 */
popl %ebx
movl %ebx, %esp
/* Enable the virtual addresses */
leal _phys_to_virt(%ebp), %eax
call *%eax
/* Restore the callee save registers */
popl %ebx
popl %esi
popl %edi
popl %ebp
/* Get the C return value */
movl %edx, %eax
/* Return */
ret
.arch i386
#endif /* CONFIG_X86_64 */
#ifdef CONFIG_X86_64
.section ".bss"
.p2align 12
/* Include a dummy space in case we are loaded badly aligned */
.space 4096
/* Reserve enough space for a page table convering 4GB with 2MB pages */
pgt_level4:
.space 4096
pgt_level3:
.space 4096
pgt_level2:
.space 16384
start_lm_addr:
.space 8
end_lm_addr:
.space 8
#endif

View File

@ -4,7 +4,7 @@
*
*/
#include "virtaddr.h"
#include "librm.h"
.arch i386
.text

View File

@ -65,10 +65,8 @@ static void x86_writeq ( uint64_t data, volatile uint64_t *io_addr ) {
: : "A" ( data ), "r" ( io_addr ) );
}
PROVIDE_IOAPI_INLINE ( x86, virt_to_phys );
PROVIDE_IOAPI_INLINE ( x86, phys_to_virt );
PROVIDE_IOAPI_INLINE ( x86, virt_to_bus );
PROVIDE_IOAPI_INLINE ( x86, bus_to_virt );
PROVIDE_IOAPI_INLINE ( x86, phys_to_bus );
PROVIDE_IOAPI_INLINE ( x86, bus_to_phys );
PROVIDE_IOAPI_INLINE ( x86, ioremap );
PROVIDE_IOAPI_INLINE ( x86, iounmap );
PROVIDE_IOAPI_INLINE ( x86, io_to_bus );

View File

@ -23,6 +23,7 @@
#include <biosint.h>
#include <pnpbios.h>
#include <basemem_packet.h>
#include <gpxe/io.h>
#include <gpxe/iobuf.h>
#include <gpxe/netdevice.h>
#include <gpxe/if_ether.h>

View File

@ -1,6 +1,7 @@
#include <stdio.h>
#include <realmode.h>
#include <bios.h>
#include <gpxe/io.h>
#include <gpxe/timer.h>
#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */

View File

@ -1,6 +1,12 @@
#ifndef _BITS_UACCESS_H
#define _BITS_UACCESS_H
#include <realmode.h>
/** @file
*
* i386-specific user access API implementations
*
*/
#include <librm.h>
#endif /* _BITS_UACCESS_H */

View File

@ -15,8 +15,6 @@
* into a machine with such an old CPU anyway.
*/
#include <virtaddr.h>
#ifdef IOAPI_X86
#define IOAPI_PREFIX_x86
#else
@ -28,24 +26,19 @@
*
*/
static inline __always_inline unsigned long
IOAPI_INLINE ( x86, virt_to_phys ) ( volatile const void *addr ) {
return ( ( ( unsigned long ) addr ) + virt_offset );
}
/*
* Physical<->Bus and Bus<->I/O address mappings
*
*/
static inline __always_inline void *
IOAPI_INLINE ( x86, phys_to_virt ) ( unsigned long phys_addr ) {
return ( ( void * ) ( phys_addr - virt_offset ) );
static inline __always_inline unsigned long
IOAPI_INLINE ( x86, phys_to_bus ) ( unsigned long phys_addr ) {
return phys_addr;
}
static inline __always_inline unsigned long
IOAPI_INLINE ( x86, virt_to_bus ) ( volatile const void *addr ) {
return virt_to_phys ( addr );
}
static inline __always_inline void *
IOAPI_INLINE ( x86, bus_to_virt ) ( unsigned long bus_addr ) {
return phys_to_virt ( bus_addr );
IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) {
return bus_addr;
}
static inline __always_inline void *

View File

@ -9,6 +9,7 @@
#include <stdint.h>
#include <gpxe/list.h>
#include <realmode.h>
struct block_device;

View File

@ -1,21 +1,109 @@
#ifndef LIBRM_H
#define LIBRM_H
/* Drag in protected-mode segment selector values */
#include "virtaddr.h"
#include "realmode.h"
/* 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
#if 0
#define LONG_CS 0x38
#define LONG_DS 0x40
#endif
#ifndef ASSEMBLY
#include "stddef.h"
#include "string.h"
#ifdef UACCESS_LIBRM
#define UACCESS_PREFIX_librm
#else
#define UACCESS_PREFIX_librm __librm_
#endif
/*
* Data structures and type definitions
/* Variables in librm.S */
extern 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 ) {
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 ) {
return ( userptr + offset + 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 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 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
*
*/
/* Access to variables in .data16 and .text16 */
extern char *data16;
extern char *text16;
@ -72,178 +160,6 @@ extern uint16_t __text16 ( rm_ds );
*/
extern void gateA20_set ( void );
/*
* librm_mgmt: functions for manipulating base memory and executing
* real-mode code.
*
* Full API documentation for these functions is in realmode.h.
*
*/
/* Macro for obtaining a physical address from a segment:offset pair. */
#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
/* Copy to/from base memory */
static inline __attribute__ (( always_inline )) void
copy_to_real_librm ( unsigned int dest_seg, unsigned int dest_off,
void *src, size_t n ) {
memcpy ( VIRTUAL ( dest_seg, dest_off ), src, n );
}
static inline __attribute__ (( always_inline )) void
copy_from_real_librm ( void *dest, unsigned int src_seg,
unsigned int src_off, size_t n ) {
memcpy ( dest, VIRTUAL ( src_seg, src_off ), n );
}
#define put_real_librm( var, dest_seg, dest_off ) \
do { \
* ( ( typeof(var) * ) VIRTUAL ( dest_seg, dest_off ) ) = var; \
} while ( 0 )
#define get_real_librm( var, src_seg, src_off ) \
do { \
var = * ( ( typeof(var) * ) VIRTUAL ( src_seg, src_off ) ); \
} while ( 0 )
#define copy_to_real copy_to_real_librm
#define copy_from_real copy_from_real_librm
#define put_real put_real_librm
#define get_real get_real_librm
/**
* A pointer to a user buffer
*
* Even though we could just use a void *, we use an intptr_t so that
* attempts to use normal pointers show up as compiler warnings. Such
* code is actually valid for librm, but not for libkir (i.e. under
* KEEP_IT_REAL), so it's good to have the warnings even under librm.
*/
typedef intptr_t userptr_t;
/**
* Add offset to user pointer
*
* @v ptr User pointer
* @v offset Offset
* @ret new_ptr New pointer value
*/
static inline __attribute__ (( always_inline )) userptr_t
userptr_add ( userptr_t ptr, off_t offset ) {
return ( ptr + offset );
}
/**
* Copy data to user buffer
*
* @v buffer User buffer
* @v offset Offset within user buffer
* @v src Source
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) {
memcpy ( ( ( void * ) buffer + offset ), src, len );
}
/**
* Copy data from user buffer
*
* @v dest Destination
* @v buffer User buffer
* @v offset Offset within user buffer
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) {
memcpy ( dest, ( ( void * ) buffer + offset ), len );
}
/**
* Copy data between user buffers
*
* @v dest Destination user buffer
* @v dest_off Offset within destination buffer
* @v src Source user buffer
* @v src_off Offset within source buffer
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
memcpy_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off,
size_t len ) {
memcpy ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ),
len );
}
/**
* Copy data between user buffers, allowing for overlap
*
* @v dest Destination user buffer
* @v dest_off Offset within destination buffer
* @v src Source user buffer
* @v src_off Offset within source buffer
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
memmove_user ( userptr_t dest, off_t dest_off, userptr_t src, off_t src_off,
size_t len ) {
memmove ( ( ( void * ) dest + dest_off ), ( ( void * ) src + src_off ),
len );
}
/**
* Fill user buffer with a constant byte
*
* @v buffer User buffer
* @v offset Offset within buffer
* @v c Constant byte with which to fill
* @v len Length
*/
static inline __attribute__ (( always_inline )) void
memset_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
memset ( ( ( void * ) buffer + offset ), c, len );
}
/**
* Find length of NUL-terminated string in user buffer
*
* @v buffer User buffer
* @v offset Offset within buffer
* @ret len Length of string (excluding NUL)
*/
static inline __attribute__ (( always_inline )) size_t
strlen_user ( userptr_t buffer, off_t offset ) {
return strlen ( ( void * ) buffer + offset );
}
/**
* Find character in user buffer
*
* @v buffer User buffer
* @v offset Starting offset within buffer
* @v c Character to search for
* @v len Length of user buffer
* @ret offset Offset of character, or <0 if not found
*/
static inline __attribute__ (( always_inline )) off_t
memchr_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
void *found;
found = memchr ( ( ( void * ) buffer + offset ), c, len );
return ( found ? ( found - ( void * ) buffer ) : -1 );
}
/**
* Convert virtual address to user buffer
*
* @v virtual Virtual address
* @ret buffer User buffer
*
* This constructs a user buffer from an ordinary pointer. Use it
* when you need to pass a pointer to an internal buffer to a function
* that expects a @c userptr_t.
*/
static inline __attribute__ (( always_inline )) userptr_t
virt_to_user ( void * virtual ) {
return ( ( intptr_t ) virtual );
}
/**
* Convert segment:offset address to user buffer
*
@ -251,32 +167,9 @@ virt_to_user ( void * virtual ) {
* @v offset Real-mode offset
* @ret buffer User buffer
*/
static inline __attribute__ (( always_inline )) userptr_t
static inline __always_inline userptr_t
real_to_user ( unsigned int segment, unsigned int offset ) {
return virt_to_user ( VIRTUAL ( segment, offset ) );
}
/**
* Convert physical address to user buffer
*
* @v physical Physical address
* @ret buffer User buffer
*/
static inline __attribute__ (( always_inline )) userptr_t
phys_to_user ( physaddr_t physical ) {
return virt_to_user ( phys_to_virt ( physical ) );
}
/**
* Convert user buffer to physical address
*
* @v buffer User buffer
* @v offset Offset within user buffer
* @ret physical Physical address
*/
static inline __attribute__ (( always_inline )) physaddr_t
user_to_phys ( userptr_t buffer, off_t offset ) {
return virt_to_phys ( ( void * ) buffer + offset );
return ( phys_to_user ( ( segment << 4 ) + offset ) );
}
extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size );

View File

@ -1,17 +0,0 @@
/*
* Architecture-specific portion of pxe.h for Etherboot
*
* This file has to define the types SEGOFF16_t, SEGDESC_t and
* SEGSEL_t for use in other PXE structures. See pxe.h for details.
*/
#ifndef PXE_ADDR_H
#define PXE_ADDR_H
#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
#define PTR_TO_SEGOFF16(ptr,segoff16) \
(segoff16).segment = SEGMENT(ptr); \
(segoff16).offset = OFFSET(ptr);
#endif /* PXE_ADDR_H */

View File

@ -1,45 +1,15 @@
#ifndef REALMODE_H
#define REALMODE_H
#ifndef ASSEMBLY
#include "stdint.h"
#include "registers.h"
#include <gpxe/io.h>
#include <stdint.h>
#include <registers.h>
#include <gpxe/uaccess.h>
/*
* Data structures and type definitions
*
*/
/* Segment:offset structure. Note that the order within the structure
* is offset:segment.
*/
struct segoff {
uint16_t offset;
uint16_t segment;
} __attribute__ (( packed ));
typedef struct segoff 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
*
@ -92,24 +62,53 @@ typedef struct segoff segoff_t;
* assembler output to make sure that it's doing the right thing.
*/
/*
* 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 )
/**
* Copy data to base memory
*
* These functions can be used to copy data to and from arbitrary
* locations in base memory.
* @v dest_seg Destination segment
* @v dest_off Destination offset
* @v src Source
* @v len Length
*/
static inline __always_inline void
copy_to_real ( unsigned int dest_seg, unsigned int dest_off,
void *src, size_t n ) {
copy_to_user ( real_to_user ( dest_seg, dest_off ), 0, src, n );
}
/*
* put_real ( variable, uint16_t dest_seg, uint16_t dest_off )
* get_real ( variable, uint16_t src_seg, uint16_t src_off )
/**
* Copy data to base memory
*
* 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.
* @v dest Destination
* @v src_seg Source segment
* @v src_off Source offset
* @v len Length
*/
static inline __always_inline void
copy_from_real ( void *dest, unsigned int src_seg,
unsigned int src_off, size_t n ) {
copy_from_user ( dest, real_to_user ( src_seg, src_off ), 0, n );
}
/**
* Write a single variable to base memory
*
* @v var Variable to write
* @v dest_seg Destination segment
* @v dest_off Destination offset
*/
#define put_real( var, dest_seg, dest_off ) \
copy_to_real ( (dest_seg), (dest_off), &(var), sizeof (var) )
/**
* Read a single variable from base memory
*
* @v var Variable to read
* @v src_seg Source segment
* @v src_off Source offset
*/
#define get_real( var, src_seg, src_off ) \
copy_from_real ( &(var), (src_seg), (src_off), sizeof (var) )
/*
* REAL_CODE ( asm_code_str )
@ -123,6 +122,4 @@ typedef struct segoff segoff_t;
*
*/
#endif /* ASSEMBLY */
#endif /* REALMODE_H */

View File

@ -10,8 +10,7 @@
*
*/
#include "compiler.h" /* for doxygen */
#include "stdint.h"
#include <stdint.h>
/**
* A 16-bit general register.
@ -184,4 +183,14 @@ struct i386_all_regs {
#define SF ( 1 << 7 )
#define OF ( 1 << 11 )
/* Segment:offset structure. Note that the order within the structure
* is offset:segment.
*/
struct segoff {
uint16_t offset;
uint16_t segment;
} PACKED;
typedef struct segoff segoff_t;
#endif /* REGISTERS_H */

View File

@ -1,74 +0,0 @@
#ifndef VIRTADDR_H
#define VIRTADDR_H
/* 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
#if 0
#define LONG_CS 0x38
#define LONG_DS 0x40
#endif
#ifndef ASSEMBLY
#ifndef KEEP_IT_REAL
/*
* Without -DKEEP_IT_REAL, we are in 32-bit protected mode with a
* fixed link address but an unknown physical start address. Our GDT
* sets up code and data segments with an offset of virt_offset, so
* that link-time addresses can still work.
*
*/
/* Variables in virtaddr.S */
extern unsigned long virt_offset;
#else /* KEEP_IT_REAL */
#include <stdint.h>
/*
* With -DKEEP_IT_REAL, we are in 16-bit real mode with fixed link
* addresses and a segmented memory model. We have separate code and
* data segments.
*
* Because we may be called in 16-bit protected mode (damn PXE spec),
* we cannot simply assume that physical = segment * 16 + offset.
* Instead, we have to look up the physical start address of the
* segment in the !PXE structure. We have to assume that
* virt_to_phys() is called only on pointers within the data segment,
* because nothing passes segment information to us.
*
* We don't implement phys_to_virt at all, because there will be many
* addresses that simply cannot be reached via a virtual address when
* the virtual address space is limited to 64kB!
*/
static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) {
/* Cheat: just for now, do the segment*16+offset calculation */
uint16_t ds;
__asm__ ( "movw %%ds, %%ax" : "=a" ( ds ) : );
return ( 16 * ds + ( ( unsigned long ) virt_addr ) );
}
/* Define it as a deprecated function so that we get compile-time
* warnings, rather than just the link-time errors.
*/
extern void * phys_to_virt ( unsigned long phys_addr )
__attribute__ ((deprecated));
#endif /* KEEP_IT_REAL */
#endif /* ASSEMBLY */
#endif /* VIRTADDR_H */

View File

@ -1,45 +1,56 @@
/*
* librm: a library for interfacing to real-mode code
*
* Michael Brown <mbrown@fensystems.co.uk>
*
*/
#include <stdint.h>
#include <librm.h>
/*
* This file provides functions for managing librm.
*
*/
/**
* Allocate space on the real-mode stack and copy data there from a
* user buffer
*
* @v data User buffer
* @v size Size of stack data
* @ret sp New value of real-mode stack pointer
*/
uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
userptr_t rm_stack;
rm_sp -= size;
rm_stack = real_to_user ( rm_ss, rm_sp );
memcpy_user ( rm_stack, 0, data, 0, size );
return rm_sp;
};
/**
* Deallocate space on the real-mode stack, optionally copying back
* data to a user buffer.
*
* @v data User buffer
* @v size Size of stack data
*/
void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
if ( data ) {
userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
memcpy_user ( rm_stack, 0, data, 0, size );
}
rm_sp += size;
};
/*
* librm: a library for interfacing to real-mode code
*
* Michael Brown <mbrown@fensystems.co.uk>
*
*/
#include <stdint.h>
#include <realmode.h>
/*
* This file provides functions for managing librm.
*
*/
/**
* Allocate space on the real-mode stack and copy data there from a
* user buffer
*
* @v data User buffer
* @v size Size of stack data
* @ret sp New value of real-mode stack pointer
*/
uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
userptr_t rm_stack;
rm_sp -= size;
rm_stack = real_to_user ( rm_ss, rm_sp );
memcpy_user ( rm_stack, 0, data, 0, size );
return rm_sp;
};
/**
* Deallocate space on the real-mode stack, optionally copying back
* data to a user buffer.
*
* @v data User buffer
* @v size Size of stack data
*/
void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
if ( data ) {
userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
memcpy_user ( rm_stack, 0, data, 0, size );
}
rm_sp += size;
};
PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
PROVIDE_UACCESS_INLINE ( librm, userptr_add );
PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
PROVIDE_UACCESS_INLINE ( librm, memmove_user );
PROVIDE_UACCESS_INLINE ( librm, memset_user );
PROVIDE_UACCESS_INLINE ( librm, strlen_user );
PROVIDE_UACCESS_INLINE ( librm, memchr_user );

View File

@ -7,6 +7,7 @@
*
*/
#define UACCESS_LIBRM
#define IOAPI_X86
#define PCIAPI_PCBIOS
#define TIMER_PCBIOS

View File

@ -19,6 +19,7 @@
#include <stdint.h>
#include <gpxe/api.h>
#include <config/ioapi.h>
#include <gpxe/uaccess.h>
/**
* Calculate static inline I/O API function name
@ -149,24 +150,20 @@
} while ( 0 )
/**
* Convert virtual address to a physical address
* Convert physical address to a bus address
*
* @v addr Virtual address
* @ret phys_addr Physical address
* @v phys_addr Physical address
* @ret bus_addr Bus address
*/
unsigned long virt_to_phys ( volatile const void *addr );
unsigned long phys_to_bus ( unsigned long phys_addr );
/**
* Convert physical address to a virtual address
* Convert bus address to a physical address
*
* @v addr Virtual address
* @v bus_addr Bus address
* @ret phys_addr Physical address
*
* This operation isn't actually valid within our memory model, and is
* impossible to achieve under -DKEEP_IT_REAL. Some drivers haven't
* been updated to avoid it yet, though.
*/
void * phys_to_virt ( unsigned long phys_addr );
unsigned long bus_to_phys ( unsigned long bus_addr );
/**
* Convert virtual address to a bus address
@ -174,7 +171,10 @@ void * phys_to_virt ( unsigned long phys_addr );
* @v addr Virtual address
* @ret bus_addr Bus address
*/
unsigned long virt_to_bus ( volatile const void *addr );
static inline __always_inline unsigned long
virt_to_bus ( volatile const void *addr ) {
return phys_to_bus ( virt_to_phys ( addr ) );
}
/**
* Convert bus address to a virtual address
@ -182,11 +182,11 @@ unsigned long virt_to_bus ( volatile const void *addr );
* @v bus_addr Bus address
* @ret addr Virtual address
*
* This operation isn't actually valid within our memory model, and is
* impossible to achieve under -DKEEP_IT_REAL. Some drivers haven't
* been updated to avoid it yet, though.
* This operation is not available under all memory models.
*/
void * bus_to_virt ( unsigned long bus_addr );
static inline __always_inline void * bus_to_virt ( unsigned long bus_addr ) {
return phys_to_virt ( bus_to_phys ( bus_addr ) );
}
/**
* Map bus address as an I/O address

View File

@ -19,9 +19,323 @@
*
*/
#include <bits/uaccess.h>
#include <stdint.h>
#include <string.h>
#include <gpxe/api.h>
#include <config/ioapi.h>
/**
* A pointer to a user buffer
*
*/
typedef unsigned long userptr_t;
/** Equivalent of NULL for user pointers */
#define UNULL ( ( userptr_t ) 0 )
/**
* @defgroup uaccess_trivial Trivial user access API implementations
*
* User access API implementations that can be used by environments in
* which virtual addresses allow access to all of memory.
*
* @{
*
*/
/**
* Convert virtual address to user pointer
*
* @v addr Virtual address
* @ret userptr User pointer
*/
static inline __always_inline userptr_t
trivial_virt_to_user ( volatile const void *addr ) {
return ( ( userptr_t ) addr );
}
/**
* Convert user pointer to virtual address
*
* @v userptr User pointer
* @v offset Offset from user pointer
* @ret addr Virtual address
*
* This operation is not available under all memory models.
*/
static inline __always_inline void *
trivial_user_to_virt ( userptr_t userptr, off_t offset ) {
return ( ( void * ) userptr + offset );
}
/**
* Add offset to user pointer
*
* @v userptr User pointer
* @v offset Offset
* @ret userptr New pointer value
*/
static inline __always_inline userptr_t
trivial_userptr_add ( userptr_t userptr, off_t offset ) {
return ( userptr + offset );
}
/**
* Copy data between user buffers
*
* @v dest Destination
* @v dest_off Destination offset
* @v src Source
* @v src_off Source offset
* @v len Length
*/
static inline __always_inline void
trivial_memcpy_user ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off, size_t len ) {
memcpy ( ( ( void * ) dest + dest_off ),
( ( void * ) src + src_off ), len );
}
/**
* Copy data between user buffers, allowing for overlap
*
* @v dest Destination
* @v dest_off Destination offset
* @v src Source
* @v src_off Source offset
* @v len Length
*/
static inline __always_inline void
trivial_memmove_user ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off, size_t len ) {
memmove ( ( ( void * ) dest + dest_off ),
( ( void * ) src + src_off ), len );
}
/**
* Fill user buffer with a constant byte
*
* @v buffer User buffer
* @v offset Offset within buffer
* @v c Constant byte with which to fill
* @v len Length
*/
static inline __always_inline void
trivial_memset_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
memset ( ( ( void * ) buffer + offset ), c, len );
}
/**
* Find length of NUL-terminated string in user buffer
*
* @v buffer User buffer
* @v offset Offset within buffer
* @ret len Length of string (excluding NUL)
*/
static inline __always_inline size_t
trivial_strlen_user ( userptr_t buffer, off_t offset ) {
return strlen ( ( void * ) buffer + offset );
}
/**
* Find character in user buffer
*
* @v buffer User buffer
* @v offset Starting offset within buffer
* @v c Character to search for
* @v len Length of user buffer
* @ret offset Offset of character, or <0 if not found
*/
static inline __always_inline off_t
trivial_memchr_user ( userptr_t buffer, off_t offset, int c, size_t len ) {
void *found;
found = memchr ( ( ( void * ) buffer + offset ), c, len );
return ( found ? ( found - ( void * ) buffer ) : -1 );
}
/** @} */
/**
* Calculate static inline user access API function name
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @ret _subsys_func Subsystem API function
*/
#define UACCESS_INLINE( _subsys, _api_func ) \
SINGLE_API_INLINE ( UACCESS_PREFIX_ ## _subsys, _api_func )
/**
* Provide an user access API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @v _func Implementing function
*/
#define PROVIDE_UACCESS( _subsys, _api_func, _func ) \
PROVIDE_SINGLE_API ( UACCESS_PREFIX_ ## _subsys, _api_func, _func )
/**
* Provide a static inline user access API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
*/
#define PROVIDE_UACCESS_INLINE( _subsys, _api_func ) \
PROVIDE_SINGLE_API_INLINE ( UACCESS_PREFIX_ ## _subsys, _api_func )
/* Include all architecture-independent user access API headers */
/* Include all architecture-dependent user access API headers */
#include <bits/uaccess.h>
/**
* Convert physical address to user pointer
*
* @v phys_addr Physical address
* @ret userptr User pointer
*/
userptr_t phys_to_user ( unsigned long phys_addr );
/**
* Convert user pointer to physical address
*
* @v userptr User pointer
* @v offset Offset from user pointer
* @ret phys_addr Physical address
*/
unsigned long user_to_phys ( userptr_t userptr, off_t offset );
/**
* Convert virtual address to user pointer
*
* @v addr Virtual address
* @ret userptr User pointer
*/
userptr_t virt_to_user ( volatile const void *addr );
/**
* Convert user pointer to virtual address
*
* @v userptr User pointer
* @v offset Offset from user pointer
* @ret addr Virtual address
*
* This operation is not available under all memory models.
*/
void * user_to_virt ( userptr_t userptr, off_t offset );
/**
* Add offset to user pointer
*
* @v userptr User pointer
* @v offset Offset
* @ret userptr New pointer value
*/
userptr_t userptr_add ( userptr_t userptr, off_t offset );
/**
* Convert virtual address to a physical address
*
* @v addr Virtual address
* @ret phys_addr Physical address
*/
static inline __always_inline unsigned long
virt_to_phys ( volatile const void *addr ) {
return user_to_phys ( virt_to_user ( addr ), 0 );
}
/**
* Convert physical address to a virtual address
*
* @v addr Virtual address
* @ret phys_addr Physical address
*
* This operation is not available under all memory models.
*/
static inline __always_inline void * phys_to_virt ( unsigned long phys_addr ) {
return user_to_virt ( phys_to_user ( phys_addr ), 0 );
}
/**
* Copy data between user buffers
*
* @v dest Destination
* @v dest_off Destination offset
* @v src Source
* @v src_off Source offset
* @v len Length
*/
void memcpy_user ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off, size_t len );
/**
* Copy data to user buffer
*
* @v dest Destination
* @v dest_off Destination offset
* @v src Source
* @v len Length
*/
static inline __always_inline void
copy_to_user ( userptr_t dest, off_t dest_off, const void *src, size_t len ) {
memcpy_user ( dest, dest_off, virt_to_user ( src ), 0, len );
}
/**
* Copy data from user buffer
*
* @v dest Destination
* @v src Source
* @v src_off Source offset
* @v len Length
*/
static inline __always_inline void
copy_from_user ( void *dest, userptr_t src, off_t src_off, size_t len ) {
memcpy_user ( virt_to_user ( dest ), 0, src, src_off, len );
}
/**
* Copy data between user buffers, allowing for overlap
*
* @v dest Destination
* @v dest_off Destination offset
* @v src Source
* @v src_off Source offset
* @v len Length
*/
void memmove_user ( userptr_t dest, off_t dest_off,
userptr_t src, off_t src_off, size_t len );
/**
* Fill user buffer with a constant byte
*
* @v userptr User buffer
* @v offset Offset within buffer
* @v c Constant byte with which to fill
* @v len Length
*/
void memset_user ( userptr_t userptr, off_t offset, int c, size_t len );
/**
* Find length of NUL-terminated string in user buffer
*
* @v userptr User buffer
* @v offset Offset within buffer
* @ret len Length of string (excluding NUL)
*/
size_t strlen_user ( userptr_t userptr, off_t offset );
/**
* Find character in user buffer
*
* @v userptr User buffer
* @v offset Starting offset within buffer
* @v c Character to search for
* @v len Length of user buffer
* @ret offset Offset of character, or <0 if not found
*/
off_t memchr_user ( userptr_t userptr, off_t offset, int c, size_t len );
#endif /* _GPXE_UACCESS_H */

View File

@ -7,9 +7,8 @@
*
*/
#include "stdint.h"
#include "pxe_addr.h" /* Architecture-specific PXE definitions */
#include "errno.h" /* PXE status codes */
#include <stdint.h>
#include <errno.h> /* PXE status codes */
/** @addtogroup pxe Preboot eXecution Environment (PXE) API
* @{