Add alloc_iob() and free_iob().  (Direct search-and-replace
equivalents for alloc_pkb() and free_pkb(), which will be retired in
due course).
diff --git a/src/core/iobuf.c b/src/core/iobuf.c
new file mode 100644
index 0000000..d04ede5
--- /dev/null
+++ b/src/core/iobuf.c
@@ -0,0 +1,74 @@
+/*
+ * 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 <stdint.h>
+#include <gpxe/malloc.h>
+#include <gpxe/iobuf.h>
+
+/** @file
+ *
+ * I/O buffers
+ *
+ */
+
+/**
+ * Allocate I/O buffer
+ *
+ * @v len	Required length of buffer
+ * @ret iobuf	I/O buffer, or NULL if none available
+ *
+ * The I/O buffer will be physically aligned to a multiple of
+ * @c IOBUF_SIZE.
+ */
+struct io_buffer * alloc_iob ( size_t len ) {
+	struct io_buffer *iobuf = NULL;
+	void *data;
+
+	/* Pad to minimum length */
+	if ( len < IOB_ZLEN )
+		len = IOB_ZLEN;
+
+	/* Align buffer length */
+	len = ( len + __alignof__( *iobuf ) - 1 ) &
+		~( __alignof__( *iobuf ) - 1 );
+	
+	/* Allocate memory for buffer plus descriptor */
+	data = malloc_dma ( len + sizeof ( *iobuf ), IOB_ALIGN );
+	if ( ! data )
+		return NULL;
+
+	iobuf = ( struct io_buffer * ) ( data + len );
+	iobuf->head = iobuf->data = iobuf->tail = data;
+	iobuf->end = iobuf;
+	return iobuf;
+}
+
+/**
+ * Free I/O buffer
+ *
+ * @v iobuf	I/O buffer
+ */
+void free_iob ( struct io_buffer *iobuf ) {
+	if ( iobuf ) {
+		assert ( iobuf->head <= iobuf->data );
+		assert ( iobuf->data <= iobuf->tail );
+		assert ( iobuf->tail <= iobuf->end );
+		free_dma ( iobuf->head,
+			   ( iobuf->end - iobuf->head ) + sizeof ( *iobuf ) );
+	}
+}
diff --git a/src/include/gpxe/iobuf.h b/src/include/gpxe/iobuf.h
index cf99050..b5de6a5 100644
--- a/src/include/gpxe/iobuf.h
+++ b/src/include/gpxe/iobuf.h
@@ -12,6 +12,26 @@
 #include <gpxe/list.h>
 
 /**
+ * I/O buffer alignment
+ *
+ * I/O buffers allocated via alloc_iob() are guaranteed to be
+ * physically aligned to this boundary.  Some cards cannot DMA across
+ * a 4kB boundary.  With a standard Ethernet MTU, aligning to a 2kB
+ * boundary is sufficient to guarantee no 4kB boundary crossings.  For
+ * a jumbo Ethernet MTU, a packet may be larger than 4kB anyway.
+ */
+#define IOB_ALIGN 2048
+
+/**
+ * Minimum I/O buffer length
+ *
+ * alloc_iob() will round up the allocated length to this size if
+ * necessary.  This is used on behalf of hardware that is not capable
+ * of auto-padding.
+ */
+#define IOB_ZLEN 64
+
+/**
  * A persistent I/O buffer
  *
  * This data structure encapsulates a long-lived I/O buffer.  The
@@ -40,109 +60,109 @@
 /**
  * Reserve space at start of I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @v len	Length to reserve
  * @ret data	Pointer to new start of buffer
  */
