Initial AoE implementation. Limitations are:
authorMichael Brown <mcb30@etherboot.org>
Sun, 28 May 2006 23:29:43 +0000 (23:29 +0000)
committerMichael Brown <mcb30@etherboot.org>
Sun, 28 May 2006 23:29:43 +0000 (23:29 +0000)
  Cannot yet handle reads of more than two sectors

  No retransmission

  No way to find out a target's MAC address (this proof of concept uses
  broadcasts)

These limitations shall not last long!  :)

src/drivers/ata/aoedev.c [new file with mode: 0644]
src/include/gpxe/aoe.h [new file with mode: 0644]
src/net/aoe.c [new file with mode: 0644]

diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c
new file mode 100644 (file)
index 0000000..8db484b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <gpxe/aoe.h>
+
+/** @file
+ *
+ * AoE ATA device
+ *
+ */
+
+/**
+ * Issue ATA command via AoE device
+ *
+ * @v ata              ATA device
+ * @v command          ATA command
+ * @ret rc             Return status code
+ */
+static int aoe_command ( struct ata_device *ata,
+                        struct ata_command *command ) {
+       struct aoe_device *aoedev
+               = container_of ( ata, struct aoe_device, ata );
+
+       return aoe_issue ( &aoedev->aoe, command );
+}
+
+/**
+ * Initialise AoE device
+ *
+ * @v aoedev           AoE device
+ */
+int init_aoedev ( struct aoe_device *aoedev ) {
+       aoedev->ata.command = aoe_command;
+       aoe_open ( &aoedev->aoe );
+       return init_atadev ( &aoedev->ata );
+}
diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h
new file mode 100644 (file)
index 0000000..094feb5
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef _GPXE_AOE_H
+#define _GPXE_AOE_H
+
+/** @file
+ *
+ * AoE protocol
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/list.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ata.h>
+
+/** An AoE ATA command */
+struct aoecmd {
+       /** AoE command flags */
+       uint8_t aflags;
+       /** ATA error/feature register */
+       uint8_t err_feat;
+       /** ATA sector count register */
+       uint8_t count;
+       /** ATA command/status register */
+       uint8_t cmd_stat;
+       /** Logical block address, in little-endian order */
+       union {
+               uint64_t u64;
+               uint8_t bytes[6];
+       } lba;
+       /** Data payload */
+       uint8_t data[0];
+} __attribute__ (( packed ));
+
+#define AOE_FL_EXTENDED        0x40    /**< LBA48 extended addressing */
+#define AOE_FL_DEV_HEAD        0x10    /**< Device/head flag */
+#define AOE_FL_ASYNC   0x02    /**< Asynchronous write */
+#define AOE_FL_WRITE   0x01    /**< Write command */
+
+/** An AoE header */
+struct aoehdr {
+       /** Protocol version number and flags */
+       uint8_t ver_flags;
+       /** Error code */
+       uint8_t error;
+       /** Major device number, in network byte order */
+       uint16_t major;
+       /** Minor device number */
+       uint8_t minor;
+       /** Command number */
+       uint8_t command;
+       /** Tag, in network byte order */
+       uint32_t tag;
+       /** Payload */
+       union {
+               /** ATA command */
+               struct aoecmd command[0];
+       } arg;
+} __attribute__ (( packed ));
+
+#define AOE_VERSION    0x10    /**< Version 1 */
+#define AOE_VERSION_MASK 0xf0  /**< Version part of ver_flags field */
+
+#define AOE_FL_RESPONSE        0x08    /**< Message is a response */
+#define AOE_FL_ERROR   0x04    /**< Command generated an error */
+
+#define AOE_MAJOR_BROADCAST 0xffff
+#define AOE_MINOR_BROADCAST 0xff
+
+#define AOE_CMD_ATA    0x00    /**< Issue ATA command */
+#define AOE_CMD_CONFIG 0x01    /**< Query Config Information */
+
+#define AOE_ERR_BAD_COMMAND    1 /**< Unrecognised command code */
+#define AOE_ERR_BAD_PARAMETER  2 /**< Bad argument parameter */
+#define AOE_ERR_UNAVAILABLE    3 /**< Device unavailable */
+#define AOE_ERR_CONFIG_EXISTS  4 /**< Config string present */
+#define AOE_ERR_BAD_VERSION    5 /**< Unsupported version */
+
+/** An AoE session */
+struct aoe_session {
+       /** List of all AoE sessions */
+       struct list_head list;
+
+       /** Network device */
+       struct net_device *netdev;
+       /** Major number */
+       uint16_t major;
+       /** Minor number */
+       uint8_t minor;
+       /** Target MAC address */
+       uint8_t target[ETH_ALEN];
+
+       /** Tag for current command */
+       uint32_t tag;
+       /** Current ATA command */
+       struct ata_command *command;
+       /** Status of the command */
+       int status;
+       /** Byte offset within command's data buffer */
+       unsigned int command_offset;
+};
+
+#define AOE_STATUS_ERR_MASK    0x0f /**< Error portion of status code */ 
+#define AOE_STATUS_PENDING     0x80 /**< Command pending */
+#define AOE_STATUS_UNDERRUN    0x40 /**< Buffer overrun */
+#define AOE_STATUS_OVERRUN     0x20 /**< Buffer underrun */
+
+/** Maximum number of sectors per packet */
+#define AOE_MAX_COUNT 2
+
+extern void aoe_open ( struct aoe_session *aoe );
+extern void aoe_close ( struct aoe_session *aoe );
+extern int aoe_issue ( struct aoe_session *aoe, struct ata_command *command );
+
+/** An AoE device */
+struct aoe_device {
+       /** ATA device interface */
+       struct ata_device ata;
+       /** AoE protocol instance */
+       struct aoe_session aoe;
+};
+
+extern int init_aoedev ( struct aoe_device *aoedev );
+
+#endif /* _GPXE_AOE_H */
diff --git a/src/net/aoe.c b/src/net/aoe.c
new file mode 100644 (file)
index 0000000..e1c9393
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <vsprintf.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <gpxe/list.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/pkbuff.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/ata.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/process.h>
+#include <gpxe/aoe.h>
+
+/** @file
+ *
+ * AoE protocol
+ *
+ */
+
+struct net_protocol aoe_protocol;
+
+/** List of all AoE sessions */
+static LIST_HEAD ( aoe_sessions );
+
+/**
+ * Send AoE command
+ *
+ * @v aoe              AoE session
+ * @ret rc             Return status code
+ *
+ * This transmits an AoE command packet.  It does not wait for a
+ * response.
+ */
+static int aoe_send_command ( struct aoe_session *aoe ) {
+       struct ata_command *command = aoe->command;
+       struct pk_buff *pkb;
+       struct aoehdr *aoehdr;
+       struct aoecmd *aoecmd;
+
+       /* Create outgoing packet buffer */
+       pkb = alloc_pkb ( ETH_HLEN + sizeof ( *aoehdr ) + sizeof ( *aoecmd ) +
+                         command->data_out_len );
+       if ( ! pkb )
+               return -ENOMEM;
+       pkb->net_protocol = &aoe_protocol;
+       pkb_reserve ( pkb, ETH_HLEN );
+       aoehdr = pkb_put ( pkb, sizeof ( *aoehdr ) );
+       aoecmd = pkb_put ( pkb, sizeof ( *aoecmd ) );
+       memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) );
+
+       /* Fill AoE header */
+       aoehdr->ver_flags = AOE_VERSION;
+       aoehdr->major = htons ( aoe->major );
+       aoehdr->minor = aoe->minor;
+       aoehdr->tag = htonl ( ++aoe->tag );
+
+       /* Fill AoE command */
+       linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ );
+       aoecmd->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) |
+                          ( command->cb.device & ATA_DEV_SLAVE ) |
+                          ( command->data_out_len ? AOE_FL_WRITE : 0 ) );
+       aoecmd->err_feat = command->cb.err_feat.bytes.cur;
+       aoecmd->count = command->cb.count.bytes.cur;
+       aoecmd->cmd_stat = command->cb.cmd_stat;
+       aoecmd->lba.u64 = cpu_to_le64 ( command->cb.lba.native );
+       if ( ! command->cb.lba48 )
+               aoecmd->lba.bytes[3] |= command->cb.device;
+
+       /* Fill data payload */
+       copy_from_user ( pkb_put ( pkb, command->data_out_len ),
+                        command->data_out, aoe->command_offset,
+                        command->data_out_len );
+
+       /* Send packet */
+       return net_transmit_via ( pkb, aoe->netdev );
+}
+
+/**
+ * Handle AoE response
+ *
+ * @v aoe              AoE session
+ * @v aoehdr           AoE header
+ * @ret rc             Return status code
+ */
+static int aoe_rx_response ( struct aoe_session *aoe, struct aoehdr *aoehdr,
+                            unsigned int len ) {
+       struct aoecmd *aoecmd = aoehdr->arg.command;
+       struct ata_command *command = aoe->command;
+       unsigned int data_in_len;
+       
+       /* Sanity check */
+       if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecmd ) ) )
+               return -EINVAL;
+
+       /* Set overall status code */
+       aoe->status = ( ( aoehdr->ver_flags & AOE_FL_ERROR ) ?
+                       aoehdr->error : 0 );
+
+       /* Copy ATA results */
+       command->cb.err_feat.bytes.cur = aoecmd->err_feat;
+       command->cb.count.bytes.cur = aoecmd->count;
+       command->cb.cmd_stat = aoecmd->cmd_stat;
+       command->cb.lba.native = le64_to_cpu ( aoecmd->lba.u64 );
+       command->cb.lba.bytes.pad = 0;
+       
+       /* Copy data payload */
+       data_in_len = ( len - sizeof ( *aoehdr ) - sizeof ( *aoecmd ) );
+       if ( data_in_len > command->data_in_len ) {
+               data_in_len = command->data_in_len;
+               aoe->status |= AOE_STATUS_OVERRUN;
+       } else if ( data_in_len < command->data_in_len ) {
+               aoe->status |= AOE_STATUS_UNDERRUN;
+       }
+       copy_to_user ( command->data_in, aoe->command_offset,
+                      aoecmd->data, data_in_len );
+
+       return 0;
+}
+
+/**
+ * Process incoming AoE packets
+ *
+ * @v pkb              Packet buffer
+ * @ret rc             Return status code
+ *
+ */
+static int aoe_rx ( struct pk_buff *pkb ) {
+       struct aoehdr *aoehdr = pkb->data;
+       unsigned int len = pkb_len ( pkb );
+       struct aoe_session *aoe;
+       int rc = 0;
+
+       /* Sanity checks */
+       if ( len < sizeof ( *aoehdr ) ) {
+               rc = -EINVAL;
+               goto done;
+       }
+       if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) {
+               rc = -EPROTONOSUPPORT;
+               goto done;
+       }
+       if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) {
+               /* Ignore AoE requests that we happen to see */
+               goto done;
+       }
+
+       /* Demultiplex amongst active AoE sessions */
+       list_for_each_entry ( aoe, &aoe_sessions, list ) {
+               if ( ntohs ( aoehdr->major ) != aoe->major )
+                       continue;
+               if ( aoehdr->minor != aoe->minor )
+                       continue;
+               if ( ntohl ( aoehdr->tag ) != aoe->tag )
+                       continue;
+
+#warning "Need a way to get the MAC address for future reference"
+
+               rc = aoe_rx_response ( aoe, aoehdr, len );
+               break;
+       }
+
+ done:
+       free_pkb ( pkb );
+       return rc;
+}
+
+/**
+ * Perform AoE network-layer routing
+ *
+ * @v pkb      Packet buffer
+ * @ret source Network-layer source address
+ * @ret dest   Network-layer destination address
+ * @ret rc     Return status code
+ */
+static int aoe_route ( const struct pk_buff *pkb __unused,
+                      struct net_header *nethdr ) {
+
+#warning "Need a way to find out the MAC address"
+       nethdr->flags = PKT_FL_BROADCAST;
+       return 0;
+}
+
+/** AoE protocol */
+struct net_protocol aoe_protocol = {
+       .name = "AoE",
+       .net_proto = htons ( ETH_P_AOE ),
+       .rx_process = aoe_rx,
+       .route = aoe_route,
+};
+
+NET_PROTOCOL ( aoe_protocol );
+
+/**
+ * Open AoE session
+ *
+ * @v aoe              AoE session
+ */
+void aoe_open ( struct aoe_session *aoe ) {
+       list_add ( &aoe->list, &aoe_sessions );
+}
+
+/**
+ * Close AoE session
+ *
+ * @v aoe              AoE session
+ */
+void aoe_close ( struct aoe_session *aoe ) {
+       list_del ( &aoe->list );
+}
+
+/**
+ * Kick an AoE session into life
+ *
+ * @v aoe              AoE session
+ *
+ * Transmits an AoE request.  Call this function to issue a new
+ * command, or when a retransmission timer expires.
+ */
+void aoe_kick ( struct aoe_session *aoe ) {
+       aoe_send_command ( aoe );
+}
+
+/**
+ * Issue ATA command via an open AoE session
+ *
+ * @v aoe              AoE session
+ * @v command          AoE command
+ * @ret rc             Return status code
+ */
+int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ) {
+       aoe->command = command;
+       aoe->status = AOE_STATUS_PENDING;
+
+       aoe_kick ( aoe );
+       while ( aoe->status & AOE_STATUS_PENDING ) {
+               step();
+       }
+       aoe->command = NULL;
+
+       return ( ( aoe->status & AOE_STATUS_ERR_MASK ) ? -EIO : 0 );
+}