mirror of
https://github.com/xcat2/xNBA.git
synced 2025-03-01 16:50:32 +00:00
Add the concept of a "user pointer" (similar to the void __user * in
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).
This commit is contained in:
parent
0ab92faedb
commit
d48d0fb1bb
@ -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;
|
||||
|
6
src/arch/i386/include/bits/uaccess.h
Normal file
6
src/arch/i386/include/bits/uaccess.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _BITS_UACCESS_H
|
||||
#define _BITS_UACCESS_H
|
||||
|
||||
#include <realmode.h>
|
||||
|
||||
#endif /* _BITS_UACCESS_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 );
|
||||
|
22
src/arch/i386/include/old_realmode.h
Normal file
22
src/arch/i386/include/old_realmode.h
Normal file
@ -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 */
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
24
src/include/gpxe/uaccess.h
Normal file
24
src/include/gpxe/uaccess.h
Normal file
@ -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 */
|
@ -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 );
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
Loading…
x
Reference in New Issue
Block a user