⛏️ index : haiku.git

/*
 * Copyright 2003-2006, Axel DΓΆrfler, axeld@pinc-software.de. All rights reserved.
 * Copyright 2006, Marcus Overhagen, marcus@overhagen.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include "bios.h"
#include "pxe_undi.h"
#include "network.h"

#include <KernelExport.h>
#include <boot/partitions.h>
#include <boot/platform.h>
#include <boot/vfs.h>
#include <boot/stdio.h>
#include <boot/stage2.h>
#include <boot/net/NetStack.h>
#include <boot/net/RemoteDisk.h>
#include <util/kernel_cpp.h>
#include <util/KMessage.h>

#include <string.h>

#define TRACE_DEVICES
#ifdef TRACE_DEVICES
#	define TRACE(x...) dprintf(x)
#else
#	define TRACE(x...)
#endif


//extern unsigned char* gBuiltinBootArchive;
//extern long long gBuiltinBootArchiveSize;

static TFTP sTFTP;


status_t 
platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
{
	TRACE("platform_add_boot_device\n");

	// get the boot archive containing kernel and drivers via TFTP
	status_t error = sTFTP.Init();
	if (error == B_OK) {
		uint8* data;
		size_t size;
		// The root path in the DHCP packet from the server might contain the
		// name of the archive. It would come first, then separated by semicolon
		// the actual root path.
		const char* fileName = "haiku-netboot.tgz";	// default
		char stackFileName[1024];
		const char* rootPath = sTFTP.RootPath();
		if (rootPath) {
			if (char* fileNameEnd = strchr(rootPath, ';')) {
				size_t len = min_c(fileNameEnd - rootPath,
					(int)sizeof(stackFileName) - 1);
				memcpy(stackFileName, rootPath, len);
				stackFileName[len] = '\0';
				fileName = stackFileName;
			}
		}

		// get the file
		error = sTFTP.ReceiveFile(fileName, &data, &size);
		if (error == B_OK) {
			char name[64];
			ip_addr_t serverAddress = sTFTP.ServerIPAddress();
			snprintf(name, sizeof(name), "%u.%u.%u.%u:%s",
				(serverAddress >> 24), (serverAddress >> 16) & 0xff,
				(serverAddress >> 8) & 0xff, serverAddress & 0xff, fileName);

			MemoryDisk* disk = new(nothrow) MemoryDisk(data, size, name);
			if (!disk) {
				dprintf("platform_add_boot_device(): Out of memory!\n");
				platform_free_region(data, size);
				return B_NO_MEMORY;
			}

			gBootParams.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, true);
			gBootParams.SetInt32(BOOT_METHOD, BOOT_METHOD_NET);
			devicesList->Add(disk);
			return B_OK;
		} else {
			dprintf("platform_add_boot_device(): Failed to load file \"%s\" "
				"via TFTP\n", fileName);
		}
	}

	return B_ENTRY_NOT_FOUND;

// 	// built-in boot archive?
// 	if (gBuiltinBootArchiveSize > 0) {
// 		MemoryDisk* disk = new(nothrow) MemoryDisk(gBuiltinBootArchive,
// 			gBuiltinBootArchiveSize);
// 		if (!disk)
// 			return B_NO_MEMORY;
// 
// 		devicesList->Add(disk);
// 		return B_OK;
// 	}

// 	error = net_stack_init();
// 	if (error != B_OK)
// 		return error;
// 
// 	// init a remote disk, if possible
// 	RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk();
// 	if (!remoteDisk) {
// 		unsigned ip = NetStack::Default()->GetEthernetInterface()->IPAddress();
// 		panic("PXE boot: can't find remote disk on server %u.%u.%u.%u\n",
// 			(ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
// 		return B_ENTRY_NOT_FOUND;
// 	}
// 
// 	devicesList->Add(remoteDisk);
// 	return B_OK;
}


status_t
platform_get_boot_partitions(struct stage2_args *args, Node *device,
	NodeList *list, NodeList *partitionList)
{
	TRACE("platform_get_boot_partition\n");
	NodeIterator iterator = list->GetIterator();
	boot::Partition *partition = NULL;
	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
		// ToDo: just take the first partition for now
		partitionList->Insert(partition);
		return B_OK;
	}

	return B_ENTRY_NOT_FOUND;
}


status_t
platform_add_block_devices(stage2_args *args, NodeList *devicesList)
{
	TRACE("platform_add_block_devices\n");
	return B_OK;
}


status_t 
platform_register_boot_device(Node *device)
{
	TRACE("platform_register_boot_device\n");

	// get the root path -- chop off the file name of the archive we loaded
	const char* rootPath = sTFTP.RootPath();
	if (rootPath) {
		if (char* fileNameEnd = strchr(rootPath, ';'))
			rootPath = fileNameEnd + 1;
	}

	if (gBootParams.SetInt32(BOOT_METHOD, BOOT_METHOD_NET) != B_OK
		|| gBootParams.AddInt64("client MAC",
			sTFTP.MACAddress().ToUInt64()) != B_OK
		|| gBootParams.AddInt32("client IP", sTFTP.IPAddress()) != B_OK
		|| gBootParams.AddInt32("server IP", sTFTP.ServerIPAddress()) != B_OK
		|| gBootParams.AddInt32("server port", sTFTP.ServerPort()) != B_OK
		|| (sTFTP.RootPath()
			&& gBootParams.AddString("net root path", rootPath)
				!= B_OK)) {
		return B_NO_MEMORY;
	}

// 	RemoteDisk *rd = static_cast<RemoteDisk *>(device);
// 	UNDI *undi = static_cast<UNDI *>(NetStack::Default()->GetEthernetInterface());
//
// 	gKernelArgs.boot_disk.identifier.bus_type = UNKNOWN_BUS;
// 	gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE;
// 	gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress();
// 	gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress();
// 	gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort();
// 	gKernelArgs.boot_disk.partition_offset = 0;
// 	gKernelArgs.boot_disk.user_selected = false;
// 	gKernelArgs.boot_disk.booted_from_image = false;
// 	gKernelArgs.boot_disk.booted_from_network = true;
// 	gKernelArgs.boot_disk.cd = false;

	return B_OK;
}


void
platform_cleanup_devices()
{
	net_stack_cleanup();
}