1/*
2 * Copyright 2003-2006, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "bios.h"
8#include "virtio.h"
9
10#include <KernelExport.h>
11#include <boot/platform.h>
12#include <boot/partitions.h>
13#include <boot/stdio.h>
14#include <boot/stage2.h>
15
16#include <AutoDeleter.h>
17
18#include <string.h>
19#include <new>
20
21//#define TRACE_DEVICES
22#ifdef TRACE_DEVICES
23# define TRACE(x...) dprintf(x)
24#else
25# define TRACE(x...) ;
26#endif
27
28
29void* aligned_malloc(size_t required_bytes, size_t alignment);
30void aligned_free(void* p);
31
32
33class VirtioBlockDevice : public Node
34{
35	public:
36		VirtioBlockDevice(VirtioDevice* blockIo);
37		virtual ~VirtioBlockDevice();
38
39		virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer,
40			size_t bufferSize);
41		virtual ssize_t WriteAt(void* cookie, off_t pos, const void* buffer,
42			size_t bufferSize) { return B_UNSUPPORTED; }
43		virtual off_t Size() const {
44			return (*(uint32*)(&fBlockIo->Regs()->config[0])
45				+ ((uint64)(*(uint32*)(&fBlockIo->Regs()->config[4])) << 32)
46				)*kVirtioBlockSectorSize;
47		}
48
49		uint32 BlockSize() const { return kVirtioBlockSectorSize; }
50		bool ReadOnly() const { return false; }
51	private:
52		ObjectDeleter<VirtioDevice> fBlockIo;
53};
54
55
56VirtioBlockDevice::VirtioBlockDevice(VirtioDevice* blockIo)
57	:
58	fBlockIo(blockIo)
59{
60	dprintf("+VirtioBlockDevice\n");
61}
62
63
64VirtioBlockDevice::~VirtioBlockDevice()
65{
66	dprintf("-VirtioBlockDevice\n");
67}
68
69
70ssize_t
71VirtioBlockDevice::ReadAt(void* cookie, off_t pos, void* buffer,
72	size_t bufferSize)
73{
74	// dprintf("ReadAt(%p, %ld, %p, %ld)\n", cookie, pos, buffer, bufferSize);
75
76	off_t offset = pos % BlockSize();
77	pos /= BlockSize();
78
79	uint32 numBlocks = (offset + bufferSize + BlockSize() - 1) / BlockSize();
80
81	ArrayDeleter<char> readBuffer(
82		new(std::nothrow) char[numBlocks * BlockSize() + 1]);
83	if (!readBuffer.IsSet())
84		return B_NO_MEMORY;
85
86	VirtioBlockRequest blkReq;
87	blkReq.type = kVirtioBlockTypeIn;
88	blkReq.ioprio = 0;
89	blkReq.sectorNum = pos;
90	IORequest req(ioOpRead, &blkReq, sizeof(blkReq));
91	IORequest reply(ioOpWrite, readBuffer.Get(), numBlocks * BlockSize() + 1);
92	IORequest* reqs[] = {&req, &reply};
93	fBlockIo->ScheduleIO(reqs, 2);
94	fBlockIo->WaitIO();
95
96	if (readBuffer[numBlocks * BlockSize()] != kVirtioBlockStatusOk) {
97		dprintf("%s: blockIo error reading from device!\n", __func__);
98		return B_ERROR;
99	}
100
101	memcpy(buffer, readBuffer.Get() + offset, bufferSize);
102
103	return bufferSize;
104}
105
106
107static VirtioBlockDevice*
108CreateVirtioBlockDev(int id)
109{
110	VirtioResources* devRes = ThisVirtioDev(kVirtioDevBlock, id);
111	if (devRes == NULL) return NULL;
112
113	ObjectDeleter<VirtioDevice> virtioDev(
114		new(std::nothrow) VirtioDevice(*devRes));
115	if (!virtioDev.IsSet())
116		panic("Can't allocate memory for VirtioDevice!");
117
118	ObjectDeleter<VirtioBlockDevice> device(
119		new(std::nothrow) VirtioBlockDevice(virtioDev.Detach()));
120	if (!device.IsSet())
121		panic("Can't allocate memory for VirtioBlockDevice!");
122
123	return device.Detach();
124}
125
126
127static off_t
128get_next_check_sum_offset(int32 index, off_t maxSize)
129{
130	if (index < 2)
131		return index * 512;
132
133	if (index < 4)
134		return (maxSize >> 10) + index * 2048;
135
136	return ((system_time() + index) % (maxSize >> 9)) * 512;
137}
138
139
140static uint32
141compute_check_sum(Node* device, off_t offset)
142{
143	char buffer[512];
144	ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer));
145	if (bytesRead < B_OK)
146		return 0;
147
148	if (bytesRead < (ssize_t)sizeof(buffer))
149		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
150
151	uint32* array = (uint32*)buffer;
152	uint32 sum = 0;
153
154	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32);
155		i++)
156		sum += array[i];
157
158	return sum;
159}
160
161
162//#pragma mark -
163
164status_t
165platform_add_boot_device(struct stage2_args* args, NodeList* devicesList)
166{
167	for (int i = 0;; i++) {
168		ObjectDeleter<VirtioBlockDevice> device(CreateVirtioBlockDev(i));
169		if (!device.IsSet()) break;
170		dprintf("virtio_block[%d]\n", i);
171		devicesList->Insert(device.Detach());
172	}
173	return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
174}
175
176
177status_t
178platform_add_block_devices(struct stage2_args* args, NodeList* devicesList)
179{
180	return B_ENTRY_NOT_FOUND;
181}
182
183
184status_t
185platform_get_boot_partitions(struct stage2_args* args, Node* bootDevice,
186	NodeList *list, NodeList *partitionList)
187{
188	NodeIterator iterator = list->GetIterator();
189	boot::Partition *partition = NULL;
190	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
191		// ToDo: just take the first partition for now
192		partitionList->Insert(partition);
193		return B_OK;
194	}
195	return B_ENTRY_NOT_FOUND;
196}
197
198
199status_t
200platform_register_boot_device(Node* device)
201{
202	TRACE("%s: called\n", __func__);
203
204	disk_identifier identifier;
205
206	identifier.bus_type = UNKNOWN_BUS;
207	identifier.device_type = UNKNOWN_DEVICE;
208	identifier.device.unknown.size = device->Size();
209
210	for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) {
211		off_t offset = get_next_check_sum_offset(i, device->Size());
212		identifier.device.unknown.check_sums[i].offset = offset;
213		identifier.device.unknown.check_sums[i].sum = compute_check_sum(device,
214			offset);
215	}
216
217	gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK);
218	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false);
219	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
220		&identifier, sizeof(disk_identifier));
221
222	return B_OK;
223}
224
225
226void
227platform_cleanup_devices()
228{
229}
230