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