1/*
2 * Copyright 2003-2006, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Copyright 2006, Marcus Overhagen, marcus@overhagen.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "bios.h"
8#include "pxe_undi.h"
9#include "network.h"
10
11#include <KernelExport.h>
12#include <boot/platform.h>
13#include <boot/vfs.h>
14#include <boot/stdio.h>
15#include <boot/stage2.h>
16#include <boot/net/NetStack.h>
17#include <boot/net/RemoteDisk.h>
18#include <util/kernel_cpp.h>
19#include <util/KMessage.h>
20
21#include <string.h>
22
23#define TRACE_DEVICES
24#ifdef TRACE_DEVICES
25#	define TRACE(x...) dprintf(x)
26#else
27#	define TRACE(x...)
28#endif
29
30
31//extern unsigned char* gBuiltinBootArchive;
32//extern long long gBuiltinBootArchiveSize;
33
34static TFTP sTFTP;
35
36
37status_t
38platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
39{
40	TRACE("platform_add_boot_device\n");
41
42	// get the boot archive containing kernel and drivers via TFTP
43	status_t error = sTFTP.Init();
44	if (error == B_OK) {
45		uint8* data;
46		size_t size;
47		// The root path in the DHCP packet from the server might contain the
48		// name of the archive. It would come first, then separated by semicolon
49		// the actual root path.
50		const char* fileName = "haiku-netboot.tgz";	// default
51		char stackFileName[1024];
52		const char* rootPath = sTFTP.RootPath();
53		if (rootPath) {
54			if (char* fileNameEnd = strchr(rootPath, ';')) {
55				size_t len = min_c(fileNameEnd - rootPath,
56					(int)sizeof(stackFileName) - 1);
57				memcpy(stackFileName, rootPath, len);
58				stackFileName[len] = '\0';
59				fileName = stackFileName;
60			}
61		}
62
63		// get the file
64		error = sTFTP.ReceiveFile(fileName, &data, &size);
65		if (error == B_OK) {
66			char name[64];
67			ip_addr_t serverAddress = sTFTP.ServerIPAddress();
68			snprintf(name, sizeof(name), "%lu.%lu.%lu.%lu:%s",
69				(serverAddress >> 24), (serverAddress >> 16) & 0xff,
70				(serverAddress >> 8) & 0xff, serverAddress & 0xff, fileName);
71
72			MemoryDisk* disk = new(nothrow) MemoryDisk(data, size, name);
73			if (!disk) {
74				dprintf("platform_add_boot_device(): Out of memory!\n");
75				platform_free_region(data, size);
76				return B_NO_MEMORY;
77			}
78
79			devicesList->Add(disk);
80			return B_OK;
81		} else {
82			dprintf("platform_add_boot_device(): Failed to load file \"%s\" "
83				"via TFTP\n", fileName);
84		}
85	}
86
87	return B_ENTRY_NOT_FOUND;
88
89// 	// built-in boot archive?
90// 	if (gBuiltinBootArchiveSize > 0) {
91// 		MemoryDisk* disk = new(nothrow) MemoryDisk(gBuiltinBootArchive,
92// 			gBuiltinBootArchiveSize);
93// 		if (!disk)
94// 			return B_NO_MEMORY;
95//
96// 		devicesList->Add(disk);
97// 		return B_OK;
98// 	}
99
100// 	error = net_stack_init();
101// 	if (error != B_OK)
102// 		return error;
103//
104// 	// init a remote disk, if possible
105// 	RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk();
106// 	if (!remoteDisk) {
107// 		unsigned ip = NetStack::Default()->GetEthernetInterface()->IPAddress();
108// 		panic("PXE boot: can't find remote disk on server %u.%u.%u.%u\n",
109// 			(ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
110// 		return B_ENTRY_NOT_FOUND;
111// 	}
112//
113// 	devicesList->Add(remoteDisk);
114// 	return B_OK;
115}
116
117
118status_t
119platform_get_boot_partition(struct stage2_args *args, Node *device,
120	NodeList *list, boot::Partition **_partition)
121{
122	TRACE("platform_get_boot_partition\n");
123	NodeIterator iterator = list->GetIterator();
124	boot::Partition *partition = NULL;
125	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
126		// ToDo: just take the first partition for now
127		*_partition = partition;
128		return B_OK;
129	}
130
131	return B_ENTRY_NOT_FOUND;
132}
133
134
135status_t
136platform_add_block_devices(stage2_args *args, NodeList *devicesList)
137{
138	TRACE("platform_add_block_devices\n");
139	return B_OK;
140}
141
142
143status_t
144platform_register_boot_device(Node *device)
145{
146	TRACE("platform_register_boot_device\n");
147
148	// get the root path -- chop off the file name of the archive we loaded
149	const char* rootPath = sTFTP.RootPath();
150	if (rootPath) {
151		if (char* fileNameEnd = strchr(rootPath, ';'))
152			rootPath = fileNameEnd + 1;
153	}
154
155	if (gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_NET) != B_OK
156		|| gBootVolume.AddInt64("client MAC",
157			sTFTP.MACAddress().ToUInt64()) != B_OK
158		|| gBootVolume.AddInt32("client IP", sTFTP.IPAddress()) != B_OK
159		|| gBootVolume.AddInt32("server IP", sTFTP.ServerIPAddress()) != B_OK
160		|| gBootVolume.AddInt32("server port", sTFTP.ServerPort()) != B_OK
161		|| (sTFTP.RootPath()
162			&& gBootVolume.AddString("net root path", rootPath)
163				!= B_OK)) {
164		return B_NO_MEMORY;
165	}
166
167// 	RemoteDisk *rd = static_cast<RemoteDisk *>(device);
168// 	UNDI *undi = static_cast<UNDI *>(NetStack::Default()->GetEthernetInterface());
169//
170// 	gKernelArgs.boot_disk.identifier.bus_type = UNKNOWN_BUS;
171// 	gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE;
172// 	gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress();
173// 	gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress();
174// 	gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort();
175// 	gKernelArgs.boot_disk.partition_offset = 0;
176// 	gKernelArgs.boot_disk.user_selected = false;
177// 	gKernelArgs.boot_disk.booted_from_image = false;
178// 	gKernelArgs.boot_disk.booted_from_network = true;
179// 	gKernelArgs.boot_disk.cd = false;
180
181	return B_OK;
182}
183