diff --git a/src/arch/i386/core/pic8259.c b/src/arch/i386/core/pic8259.c index 5c00deed..411e9419 100644 --- a/src/arch/i386/core/pic8259.c +++ b/src/arch/i386/core/pic8259.c @@ -6,7 +6,7 @@ #include #include "pic8259.h" -#include "realmode.h" +#include "old_realmode.h" /* State of trivial IRQ handler */ irq_t trivial_irq_installed_on = IRQ_NONE; diff --git a/src/arch/i386/include/bits/uaccess.h b/src/arch/i386/include/bits/uaccess.h new file mode 100644 index 00000000..9c6d0c21 --- /dev/null +++ b/src/arch/i386/include/bits/uaccess.h @@ -0,0 +1,6 @@ +#ifndef _BITS_UACCESS_H +#define _BITS_UACCESS_H + +#include + +#endif /* _BITS_UACCESS_H */ diff --git a/src/arch/i386/include/librm.h b/src/arch/i386/include/librm.h index af1470b9..17fcc78b 100644 --- a/src/arch/i386/include/librm.h +++ b/src/arch/i386/include/librm.h @@ -92,6 +92,69 @@ copy_from_real_librm ( void *dest, unsigned int src_seg, #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; + +/** + * 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 ); +} + +/** + * 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 + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @ret buffer User buffer + */ +static inline __attribute__ (( always_inline )) userptr_t +real_to_user ( unsigned int segment, unsigned int offset ) { + return virt_to_user ( VIRTUAL ( segment, offset ) ); +} + /* Copy to/from real-mode stack */ extern uint16_t copy_to_rm_stack ( void *data, size_t size ); extern void remove_from_rm_stack ( void *data, size_t size ); diff --git a/src/arch/i386/include/old_realmode.h b/src/arch/i386/include/old_realmode.h new file mode 100644 index 00000000..3dde8c81 --- /dev/null +++ b/src/arch/i386/include/old_realmode.h @@ -0,0 +1,22 @@ +#ifndef _OLD_REALMODE_H +#define _OLD_REALMODE_H + +#include + +#warning "Anything including this header is obsolete and must be rewritten" + +/* Just for now */ +#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 ) +#define OFFSET(x) ( virt_to_phys ( x ) & 0xf ) +#define SEGOFF(x) { OFFSET(x), SEGMENT(x) } + +/* To make basemem.c compile */ +extern int lock_real_mode_stack; +extern char *real_mode_stack; +extern char real_mode_stack_size[]; + +#define RM_FRAGMENT(name,asm) \ + void name ( void ) {} \ + extern char name ## _size[]; + +#endif /* _OLD_REALMODE_H */ diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h index 07d41e54..10a28b23 100644 --- a/src/arch/i386/include/realmode.h +++ b/src/arch/i386/include/realmode.h @@ -149,25 +149,6 @@ typedef struct segoff segoff_t; * "(discard)" in the above code. */ -#warning "realmode.h contains placeholders for obsolete macros" - - -/* Just for now */ -#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 ) -#define OFFSET(x) ( virt_to_phys ( x ) & 0xf ) -#define SEGOFF(x) { OFFSET(x), SEGMENT(x) } - -/* To make basemem.c compile */ -extern int lock_real_mode_stack; -extern char *real_mode_stack; -extern char real_mode_stack_size[]; - -#define RM_FRAGMENT(name,asm) \ - void name ( void ) {} \ - extern char name ## _size[]; - - - #endif /* ASSEMBLY */ #endif /* REALMODE_H */ diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index d4d15c16..d87428f7 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -93,7 +93,7 @@ static unsigned long chs_to_lba ( struct int13_drive *drive, lba = ( ( ( ( cylinder * drive->heads ) + head ) * drive->sectors_per_track ) + sector - 1 ); - DBG ( "C/H/S address %x/%x/%x -> LBA %x\n", + DBG ( "C/H/S address %x/%x/%x -> LBA %lx\n", cylinder, head, sector, lba ); return lba; @@ -111,19 +111,15 @@ static unsigned long chs_to_lba ( struct int13_drive *drive, static int int13_read ( struct int13_drive *drive, uint64_t lba, struct segoff data, unsigned long count ) { struct block_device *blockdev = drive->blockdev; - size_t blksize = blockdev->blksize; - uint8_t buffer[blksize]; + userptr_t buffer = real_to_user ( data.segment, data.offset ); int rc; DBG ( "Read %lx sectors from %llx to %04x:%04x\n", count, ( unsigned long long ) lba, data.segment, data.offset ); - while ( count-- ) { - if ( ( rc = blockdev->read ( blockdev, lba, buffer ) ) != 0 ) - return INT13_STATUS_READ_ERROR; - copy_to_real ( data.segment, data.offset, buffer, blksize ); - data.offset += blksize; - lba++; - } + + if ( ( rc = blockdev->read ( blockdev, lba, count, buffer ) ) != 0 ) + return INT13_STATUS_READ_ERROR; + return 0; } @@ -139,19 +135,15 @@ static int int13_read ( struct int13_drive *drive, uint64_t lba, static int int13_write ( struct int13_drive *drive, uint64_t lba, struct segoff data, unsigned long count ) { struct block_device *blockdev = drive->blockdev; - size_t blksize = blockdev->blksize; - uint8_t buffer[blksize]; + userptr_t buffer = real_to_user ( data.segment, data.offset ); int rc; DBG ( "Write %lx sectors from %04x:%04x to %llx\n", count, data.segment, data.offset, ( unsigned long long ) lba ); - while ( count-- ) { - copy_from_real ( buffer, data.segment, data.offset, blksize ); - if ( ( rc = blockdev->write ( blockdev, lba, buffer ) ) != 0 ) - return INT13_STATUS_WRITE_ERROR; - data.offset += blksize; - lba++; - } + + if ( ( rc = blockdev->write ( blockdev, lba, count, buffer ) ) != 0 ) + return INT13_STATUS_WRITE_ERROR; + return 0; } @@ -202,7 +194,7 @@ static int int13_read_sectors ( struct int13_drive *drive, }; if ( drive->blockdev->blksize != INT13_BLKSIZE ) { - DBG ( "Invalid blocksize (%d) for non-extended read\n", + DBG ( "Invalid blocksize (%zd) for non-extended read\n", drive->blockdev->blksize ); return INT13_STATUS_INVALID; } @@ -233,7 +225,7 @@ static int int13_write_sectors ( struct int13_drive *drive, }; if ( drive->blockdev->blksize != INT13_BLKSIZE ) { - DBG ( "Invalid blocksize (%d) for non-extended write\n", + DBG ( "Invalid blocksize (%zd) for non-extended write\n", drive->blockdev->blksize ); return INT13_STATUS_INVALID; } diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 7ddc4a11..1a1d70d8 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -50,11 +50,12 @@ static int scsi_command ( struct scsi_device *scsi, * * @v blockdev Block device * @v block LBA block number + * @v count Block count * @v buffer Data buffer * @ret rc Return status code */ static int scsi_read ( struct block_device *blockdev, uint64_t block, - void *buffer ) { + unsigned long count, userptr_t buffer ) { struct scsi_device *scsi = block_to_scsi ( blockdev ); struct scsi_command command; struct scsi_cdb_read_16 *cdb = &command.cdb.read16; @@ -63,9 +64,9 @@ static int scsi_read ( struct block_device *blockdev, uint64_t block, memset ( &command, 0, sizeof ( command ) ); cdb->opcode = SCSI_OPCODE_READ_16; cdb->lba = cpu_to_be64 ( block ); - cdb->len = cpu_to_be32 ( 1 ); /* always a single block */ + cdb->len = cpu_to_be32 ( count ); command.data_in = buffer; - command.data_in_len = blockdev->blksize; + command.data_in_len = ( count * blockdev->blksize ); return scsi_command ( scsi, &command ); } @@ -74,11 +75,12 @@ static int scsi_read ( struct block_device *blockdev, uint64_t block, * * @v blockdev Block device * @v block LBA block number + * @v count Block count * @v buffer Data buffer * @ret rc Return status code */ static int scsi_write ( struct block_device *blockdev, uint64_t block, - const void *buffer ) { + unsigned long count, userptr_t buffer ) { struct scsi_device *scsi = block_to_scsi ( blockdev ); struct scsi_command command; struct scsi_cdb_write_16 *cdb = &command.cdb.write16; @@ -87,9 +89,9 @@ static int scsi_write ( struct block_device *blockdev, uint64_t block, memset ( &command, 0, sizeof ( command ) ); cdb->opcode = SCSI_OPCODE_WRITE_16; cdb->lba = cpu_to_be64 ( block ); - cdb->len = cpu_to_be32 ( 1 ); /* always a single block */ + cdb->len = cpu_to_be32 ( count ); command.data_out = buffer; - command.data_out_len = blockdev->blksize; + command.data_out_len = ( count * blockdev->blksize ); return scsi_command ( scsi, &command ); } @@ -111,7 +113,7 @@ static int scsi_read_capacity ( struct block_device *blockdev ) { cdb->opcode = SCSI_OPCODE_SERVICE_ACTION_IN; cdb->service_action = SCSI_SERVICE_ACTION_READ_CAPACITY_16; cdb->len = cpu_to_be32 ( sizeof ( capacity ) ); - command.data_in = &capacity; + command.data_in = virt_to_user ( &capacity ); command.data_in_len = sizeof ( capacity ); if ( ( rc = scsi_command ( scsi, &command ) ) != 0 ) diff --git a/src/include/gpxe/blockdev.h b/src/include/gpxe/blockdev.h index 59f5bf74..467ed1d9 100644 --- a/src/include/gpxe/blockdev.h +++ b/src/include/gpxe/blockdev.h @@ -8,6 +8,8 @@ * */ +#include + /** A block device */ struct block_device { /** Block size */ @@ -19,21 +21,23 @@ struct block_device { * * @v blockdev Block device * @v block Block number + * @v count Block count * @v buffer Data buffer * @ret rc Return status code */ int ( * read ) ( struct block_device *blockdev, uint64_t block, - void *buffer ); + unsigned long count, userptr_t buffer ); /** * Write block * * @v blockdev Block device * @v block Block number + * @v count Block count * @v buffer Data buffer * @ret rc Return status code */ int ( * write ) ( struct block_device *blockdev, uint64_t block, - const void *buffer ); + unsigned long count, userptr_t buffer ); }; #endif /* _GPXE_BLOCKDEV_H */ diff --git a/src/include/gpxe/scsi.h b/src/include/gpxe/scsi.h index dea39e16..d7da1255 100644 --- a/src/include/gpxe/scsi.h +++ b/src/include/gpxe/scsi.h @@ -3,6 +3,7 @@ #include #include +#include /** * @defgroup scsiops SCSI operation codes @@ -123,14 +124,14 @@ struct scsi_command { /** CDB for this command */ union scsi_cdb cdb; /** Data-out buffer (may be NULL) */ - const void *data_out; + userptr_t data_out; /** Data-out buffer length * * Must be zero if @c data_out is NULL */ size_t data_out_len; /** Data-in buffer (may be NULL) */ - void *data_in; + userptr_t data_in; /** Data-in buffer length * * Must be zero if @c data_in is NULL diff --git a/src/include/gpxe/uaccess.h b/src/include/gpxe/uaccess.h new file mode 100644 index 00000000..38853bfb --- /dev/null +++ b/src/include/gpxe/uaccess.h @@ -0,0 +1,24 @@ +#ifndef _GPXE_UACCESS_H +#define _GPXE_UACCESS_H + +/** + * @file + * + * Access to external ("user") memory + * + * gPXE often needs to transfer data between internal and external + * buffers. On i386, the external buffers may require access via a + * different segment, and the buffer address cannot be encoded into a + * simple void * pointer. The @c userptr_t type encapsulates the + * information needed to identify an external buffer, and the + * copy_to_user() and copy_from_user() functions provide methods for + * transferring data between internal and external buffers. + * + * Note that userptr_t is an opaque type; in particular, performing + * arithmetic upon a userptr_t is not allowed. + * + */ + +#include + +#endif /* _GPXE_UACCESS_H */ diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 633c895c..d7c4b120 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /** @file @@ -130,7 +131,7 @@ static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data, assert ( iscsi->command != NULL ); assert ( iscsi->command->data_in != NULL ); assert ( ( offset + len ) <= iscsi->command->data_in_len ); - memcpy ( ( iscsi->command->data_in + offset ), data, len ); + copy_to_user ( iscsi->command->data_in, offset, data, len ); /* Record SCSI status, if present */ if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) @@ -234,7 +235,11 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi ) { assert ( iscsi->command->data_out != NULL ); assert ( ( offset + len ) <= iscsi->command->data_out_len ); - tcp_send ( &iscsi->tcp, iscsi->command->data_out + offset, len ); + if ( len > tcp_buflen ) + len = tcp_buflen; + copy_from_user ( tcp_buffer, iscsi->command->data_out, offset, len ); + + tcp_send ( &iscsi->tcp, tcp_buffer, len ); } /****************************************************************************