 headers/private/kernel/boot/net/iSCSITarget.h     |  352 +++++++++++
 src/system/boot/loader/net/Jamfile                |    3 +
 src/system/boot/loader/net/iSCSITarget.cpp        |  654 +++++++++++++++++++++
 src/system/boot/platform/openfirmware/devices.cpp |   54 ++-
 4 files changed, 1058 insertions(+), 5 deletions(-)

diff --git a/headers/private/kernel/boot/net/iSCSITarget.h b/headers/private/kernel/boot/net/iSCSITarget.h
new file mode 100644
index 0000000..736076a
--- /dev/null
+++ b/headers/private/kernel/boot/net/iSCSITarget.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2010, Andreas Faerber <andreas.faerber@web.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef BOOT_NET_ISCSITARGET_H
+#define BOOT_NET_ISCSITARGET_H
+
+#include <ByteOrder.h>
+#include <SupportDefs.h>
+#include <util/kernel_cpp.h>
+
+#include <boot/platform.h>
+#include <boot/stdio.h>
+#include <boot/vfs.h>
+#include <boot/net/NetDefs.h>
+
+#define ISCSI_PORT 3260
+
+// iSCSI Basic Header Segment (BHS) (RFC 3720 10.2.1)
+
+#define ISCSI_BHS_BYTE0 		\
+	bool reserved:1;			\
+	bool immediateDelivery:1;	\
+	uint8 opcode:6;
+#define ISCSI_BHS_START 		\
+	ISCSI_BHS_BYTE0				\
+	bool final:1;
+#define ISCSI_BHS_LENGTHS 		\
+	uint8 totalAHSLength; 		\
+	uint32 dataSegmentLength:24;
+#define ISCSI_BHS_TASK_TAG 		\
+	uint32 initiatorTaskTag;
+#define ISCSI_BHS_TAGS		\
+	ISCSI_BHS_TASK_TAG			\
+	uint32 targetTransferTag;
+
+struct iscsi_basic_header_segment {
+	ISCSI_BHS_START
+	uint32 opcodeSpecific:23;
+	ISCSI_BHS_LENGTHS
+	uint64 lun;
+	ISCSI_BHS_TASK_TAG
+	uint8 opcodeSpecific2[28];
+} __attribute__ ((__packed__));
+
+// initiator opcodes
+#define ISCSI_OPCODE_NOP_OUT		0x00
+#define ISCSI_OPCODE_SCSI_COMMAND	0x01
+#define ISCSI_OPCODE_LOGIN_REQUEST	0x03
+#define ISCSI_OPCODE_TEXT_REQUEST	0x04
+#define ISCSI_OPCODE_LOGOUT_REQUEST	0x06
+// target opcodes
+#define ISCSI_OPCODE_NOP_IN				0x20
+#define ISCSI_OPCODE_SCSI_RESPONSE		0x21
+#define ISCSI_OPCODE_LOGIN_RESPONSE 	0x23
+#define ISCSI_OPCODE_TEXT_RESPONSE		0x24
+#define ISCSI_OPCODE_LOGOUT_RESPONSE	0x26
+
+// SCSI Command (RFC 3720 10.3)
+struct iscsi_scsi_command {
+	iscsi_scsi_command()
+		:
+		reserved(0),
+		opcode(ISCSI_OPCODE_SCSI_COMMAND),
+		reserved2(0),
+		reserved3(0)
+	{
+	}
+	ISCSI_BHS_START
+	bool r:1;
+	bool w:1;
+	uint8 reserved2:2;
+	uint8 attr:3;
+	uint16 reserved3;
+	ISCSI_BHS_LENGTHS
+	uint64 lun;
+	ISCSI_BHS_TASK_TAG
+	uint32 expectedDataTransferLength;
+	uint32 cmdSN;
+	uint32 expStatSN;
+	uint8 cdb[16];
+} __attribute__ ((__packed__));
+
+// SCSI Response (RFC 3720 10.4)
+struct iscsi_scsi_response {
+	ISCSI_BHS_START
+	uint8 reserved2:2;
+	bool o:1;
+	bool u:1;
+	bool O:1;
+	bool U:1;
+	bool reserved3:1;
+	uint8 response;
+	uint8 status;
+	ISCSI_BHS_LENGTHS
+	uint32 reserved4[2];
+	ISCSI_BHS_TASK_TAG
+	uint32 snackTag;
+	uint32 statSN;
+	uint32 expCmdSN;
+	uint32 maxCmdSN;
+	uint32 expDataSN;
+	uint32 bidirectionalReadResidualCount;
+	uint32 residualCount;
+} __attribute__ ((__packed__));
+
+// Text Request (RFC 3720 10.10)
+struct iscsi_text_request {
+	iscsi_text_request()
+		:
+		reserved(0),
+		opcode(ISCSI_OPCODE_TEXT_REQUEST),
+		reserved2(2)
+	{
+		reserved3[0] = 0;
+		reserved3[1] = 0;
+		reserved3[2] = 0;
+		reserved3[3] = 0;
+	}
+	ISCSI_BHS_START
+	bool c:1; // continue
+	uint32 reserved2:22;
+	ISCSI_BHS_LENGTHS
+	uint64 lun;
+	ISCSI_BHS_TAGS
+	uint32 cmdSN;
+	uint32 expStatSN;
+	uint32 reserved3[4];
+} __attribute__ ((__packed__));
+
+// Text Response (RFC 3720 10.11)
+struct iscsi_text_response {
+	ISCSI_BHS_START
+	bool c:1; // continue
+	uint32 reserved2:22;
+	ISCSI_BHS_LENGTHS
+	uint64 lun;
+	ISCSI_BHS_TAGS
+	uint32 statSN;
+	uint32 expCmdSN;
+	uint32 maxCmdSN;
+	uint32 reserved3[3];
+} __attribute__ ((__packed__));
+
+struct iscsi_isid {
+	uint8 t:2;
+	uint8 a:6;
+	uint16 b;
+	uint8 c;
+	uint16 d;
+} __attribute__ ((__packed__));
+
+// Login Request (RFC 3720 10.12)
+struct iscsi_login_request {
+	iscsi_login_request()
+		:
+		reserved(false),
+		immediateDelivery(1),
+		opcode(ISCSI_OPCODE_LOGIN_REQUEST),
+		reserved2(0),
+		reserved3(0)
+	{
+		memset(reserved4, 0, sizeof(reserved4));
+	}
+
+	ISCSI_BHS_BYTE0
+	bool transit:1;
+	bool c:1;					// continue
+	uint8 reserved2:2;
+	uint8 currentStage:2;
+	uint8 nextStage:2;
+	uint8 versionMax;
+	uint8 versionMin;
+	ISCSI_BHS_LENGTHS
+	iscsi_isid isid;
+	uint16 tsih;
+	ISCSI_BHS_TASK_TAG
+	uint16 cid;
+	uint16 reserved3;
+	uint32 cmdSN;
+	uint32 expStatSN;
+	uint32 reserved4[4];
+} __attribute__ ((__packed__));
+
+#define ISCSI_SESSION_STAGE_SECURITY_NEGOTIATION			0
+#define ISCSI_SESSION_STAGE_LOGIN_OPERATIONAL_NEGOTIATION	1
+#define ISCSI_SESSION_STAGE_FULL_FEATURE_PHASE				3
+
+#define ISCSI_VERSION 0x00
+
+#define ISCSI_ISID_OUI		0
+#define ISCSI_ISID_EN		1
+#define ISCSI_ISID_RANDOM	2
+
+// Login Response (RFC 3720 10.13)
+struct iscsi_login_response {
+	ISCSI_BHS_BYTE0
+	bool transit:1;
+	bool c:1;					// continue
+	uint8 reserved2:2;
+	uint8 currentStage:2;
+	uint8 nextStage:2;
+	uint8 versionMax;
+	uint8 versionActive;
+	ISCSI_BHS_LENGTHS
+	iscsi_isid isid;
+	uint16 tsih;
+	ISCSI_BHS_TASK_TAG
+	uint32 reserved3;
+	uint32 statSN;
+	uint32 expCmdSN;
+	uint32 maxCmdSN;
+	uint8 statusClass;
+	uint8 statusDetail;
+	uint16 reserved4;
+	uint32 reserved5[2];
+} __attribute__ ((__packed__));
+
+// Logout Request (RFC 3720 10.14)
+struct iscsi_logout_request {
+	iscsi_logout_request()
+		:
+		reserved(0),
+		opcode(ISCSI_OPCODE_LOGOUT_REQUEST),
+		final(true),
+		reserved2(0),
+		reserved4(0)
+	{
+		reserved3[0] = 0;
+		reserved3[1] = 0;
+		reserved5[0] = 0;
+		reserved5[1] = 0;
+		reserved5[2] = 0;
+		reserved5[3] = 0;
+	}
+	ISCSI_BHS_START
+	uint8 reasonCode:7;
+	uint16 reserved2;
+	ISCSI_BHS_LENGTHS
+	uint32 reserved3[2];
+	ISCSI_BHS_TASK_TAG
+	uint16 cid;
+	uint16 reserved4;
+	uint32 cmdSN;
+	uint32 expStatSN;
+	uint32 reserved5[4];
+} __attribute__ ((__packed__));
+
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION		0
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION	1
+#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION	2
+
+// Logout Response (RFC 3720 10.15)
+struct iscsi_logout_response {
+	ISCSI_BHS_START
+	uint8 reserved2:7;
+	uint8 response;
+	uint8 reserved3;
+	ISCSI_BHS_LENGTHS
+	uint32 reserved4[2];
+	ISCSI_BHS_TASK_TAG
+	uint32 reserved5;
+	uint32 statSN;
+	uint32 expCmdSN;
+	uint32 maxCmdSN;
+	uint32 reserved6;
+	uint16 time2Wait;
+	uint16 time2Remain;
+	uint32 reserved7;
+} __attribute__ ((__packed__));
+
+class TCPSocket;
+
+class iSCSISession;
+
+class iSCSIConnection {
+public:
+	iSCSIConnection();
+	virtual ~iSCSIConnection();
+
+	status_t Init(iSCSISession* session);
+	status_t Open(ip_addr_t address, uint16 port);
+	status_t Login(const char* targetName = NULL, char** targetAlias = NULL);
+	status_t Logout(bool closeSession = false);
+	status_t GetText(const char* request, size_t requestLength, char** response, size_t* responseLength);
+	status_t SendCommand(const void* command, size_t commandSize,
+		bool r, bool w, uint32 expectedDataTransferLength,
+		void** response, size_t* responseLength);
+	status_t ReadData();
+
+	bool Active() const { return fConnected; }
+
+private:
+	status_t _ReadResponse(void* buffer, size_t bufferSize, size_t* bytesRead, bigtime_t timeout = 100000LL);
+
+	iSCSISession* fSession;
+	TCPSocket* fSocket;
+	bool fConnected;
+	uint16 fConnectionID;
+	uint32 fStatusSequenceNumber;
+};
+
+class iSCSISession {
+public:
+	iSCSISession(const char* targetName = NULL);
+	virtual ~iSCSISession();
+
+	status_t Init(ip_addr_t address, uint16 port, char** targetAlias = NULL);
+	status_t Close();
+
+	uint32 CommandSequenceNumber() const { return fCommandSequenceNumber; }
+	uint32 NextCommandSequenceNumber() { return fCommandSequenceNumber++; }
+	iSCSIConnection* Connection() const { return fConnection; }
+
+	bool Active() { return fConnection != NULL && fConnection->Active(); }
+
+private:
+	bool fDiscovery;
+	const char* fTargetName;
+	uint32 fCommandSequenceNumber;
+	iSCSIConnection* fConnection;//XXX should be multiple
+};
+
+class iSCSITarget : public Node {
+public:
+	iSCSITarget();
+	virtual ~iSCSITarget();
+	status_t Init(ip_addr_t address, uint16 port, const char* targetName);
+
+	virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
+		size_t bufferSize);
+	virtual ssize_t WriteAt(void* cookie, off_t pos, const void* buffer,
+		size_t bufferSize);
+
+	virtual status_t GetName(char* buffer, size_t bufferSize) const;
+	virtual off_t Size() const;
+
+	static bool DiscoverTargets(ip_addr_t address, uint16 port,
+		void (*targetCallback)(Node* node, void* opaque), void* opaque);
+	static bool _AddDevice(ip_addr_t address, uint16 port,
+		const char* targetName,
+		void (*callback)(Node* node, void* opaque), void* opaque);
+
+private:
+	iSCSISession* fSession;
+	char* fTargetName;
+	char* fTargetAlias;
+	uint32 fLastLBA;
+	uint32 fBlockSize;
+};
+
+#endif
diff --git a/src/system/boot/loader/net/Jamfile b/src/system/boot/loader/net/Jamfile
index 8555a7d..a0c54b2 100644
--- a/src/system/boot/loader/net/Jamfile
+++ b/src/system/boot/loader/net/Jamfile
@@ -1,6 +1,7 @@
 SubDir HAIKU_TOP src system boot loader net ;
 
 UsePrivateHeaders kernel [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ;
+UsePrivateHeaders drivers ;
 
 SubDirC++Flags -fno-rtti ;
 
@@ -13,6 +14,8 @@ KernelStaticLibrary boot_net :
 	NetStack.cpp
 	RemoteDisk.cpp
 	UDP.cpp
 	TCP.cpp
+	iSCSITarget.cpp
 
 	: -fno-pic
 ;
diff --git a/src/system/boot/loader/net/iSCSITarget.cpp b/src/system/boot/loader/net/iSCSITarget.cpp
new file mode 100644
index 0000000..bf4734b
--- /dev/null
+++ b/src/system/boot/loader/net/iSCSITarget.cpp
@@ -0,0 +1,654 @@
+/*
+ * Copyright 2010, Andreas Faerber <andreas.faerber@web.de>
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+
+#include <boot/net/iSCSITarget.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <KernelExport.h>
+
+#include <boot/net/ChainBuffer.h>
+#include <boot/net/TCP.h>
+#include <scsi_cmds.h>
+
+
+
+#define ISCSI_ALIGNMENT 4
+#define ISCSI_ALIGN(x) (((x) + ISCSI_ALIGNMENT - 1) & ~(ISCSI_ALIGNMENT - 1))
+#define ISCSI_PADDING(x) ((((x) % ISCSI_ALIGNMENT) == 0) ? 0 \
+	: (ISCSI_ALIGNMENT - ((x) % ISCSI_ALIGNMENT)))
+
+// derived from the schedulers
+static int
+_rand(void)
+{
+	static int next = 0;
+	if (next == 0)
+		next = system_time();
+
+	next = next * 1103515245 + 12345;
+	return next;
+}
+
+
+bool
+iSCSITarget::DiscoverTargets(ip_addr_t address, uint16 port,
+	void (*targetCallback)(Node* node, void* opaque), void* opaque)
+{
+	iSCSISession* session = new(nothrow) iSCSISession();
+	if (session == NULL)
+		return false;
+	status_t error = session->Init(address, port);
+	if (error != B_OK) {
+		delete session;
+		return false;
+	}
+	const char* request = "SendTargets=All";
+	char* sendTargets = NULL;
+	size_t sendTargetsLength = 0;
+	error = session->Connection()->GetText(request, strlen(request) + 1,
+		&sendTargets, &sendTargetsLength);
+	session->Close();
+	delete session;
+	if (error != B_OK) {
+		return false;
+	}
+	bool addedDisk = false;
+	char* targetName = NULL;
+	bool seenAddress = false;
+	for (char* pair = sendTargets; pair < sendTargets + sendTargetsLength;
+			pair += strlen(pair) + 1) {
+		printf("%s\n", pair);
+		if (strncmp(pair, "TargetName=", strlen("TargetName=")) == 0) {
+			if (targetName != NULL && !seenAddress) {
+				if (_AddDevice(address, port, targetName, targetCallback, opaque))
+					addedDisk = true;
+			}
+			seenAddress = false;
+			targetName = pair + strlen("TargetName=");
+		} else if (strncmp(pair, "TargetAddress=", strlen("TargetAddress="))
+				== 0) {
+			seenAddress = true;
+			char* targetAddress = pair + strlen("TargetAddress=");
+			char* comma = strrchr(targetAddress, ',');
+			int addressLength = comma - targetAddress;
+			targetAddress = strndup(targetAddress, addressLength);
+			uint16 targetPort = ISCSI_PORT;
+			char* colon = strrchr(targetAddress, ':');
+			if (colon != NULL) {
+				// colon could be part of an IPv6 address, e.g. [::1.2.3.4]
+				char* bracket = strrchr(targetAddress, ']');
+				if (bracket == NULL || bracket < colon) {
+					targetPort = strtol(colon + 1, NULL, 10);
+					colon[0] = '\0';
+				}
+			}
+			if (targetName != NULL && isdigit(targetAddress[0])) {
+				if (_AddDevice(ip_parse_address(targetAddress), targetPort,
+						targetName, targetCallback, opaque))
+					addedDisk = true;
+			}
+			free(targetAddress);
+		}
+	}
+	if (targetName != NULL && !seenAddress) {
+		if (_AddDevice(address, port, targetName, targetCallback, opaque))
+			addedDisk = true;
+	}
+	free(sendTargets);
+	return addedDisk;
+}
+
+
+bool
+iSCSITarget::_AddDevice(ip_addr_t address, uint16 port,
+	const char* targetName, void (*callback)(Node* node, void* opaque),
+	void* opaque)
+{
+	printf("=> %s @ %08lx:%u\n", targetName, address, port);
+	iSCSITarget* disk = new(nothrow) iSCSITarget();
+	if (disk == NULL)
+		return false;
+	status_t error = disk->Init(address, port, targetName);
+	if (error != B_OK) {
+		delete disk;
+		return false;
+	}
+	printf("disk size = %llu\n", disk->Size());
+	callback(disk, opaque);
+	return true;
+}
+
+
+iSCSITarget::iSCSITarget()
+	:
+	fSession(NULL),
+	fTargetName(NULL),
+	fTargetAlias(NULL)
+{
+}
+
+
+iSCSITarget::~iSCSITarget()
+{
+	free(fTargetName);
+	free(fTargetAlias);
+	delete fSession;
+}
+
+
+status_t
+iSCSITarget::Init(ip_addr_t address, uint16 port, const char* targetName)
+{
+	fTargetName = strdup(targetName);
+	if (fTargetName == NULL)
+		return B_NO_MEMORY;
+
+	fSession = new(nothrow) iSCSISession(fTargetName);
+	if (fSession == NULL)
+		return B_NO_MEMORY;
+	status_t error = fSession->Init(address, port, &fTargetAlias);
+	if (error != B_OK)
+		return error;
+	if (targetName == NULL)
+		return B_OK;
+
+	scsi_cmd_read_capacity cmd;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.opcode = SCSI_OP_READ_CAPACITY;
+	cmd.relative_address = 0;
+	cmd.lun = 0;
+	cmd.lba = 0;
+	cmd.pmi = 0;
+	cmd.control = 0;
+	scsi_res_read_capacity* resp;
+	size_t respLength;
+	error = fSession->Connection()->SendCommand(&cmd, sizeof(cmd),
+		false, false, 0,
+		(void**)&resp, &respLength);
+	if (error != B_OK)
+		return error;
+	printf("lba = %lu, block size = %lu\n", resp->lba, resp->block_size);
+	fLastLBA = resp->lba;
+	fBlockSize = resp->block_size;
+	return B_OK;
+}
+
+
+ssize_t
+iSCSITarget::ReadAt(void* /*cookie*/, off_t pos, void* buffer, size_t bufferSize)
+{
+	printf("!!!! iSCSITarget::ReadAt(): size = %lu\n", bufferSize);
+	fSession->Connection()->ReadData();
+
+	scsi_cmd_rw_12 cmd;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.opcode = SCSI_OP_READ_12;
+	cmd.relative_address = 0;
+	cmd.force_unit_access = 0;
+	cmd.disable_page_out = 0;
+	cmd.lun = 0;
+	cmd.lba = pos / fBlockSize;
+	cmd.length = bufferSize;
+	cmd.control = 0;
+	void* resp;
+	size_t respLength;
+	status_t error = fSession->Connection()->SendCommand(&cmd, sizeof(cmd),
+		true, false, cmd.length, &resp, &respLength);
+	if (error != B_OK)
+		return error;
+	printf("====> length = %lu\n", respLength);
+	
+	error = fSession->Connection()->ReadData();
+	return B_PERMISSION_DENIED;
+}
+
+
+ssize_t
+iSCSITarget::WriteAt(void* /*cookie*/, off_t pos, const void* buffer, size_t bufferSize)
+{
+	return B_PERMISSION_DENIED;
+}
+
+
+status_t
+iSCSITarget::GetName(char* buffer, size_t bufferSize) const
+{
+	if (buffer == NULL)
+		return B_BAD_VALUE;
+
+	snprintf(buffer, bufferSize, "iSCSI:%s", fTargetName);
+
+	return B_OK;
+}
+
+
+off_t
+iSCSITarget::Size() const
+{
+	return fLastLBA * fBlockSize;
+}
+
+
+iSCSISession::iSCSISession(const char* targetName)
+	:
+	fDiscovery(targetName == NULL),
+	fTargetName(targetName),
+	fConnection(NULL)
+{
+	fCommandSequenceNumber = _rand();
+}
+
+
+iSCSISession::~iSCSISession()
+{
+	if (Active())
+		Close();
+	delete fConnection;
+}
+
+
+status_t
+iSCSISession::Init(ip_addr_t address, uint16 port, char** targetAlias)
+{
+	fConnection = new(nothrow) iSCSIConnection();
+	if (fConnection == NULL)
+		return B_NO_MEMORY;
+	status_t error = fConnection->Init(this);
+	if (error != B_OK)
+		return error;
+	error = fConnection->Open(address, port);
+	if (error != B_OK)
+		return error;
+	error = fConnection->Login(fTargetName, targetAlias);
+	if (error != B_OK)
+		return error;
+	return B_OK;
+}
+
+
+status_t
+iSCSISession::Close()
+{
+	if (fConnection == NULL)
+		return B_NO_INIT;
+
+	status_t error = fConnection->Logout(true);
+	if (error != B_OK)
+		return error;
+	return B_OK;
+}
+
+
+iSCSIConnection::iSCSIConnection()
+	:
+	fSession(NULL),
+	fSocket(NULL),
+	fConnected(false),
+	fConnectionID(0)
+{
+}
+
+
+iSCSIConnection::~iSCSIConnection()
+{
+	if (fSocket != NULL && fConnected) {
+		if (Logout() != B_OK)
+			fSocket->Close();
+	}
+	delete fSocket;
+}
+
+
+status_t
+iSCSIConnection::Init(iSCSISession* session)
+{
+	fSession = session;
+	return B_OK;
+}
+
+status_t
+iSCSIConnection::Open(ip_addr_t address, uint16 port)
+{
+	fSocket = new(nothrow) TCPSocket();
+	if (fSocket == NULL)
+		return B_NO_MEMORY;
+	printf("iSCSI initiator connecting to target...\n");
+	status_t error = fSocket->Connect(address, port);
+	if (error != B_OK)
+		return error;
+	fConnected = true;
+	printf("connected.\n");
+	fConnectionID = _rand();
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::Login(const char* targetName, char** targetAlias)
+{
+	char data[256];
+	size_t dataLength = 0;
+	const char* keyValue;
+	keyValue = "InitiatorName=iqn.2002-10.org.haiku-os:haiku.bootloader.hostX";
+	strcpy(data + dataLength, keyValue);
+	dataLength += strlen(keyValue) + 1;
+	if (targetName != NULL) {
+		dataLength += sprintf(data + dataLength, "TargetName=%s", targetName) + 1;
+	} else {
+		keyValue = "SessionType=Discovery";
+		strcpy(data + dataLength, keyValue);
+		dataLength += strlen(keyValue) + 1;
+	}
+
+	iscsi_login_request req;
+	req.transit = true;
+	req.c = false;
+	req.currentStage = ISCSI_SESSION_STAGE_LOGIN_OPERATIONAL_NEGOTIATION;
+	req.nextStage    = ISCSI_SESSION_STAGE_FULL_FEATURE_PHASE;
+	req.versionMax = ISCSI_VERSION;
+	req.versionMin = ISCSI_VERSION;
+	req.totalAHSLength = 0;
+	req.dataSegmentLength = dataLength;
+	printf("data segment length = %lu\n", req.dataSegmentLength);
+	req.isid.t = ISCSI_ISID_RANDOM;
+	req.isid.a = 0;
+	uint32 random = _rand();
+	req.isid.b = random >> 8;
+	req.isid.c = random & 0xff;
+	req.isid.d = _rand();
+	req.tsih = 0;
+	req.initiatorTaskTag = _rand();
+	req.cid = fConnectionID;
+	req.cmdSN = fSession->CommandSequenceNumber();
+	req.expStatSN = 0;
+	status_t error = fSocket->Write(&req, sizeof(req));
+	if (error != B_OK) {
+		return error;
+	}
+	for (unsigned int i = dataLength; i < ISCSI_ALIGN(dataLength); i++)
+		data[i] = '\0';
+	dataLength = ISCSI_ALIGN(dataLength);
+	printf("data length = %lu\n", dataLength);
+	error = fSocket->Write(data, dataLength);
+	if (error != B_OK) {
+		printf("write error\n");
+		return error;
+	}
+	printf("iSCSI login request sent.\n");
+
+	iscsi_login_response resp;
+	size_t bytesRead = 0;
+	error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI login response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	printf("iSCSI login response: opcode = %x, status class = %u, status detail = %u, transit = %u, data length = %lu\n",
+		resp.opcode, resp.statusClass, resp.statusDetail, resp.transit, resp.dataSegmentLength);
+	if (resp.dataSegmentLength > 0) {
+		error = fSocket->Read(data, resp.dataSegmentLength, &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI login response data read error\n");
+			return error;
+		}
+		printf("bytesRead = %lu\n", bytesRead);
+		if (resp.dataSegmentLength % ISCSI_ALIGNMENT) {
+			error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 100000LL);
+			if (error != B_OK) {
+				printf("iSCSI login response padding read error\n");
+				return error;
+			}
+			printf("bytesRead = %lu\n", bytesRead);
+		}
+		for (char* pair = data; pair < data + resp.dataSegmentLength; pair += strlen(pair) + 1) {
+			printf("%s\n", pair);
+			if (targetAlias != NULL && strncmp(pair, "TargetAlias=", strlen("TargetAlias=")) == 0) {
+				*targetAlias = strdup(pair + strlen("TargetAlias="));
+			}
+		}
+	}
+	fStatusSequenceNumber = resp.statSN;
+	if (resp.statusClass != 0) {
+		printf("iSCSI login response indicates failure.\n");
+		return B_BAD_VALUE;
+	}
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::Logout(bool closeSession)
+{
+	iscsi_logout_request req;
+	printf("req size = %lu\n", sizeof(req));
+	req.immediateDelivery = true;
+	req.reasonCode = closeSession ? ISCSI_LOGOUT_REASON_CLOSE_SESSION
+		: ISCSI_LOGOUT_REASON_CLOSE_CONNECTION;
+	req.totalAHSLength = 0;
+	req.dataSegmentLength = 0;
+	req.initiatorTaskTag = _rand();
+	req.cid = closeSession ? 0 : fConnectionID;
+	req.cmdSN = fSession->CommandSequenceNumber();
+	req.expStatSN = fStatusSequenceNumber;
+	status_t error = fSocket->Write(&req, sizeof(req));
+	if (error != B_OK) {
+		printf("iSCSI logout request write error\n");
+		return error;
+	}
+	printf("iSCSI logout request sent.\n");
+
+	iscsi_logout_response resp;
+	printf("resp size = %lu\n", sizeof(resp));
+	size_t bytesRead = 0;
+	error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI logout response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	printf("iSCSI logout response: opcode = %x, response = %u\n",
+		resp.opcode, resp.response);
+	fStatusSequenceNumber = resp.statSN;
+	if (resp.response != 0) {
+		printf("iSCSI logout response indicates failure.\n");
+		return error;
+	}
+
+	fSocket->Close();
+	fConnected = false;
+	printf("disconnected\n");
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::GetText(const char* request, size_t requestLength, char** response, size_t* responseLength)
+{
+	iscsi_text_request req;
+	req.immediateDelivery = true;
+	req.final = true;
+	req.c = false;
+	req.totalAHSLength = 0;
+	req.dataSegmentLength = requestLength;
+	req.lun = 0;
+	req.initiatorTaskTag = _rand();
+	req.targetTransferTag = 0xffffffffL;
+	req.cmdSN = fSession->NextCommandSequenceNumber();
+	req.expStatSN = fStatusSequenceNumber;
+	status_t error = fSocket->Write(&req, sizeof(req));
+	if (error != B_OK) {
+		printf("iSCSI text request write error\n");
+		return error;
+	}
+	error = fSocket->Write(request, requestLength);
+	if (error != B_OK) {
+		printf("iSCSI text request data write error\n");
+		return error;
+	}
+	if (requestLength % ISCSI_ALIGNMENT != 0) {
+		char buffer[3];
+		memset(buffer, 0, 3);
+		error = fSocket->Write(buffer, ISCSI_ALIGNMENT - (requestLength % ISCSI_ALIGNMENT));
+		if (error != B_OK) {
+			printf("iSCSI text request padding write error\n");
+			return error;
+		}
+	}
+	printf("iSCSI text request sent.\n");
+
+	iscsi_logout_response resp;
+	printf("resp size = %lu\n", sizeof(resp));
+	size_t bytesRead = 0;
+	error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI text response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	printf("iSCSI text response: opcode = %x\n",
+		resp.opcode);
+	*response = (char*)malloc(resp.dataSegmentLength);
+	if (*response == NULL)
+		return B_NO_MEMORY;
+	error = fSocket->Read(*response, resp.dataSegmentLength, &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI text response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	*responseLength = resp.dataSegmentLength;
+	if (resp.dataSegmentLength % ISCSI_ALIGNMENT != 0) {
+		error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI text response padding read error\n");
+			return error;
+		}
+	}
+	fStatusSequenceNumber = resp.statSN;
+	
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::SendCommand(const void* command, size_t commandSize,
+	bool r, bool w, uint32 expectedDataTransferLength,
+	void** response, size_t* responseLength)
+{
+	printf("iSCSIConnection::SendCommand(): command size = %lu\n", commandSize);
+	iscsi_scsi_command req;
+	printf("req size = %lu\n", sizeof(req));
+	req.immediateDelivery = true;
+	req.final = true;
+	req.r = r;
+	req.w = w;
+	req.attr = 0;
+	req.totalAHSLength = 0;
+	req.dataSegmentLength = 0;
+	req.lun = 0;
+	req.initiatorTaskTag = _rand();
+	req.expectedDataTransferLength = expectedDataTransferLength;
+	req.cmdSN = fSession->NextCommandSequenceNumber();
+	req.expStatSN = fStatusSequenceNumber;
+	if (commandSize > 16)
+		memset(req.cdb, 0, 16);
+	memcpy(req.cdb, command, commandSize <= 16 ? commandSize : 16);
+	status_t error = fSocket->Write(&req, sizeof(req));
+	if (error != B_OK) {
+		printf("iSCSI SCSI command write error\n");
+		return error;
+	}
+	printf("iSCSI SCSI command sent.\n");
+
+	iscsi_scsi_response resp;
+	printf("resp size = %lu\n", sizeof(resp));
+	size_t bytesRead = 0;
+	error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI SCSI response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	printf("iSCSI SCSI response: opcode = %x, response = %x, status = %x, AHS length = %x\n",
+		resp.opcode, resp.response, resp.status, resp.totalAHSLength);
+	if (resp.dataSegmentLength > 0) {
+		*response = malloc(resp.dataSegmentLength);
+		if (*response == NULL)
+			return B_NO_MEMORY;
+		error = fSocket->Read(*response, resp.dataSegmentLength, &bytesRead, 10000000LL);
+		if (error != B_OK) {
+			printf("iSCSI text response read error\n");
+			return error;
+		}
+		printf("bytesRead = %lu\n", bytesRead);
+	}
+	*responseLength = resp.dataSegmentLength;
+	if (resp.dataSegmentLength % ISCSI_ALIGNMENT != 0) {
+		error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI SCSI response padding read error\n");
+			return error;
+		}
+	}
+	if (resp.response != 0x00 || resp.status != SCSI_STATUS_GOOD)
+		return B_BAD_VALUE;
+
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::ReadData()
+{
+	iscsi_basic_header_segment resp;
+	size_t bytesRead = 0;
+	status_t error = fSocket->Read(&resp, sizeof(resp), &bytesRead, 10000000LL);
+	if (error != B_OK) {
+		printf("iSCSI SCSI response read error\n");
+		return error;
+	}
+	printf("bytesRead = %lu\n", bytesRead);
+	printf("iSCSI response: opcode = %x, AHS length = %u, data length = %lu\n",
+		resp.opcode, resp.totalAHSLength, resp.dataSegmentLength);
+	if (resp.totalAHSLength > 0) {
+		error = fSocket->Read(NULL, resp.totalAHSLength, &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI response header read error\n");
+			return error;
+		}
+		printf("bytesRead = %lu\n", bytesRead);
+	}
+	if (resp.dataSegmentLength > 0) {
+		error = fSocket->Read(NULL, resp.dataSegmentLength, &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI response data read error\n");
+			return error;
+		}
+		printf("bytesRead = %lu\n", bytesRead);
+	}
+	if (resp.dataSegmentLength % ISCSI_ALIGNMENT != 0) {
+		error = fSocket->Read(NULL, ISCSI_PADDING(resp.dataSegmentLength), &bytesRead, 1000000LL);
+		if (error != B_OK) {
+			printf("iSCSI SCSI response padding read error\n");
+			return error;
+		}
+	}
+	return B_OK;
+}
+
+
+status_t
+iSCSIConnection::_ReadResponse(void* buffer, size_t bufferSize, size_t* bytesRead, bigtime_t timeout)
+{
+	// assuming bufferSize >= 48
+	status_t error = fSocket->Read(buffer, bufferSize, bytesRead, timeout);
+	if (error != B_OK)
+		return error;
+	//if (*bytesRead >= 1)
+	return error;
+}
diff --git a/src/system/boot/platform/openfirmware/devices.cpp b/src/system/boot/platform/openfirmware/devices.cpp
index 601ac50..4eb00c0 100644
--- a/src/system/boot/platform/openfirmware/devices.cpp
+++ b/src/system/boot/platform/openfirmware/devices.cpp
@@ -12,6 +12,8 @@
 #include <boot/stage2.h>
 #include <boot/net/NetStack.h>
 #include <boot/net/RemoteDisk.h>
+#include <boot/net/IP.h>
+#include <boot/net/iSCSITarget.h>
 #include <platform/openfirmware/devices.h>
 #include <platform/openfirmware/openfirmware.h>
 #include <util/kernel_cpp.h>
@@ -22,6 +24,17 @@
 char sBootPath[192];
 
 
+static void
+iscsi_add_boot_device(Node* node, void* opaque)
+{
+	NodeList* devicesList = static_cast<NodeList*>(opaque);
+	//devicesList->Add(node);
+
+	uint8 buffer[512];
+	node->ReadAt(NULL, 0, buffer, sizeof(buffer));
+	of_exit();
+}
+
 status_t 
 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
 {
@@ -45,14 +58,43 @@ platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
 			status_t error = net_stack_init();
 			if (error != B_OK)
 				return error;
-		
+
+			bool foundDisk = false;
+			ip_addr_t bootAddress = 0;
+			char* bootArgs = strrchr(sBootPath, ':');
+			if (bootArgs != NULL) {
+				bootArgs++;
+				char* comma = strchr(bootArgs, ',');
+				if (comma != NULL && comma - bootArgs > 0) {
+					comma[0] = '\0';
+					bootAddress = ip_parse_address(bootArgs);
+					comma[0] = ',';
+				}
+			}
+			if (bootAddress == 0) {
+				int package = of_finddevice("/options");
+				char defaultServerIP[16];
+				int bytesRead = of_getprop(package, "default-server-ip",
+					defaultServerIP, sizeof(defaultServerIP) - 1);
+				if (bytesRead != OF_FAILED && bytesRead > 1) {
+					defaultServerIP[bytesRead] = '\0';
+					bootAddress = ip_parse_address(defaultServerIP);
+				}
+			}
+
+			if (bootAddress != 0) {
+				if (iSCSITarget::DiscoverTargets(bootAddress, ISCSI_PORT,
+						iscsi_add_boot_device, devicesList))
+					foundDisk = true;
+				//of_exit();//XXX
+			}
+
 			// init a remote disk, if possible
 			RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk();
-			if (!remoteDisk)
-				return B_ENTRY_NOT_FOUND;
+			if (remoteDisk)
+				devicesList->Add(remoteDisk);
 
-			devicesList->Add(remoteDisk);
-			return B_OK;
+			return foundDisk ? B_OK : B_ENTRY_NOT_FOUND;
 		}
 
 		if (strcmp("block", type) != 0) {
