Add the concept of a "user pointer" (similar to the void __user * in
authorMichael Brown <mcb30@etherboot.org>
Fri, 19 May 2006 15:06:51 +0000 (15:06 +0000)
committerMichael Brown <mcb30@etherboot.org>
Fri, 19 May 2006 15:06:51 +0000 (15:06 +0000)
the kernel), which encapsulates the information needed to refer to an
external buffer.  Under normal operation, this can just be a void *
equivalent, but under -DKEEP_IT_REAL it would be a segoff_t equivalent.

Use this concept to avoid the need for bounce buffers in int13.c,
which reduces memory usage and opens up the possibility of using
multi-sector reads.

Extend the block-device API and the SCSI block device implementation
to support multi-sector reads.

Update iscsi.c to use user buffers.

Move the obsolete portions of realmode.h to old_realmode.h.

MS-DOS now boots an order of magnitude faster over iSCSI (~10 seconds
from power-up to C:> prompt in bochs).

src/arch/i386/core/pic8259.c
src/arch/i386/include/bits/uaccess.h [new file with mode: 0644]
src/arch/i386/include/librm.h
src/arch/i386/include/old_realmode.h [new file with mode: 0644]
src/arch/i386/include/realmode.h
src/arch/i386/interface/pcbios/int13.c
src/drivers/block/scsi.c
src/include/gpxe/blockdev.h
src/include/gpxe/scsi.h
src/include/gpxe/uaccess.h [new file with mode: 0644]
src/net/tcp/iscsi.c

index 5c00dee..411e941 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <etherboot.h>
 #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 (file)
index 0000000..9c6d0c2
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _BITS_UACCESS_H
+#define _BITS_UACCESS_H
+
+#include <realmode.h>
+
+#endif /* _BITS_UACCESS_H */
index af1470b..17fcc78 100644 (file)
@@ -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 (file)
index 0000000..3dde8c8
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _OLD_REALMODE_H
+#define _OLD_REALMODE_H
+
+#include <realmode.h>
+
+#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 */
index 07d41e5..10a28b2 100644 (file)
@@ -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 */
index d4d15c1..d87428f 100644 (file)
@@ -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;
        }
index 7ddc4a1..1a1d70d 100644 (file)
@@ -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 )
index 59f5bf7..467ed1d 100644 (file)
@@ -8,6 +8,8 @@
  *
  */
 
+#include <gpxe/uaccess.h>
+
 /** 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 */
index dea39e1..d7da125 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdint.h>
 #include <gpxe/blockdev.h>
+#include <gpxe/uaccess.h>
 
 /**
  * @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 (file)
index 0000000..38853bf
--- /dev/null
@@ -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 <bits/uaccess.h>
+
+#endif /* _GPXE_UACCESS_H */
index 633c895..d7c4b12 100644 (file)
@@ -24,6 +24,7 @@
 #include <byteswap.h>
 #include <gpxe/scsi.h>
 #include <gpxe/process.h>
+#include <gpxe/uaccess.h>
 #include <gpxe/iscsi.h>
 
 /** @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 );
 }
 
 /****************************************************************************