xref: /haiku/src/system/boot/platform/riscv/devices.cpp (revision ae5abb58266fba43b3479995388d13e92013e08f)
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 
29 void* aligned_malloc(size_t required_bytes, size_t alignment);
30 void aligned_free(void* p);
31 
32 
33 class 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 
56 VirtioBlockDevice::VirtioBlockDevice(VirtioDevice* blockIo)
57 	:
58 	fBlockIo(blockIo)
59 {
60 	dprintf("+VirtioBlockDevice\n");
61 }
62 
63 
64 VirtioBlockDevice::~VirtioBlockDevice()
65 {
66 	dprintf("-VirtioBlockDevice\n");
67 }
68 
69 
70 ssize_t
71 VirtioBlockDevice::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 
107 static VirtioBlockDevice*
108 CreateVirtioBlockDev(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 
127 static off_t
128 get_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 
140 static uint32
141 compute_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 
164 status_t
165 platform_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 
177 status_t
178 platform_add_block_devices(struct stage2_args* args, NodeList* devicesList)
179 {
180 	return B_ENTRY_NOT_FOUND;
181 }
182 
183 
184 status_t
185 platform_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 
199 status_t
200 platform_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 
226 void
227 platform_cleanup_devices()
228 {
229 }
230