-static inline void * iob_reserve ( struct io_buffer *iob, size_t len ) {
-	iob->data += len;
-	iob->tail += len;
-	assert ( iob->tail <= iob->end );
-	return iob->data;
+static inline void * iob_reserve ( struct io_buffer *iobuf, size_t len ) {
+	iobuf->data += len;
+	iobuf->tail += len;
+	assert ( iobuf->tail <= iobuf->end );
+	return iobuf->data;
 }
 
 /**
  * Add data to start of I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @v len	Length to add
  * @ret data	Pointer to new start of buffer
  */
-static inline void * iob_push ( struct io_buffer *iob, size_t len ) {
-	iob->data -= len;
-	assert ( iob->data >= iob->head );
-	return iob->data;
+static inline void * iob_push ( struct io_buffer *iobuf, size_t len ) {
+	iobuf->data -= len;
+	assert ( iobuf->data >= iobuf->head );
+	return iobuf->data;
 }
 
 /**
  * Remove data from start of I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @v len	Length to remove
  * @ret data	Pointer to new start of buffer
  */
-static inline void * iob_pull ( struct io_buffer *iob, size_t len ) {
-	iob->data += len;
-	assert ( iob->data <= iob->tail );
-	return iob->data;
+static inline void * iob_pull ( struct io_buffer *iobuf, size_t len ) {
+	iobuf->data += len;
+	assert ( iobuf->data <= iobuf->tail );
+	return iobuf->data;
 }
 
 /**
  * Add data to end of I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @v len	Length to add
  * @ret data	Pointer to newly added space
  */
-static inline void * iob_put ( struct io_buffer *iob, size_t len ) {
-	void *old_tail = iob->tail;
-	iob->tail += len;
-	assert ( iob->tail <= iob->end );
+static inline void * iob_put ( struct io_buffer *iobuf, size_t len ) {
+	void *old_tail = iobuf->tail;
+	iobuf->tail += len;
+	assert ( iobuf->tail <= iobuf->end );
 	return old_tail;
 }
 
 /**
  * Remove data from end of I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @v len	Length to remove
  */
-static inline void iob_unput ( struct io_buffer *iob, size_t len ) {
-	iob->tail -= len;
-	assert ( iob->tail >= iob->data );
+static inline void iob_unput ( struct io_buffer *iobuf, size_t len ) {
+	iobuf->tail -= len;
+	assert ( iobuf->tail >= iobuf->data );
 }
 
 /**
  * Empty an I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  */
-static inline void iob_empty ( struct io_buffer *iob ) {
-	iob->tail = iob->data;
+static inline void iob_empty ( struct io_buffer *iobuf ) {
+	iobuf->tail = iobuf->data;
 }
 
 /**
  * Calculate length of data in an I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @ret len	Length of data in buffer
  */
-static inline size_t iob_len ( struct io_buffer *iob ) {
-	return ( iob->tail - iob->data );
+static inline size_t iob_len ( struct io_buffer *iobuf ) {
+	return ( iobuf->tail - iobuf->data );
 }
 
 /**
  * Calculate available space at start of an I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @ret len	Length of data available at start of buffer
  */
-static inline size_t iob_headroom ( struct io_buffer *iob ) {
-	return ( iob->data - iob->head );
+static inline size_t iob_headroom ( struct io_buffer *iobuf ) {
+	return ( iobuf->data - iobuf->head );
 }
 
 /**
  * Calculate available space at end of an I/O buffer
  *
- * @v iob	I/O buffer
+ * @v iobuf	I/O buffer
  * @ret len	Length of data available at end of buffer
  */
-static inline size_t iob_tailroom ( struct io_buffer *iob ) {
-	return ( iob->end - iob->tail );
+static inline size_t iob_tailroom ( struct io_buffer *iobuf ) {
+	return ( iobuf->end - iobuf->tail );
 }
 
 extern struct io_buffer * alloc_iob ( size_t len );
-extern void free_iob ( struct io_buffer *iob );
-extern void iob_pad ( struct io_buffer *iob, size_t min_len );
+extern void free_iob ( struct io_buffer *iobuf );
+extern void iob_pad ( struct io_buffer *iobuf, size_t min_len );
 
 #endif /* _GPXE_IOBUF_H */