1/*
2 * Copyright 2010-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <file_systems/ram_disk/ram_disk.h>
8
9#include <ctype.h>
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <string.h>
14#include <unistd.h>
15
16#include <algorithm>
17
18#include <device_manager.h>
19#include <Drivers.h>
20
21#include <AutoDeleter.h>
22#include <util/AutoLock.h>
23#include <util/DoublyLinkedList.h>
24
25#include <fs/KPath.h>
26#include <lock.h>
27#include <util/fs_trim_support.h>
28#include <vm/vm.h>
29#include <vm/VMCache.h>
30#include <vm/vm_page.h>
31
32#include "dma_resources.h"
33#include "io_requests.h"
34#include "IOSchedulerSimple.h"
35
36
37//#define TRACE_RAM_DISK
38#ifdef TRACE_RAM_DISK
39#	define TRACE(x...)	dprintf(x)
40#else
41#	define TRACE(x...) do {} while (false)
42#endif
43
44
45static const unsigned char kRamdiskIcon[] = {
46	0x6e, 0x63, 0x69, 0x66, 0x0e, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
47	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
48	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
49	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
50	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
51	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
52	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
53	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
54	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
55	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
56	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
57	0x03, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x6a, 0x05, 0x33, 0x02,
58	0x00, 0x06, 0x02, 0x3a, 0x5d, 0x2c, 0x39, 0xf8, 0xb1, 0xb9, 0xdb, 0xf1,
59	0x3a, 0x4c, 0x0f, 0x48, 0xae, 0xea, 0x4a, 0xc0, 0x91, 0x00, 0x74, 0x74,
60	0x74, 0xff, 0x3e, 0x3d, 0x3d, 0x02, 0x00, 0x16, 0x02, 0x38, 0x22, 0x1b,
61	0x3b, 0x11, 0x73, 0xbc, 0x5e, 0xb5, 0x39, 0x4b, 0xaa, 0x4a, 0x47, 0xf1,
62	0x49, 0xc2, 0x1d, 0x00, 0xb0, 0xff, 0x83, 0x02, 0x00, 0x16, 0x03, 0x36,
63	0xed, 0xe9, 0x36, 0xb9, 0x49, 0xba, 0x0a, 0xf6, 0x3a, 0x32, 0x6f, 0x4a,
64	0x79, 0xef, 0x4b, 0x03, 0xe7, 0x00, 0x5a, 0x38, 0xdc, 0xff, 0x7e, 0x0d,
65	0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31,
66	0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39, 0x25,
67	0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a, 0x04,
68	0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27, 0x43,
69	0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a, 0x05,
70	0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x0a, 0x04,
71	0x3c, 0x5a, 0x5a, 0x3c, 0x5a, 0x36, 0x3c, 0x52, 0x0a, 0x04, 0x24, 0x4e,
72	0x3c, 0x5a, 0x3c, 0x52, 0x24, 0x48, 0x06, 0x07, 0xaa, 0x3f, 0x42, 0x2e,
73	0x24, 0x48, 0x3c, 0x52, 0x5a, 0x36, 0x51, 0x33, 0x51, 0x33, 0x50, 0x34,
74	0x4b, 0x33, 0x4d, 0x34, 0x49, 0x32, 0x49, 0x30, 0x48, 0x31, 0x49, 0x30,
75	0x06, 0x08, 0xfa, 0xfa, 0x42, 0x50, 0x3e, 0x54, 0x40, 0x55, 0x3f, 0xc7,
76	0xeb, 0x41, 0xc8, 0x51, 0x42, 0xc9, 0x4f, 0x42, 0xc8, 0xda, 0x42, 0xca,
77	0x41, 0xc0, 0xf1, 0x5d, 0x45, 0xca, 0x81, 0x46, 0xc7, 0xb7, 0x46, 0xc8,
78	0xa9, 0x46, 0xc7, 0x42, 0x44, 0x51, 0x45, 0xc6, 0xb9, 0x43, 0xc6, 0x53,
79	0x0a, 0x07, 0x3c, 0x5c, 0x40, 0x5c, 0x42, 0x5e, 0x48, 0x5e, 0x4a, 0x5c,
80	0x46, 0x5a, 0x45, 0x4b, 0x06, 0x09, 0x9a, 0xf6, 0x03, 0x42, 0x2e, 0x24,
81	0x48, 0x4e, 0x3c, 0x5a, 0x5a, 0x3c, 0x36, 0x51, 0x33, 0x51, 0x33, 0x50,
82	0x34, 0x4b, 0x33, 0x4d, 0x34, 0x49, 0x32, 0x49, 0x30, 0x48, 0x31, 0x49,
83	0x30, 0x18, 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10,
84	0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02,
85	0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04,
86	0x10, 0x01, 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24,
87	0xb3, 0x99, 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30,
88	0x20, 0xb2, 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x09, 0x01, 0x0b,
89	0x02, 0x3e, 0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0,
90	0x21, 0x48, 0xed, 0x4d, 0xc8, 0x5a, 0x02, 0x0a, 0x09, 0x01, 0x0b, 0x02,
91	0x3e, 0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21,
92	0x48, 0x4c, 0xd4, 0xc7, 0x9c, 0x11, 0x0a, 0x09, 0x01, 0x0b, 0x02, 0x3e,
93	0x9b, 0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x47,
94	0x5c, 0xe7, 0xc6, 0x2c, 0x1a, 0x0a, 0x09, 0x01, 0x0b, 0x02, 0x3e, 0x9b,
95	0x12, 0xb5, 0xf9, 0x99, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x46, 0x1b,
96	0xf5, 0xc4, 0x28, 0x4e, 0x0a, 0x08, 0x01, 0x0c, 0x12, 0x3e, 0xc0, 0x21,
97	0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x45, 0xb6, 0x34,
98	0xc4, 0x22, 0x1f, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x0a, 0x01, 0x07,
99	0x02, 0x3e, 0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0,
100	0x21, 0x45, 0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x0b, 0x01, 0x08, 0x02,
101	0x3e, 0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21,
102	0x45, 0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x0c, 0x01, 0x09, 0x02, 0x3e,
103	0xc0, 0x21, 0xb6, 0x19, 0x10, 0x36, 0x19, 0x10, 0x3e, 0xc0, 0x21, 0x45,
104	0xb6, 0x34, 0xc4, 0x22, 0x1f, 0x0a, 0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98,
105	0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x48, 0xf2,
106	0x4e, 0xc7, 0xee, 0x3f, 0x01, 0x17, 0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01,
107	0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e,
108	0x7b, 0x5e, 0x48, 0xf2, 0x4e, 0xc7, 0xee, 0x3f, 0x0a, 0x08, 0x01, 0x0a,
109	0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b,
110	0x5e, 0x48, 0x53, 0xa1, 0xc6, 0xa0, 0xb6, 0x01, 0x17, 0x84, 0x22, 0x04,
111	0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35,
112	0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x48, 0x53, 0xa1, 0xc6, 0xa0, 0xb6, 0x0a,
113	0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9,
114	0x3d, 0x3e, 0x7b, 0x5e, 0x47, 0x69, 0xe9, 0xc4, 0xa6, 0x5a, 0x01, 0x17,
115	0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e, 0x98, 0xfd, 0xb5,
116	0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x47, 0x69, 0xe9, 0xc4,
117	0xa6, 0x5a, 0x0a, 0x08, 0x01, 0x0a, 0x12, 0x3e, 0x98, 0xfd, 0xb5, 0xf6,
118	0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x46, 0x2c, 0x90, 0xb8, 0xd1,
119	0xff, 0x01, 0x17, 0x84, 0x22, 0x04, 0x0a, 0x0d, 0x01, 0x0a, 0x02, 0x3e,
120	0x98, 0xfd, 0xb5, 0xf6, 0x6c, 0x35, 0xc9, 0x3d, 0x3e, 0x7b, 0x5e, 0x46,
121	0x2c, 0x90, 0xb8, 0xd1, 0xff
122};
123
124
125// parameters for the DMA resource
126static const uint32 kDMAResourceBufferCount			= 16;
127static const uint32 kDMAResourceBounceBufferCount	= 16;
128
129static const char* const kDriverModuleName
130	= "drivers/disk/virtual/ram_disk/driver_v1";
131static const char* const kControlDeviceModuleName
132	= "drivers/disk/virtual/ram_disk/control/device_v1";
133static const char* const kRawDeviceModuleName
134	= "drivers/disk/virtual/ram_disk/raw/device_v1";
135
136static const char* const kControlDeviceName = RAM_DISK_CONTROL_DEVICE_NAME;
137static const char* const kRawDeviceBaseName = RAM_DISK_RAW_DEVICE_BASE_NAME;
138
139static const char* const kFilePathItem = "ram_disk/file_path";
140static const char* const kDeviceSizeItem = "ram_disk/device_size";
141static const char* const kDeviceIDItem = "ram_disk/id";
142
143
144struct RawDevice;
145typedef DoublyLinkedList<RawDevice> RawDeviceList;
146
147struct device_manager_info* sDeviceManager;
148
149static RawDeviceList sDeviceList;
150static mutex sDeviceListLock = MUTEX_INITIALIZER("ram disk device list");
151static uint64 sUsedRawDeviceIDs = 0;
152
153
154static int32	allocate_raw_device_id();
155static void		free_raw_device_id(int32 id);
156
157
158struct Device {
159	Device(device_node* node)
160		:
161		fNode(node)
162	{
163		mutex_init(&fLock, "ram disk device");
164	}
165
166	virtual ~Device()
167	{
168		mutex_destroy(&fLock);
169	}
170
171	bool Lock()		{ mutex_lock(&fLock); return true; }
172	void Unlock()	{ mutex_unlock(&fLock); }
173
174	device_node* Node() const	{ return fNode; }
175
176	virtual status_t PublishDevice() = 0;
177
178protected:
179	mutex			fLock;
180	device_node*	fNode;
181};
182
183
184struct ControlDevice : Device {
185	ControlDevice(device_node* node)
186		:
187		Device(node)
188	{
189	}
190
191	status_t Register(const char* filePath, uint64 deviceSize, int32& _id)
192	{
193		int32 id = allocate_raw_device_id();
194		if (id < 0)
195			return B_BUSY;
196
197		device_attr attrs[] = {
198			{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
199				{.string = "RAM Disk Raw Device"}},
200			{kDeviceSizeItem, B_UINT64_TYPE, {.ui64 = deviceSize}},
201			{kDeviceIDItem, B_UINT32_TYPE, {.ui32 = (uint32)id}},
202			{kFilePathItem, B_STRING_TYPE, {.string = filePath}},
203			{NULL}
204		};
205
206		// If filePath is NULL, remove the attribute.
207		if (filePath == NULL) {
208			size_t count = sizeof(attrs) / sizeof(attrs[0]);
209			memset(attrs + count - 2, 0, sizeof(attrs[0]));
210		}
211
212		status_t error = sDeviceManager->register_node(
213			sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs,
214			NULL, NULL);
215		if (error != B_OK) {
216			free_raw_device_id(id);
217			return error;
218		}
219
220		_id = id;
221		return B_OK;
222	}
223
224	virtual status_t PublishDevice()
225	{
226		return sDeviceManager->publish_device(Node(), kControlDeviceName,
227			kControlDeviceModuleName);
228	}
229};
230
231
232struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
233	RawDevice(device_node* node)
234		:
235		Device(node),
236		fID(-1),
237		fUnregistered(false),
238		fDeviceSize(0),
239		fDeviceName(NULL),
240		fFilePath(NULL),
241		fCache(NULL),
242		fDMAResource(NULL),
243		fIOScheduler(NULL)
244	{
245	}
246
247	virtual ~RawDevice()
248	{
249		if (fID >= 0) {
250			MutexLocker locker(sDeviceListLock);
251			sDeviceList.Remove(this);
252		}
253
254		free(fDeviceName);
255		free(fFilePath);
256	}
257
258	int32 ID() const				{ return fID; }
259	off_t DeviceSize() const		{ return fDeviceSize; }
260	const char* DeviceName() const	{ return fDeviceName; }
261
262	bool IsUnregistered() const		{ return fUnregistered; }
263
264	void SetUnregistered(bool unregistered)
265	{
266		fUnregistered = unregistered;
267	}
268
269	status_t Init(int32 id, const char* filePath, uint64 deviceSize)
270	{
271		fID = id;
272		fFilePath = filePath != NULL ? strdup(filePath) : NULL;
273		if (filePath != NULL && fFilePath == NULL)
274			return B_NO_MEMORY;
275
276		fDeviceSize = (deviceSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE
277			* B_PAGE_SIZE;
278
279		if (fDeviceSize < B_PAGE_SIZE
280			|| (uint64)fDeviceSize / B_PAGE_SIZE
281				> vm_page_num_pages() * 2 / 3) {
282			return B_BAD_VALUE;
283		}
284
285		// construct our device path
286		KPath path(kRawDeviceBaseName);
287		char buffer[32];
288		snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fID);
289
290		status_t error = path.Append(buffer);
291		if (error != B_OK)
292			return error;
293
294		fDeviceName = path.DetachBuffer();
295
296		// insert into device list
297		RawDevice* nextDevice = NULL;
298		MutexLocker locker(sDeviceListLock);
299		for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
300				(nextDevice = it.Next()) != NULL;) {
301			if (nextDevice->ID() > fID)
302				break;
303		}
304
305		sDeviceList.InsertBefore(nextDevice, this);
306
307		return B_OK;
308	}
309
310	status_t Prepare()
311	{
312		status_t error = VMCacheFactory::CreateAnonymousCache(fCache, false, 0,
313			0, false, VM_PRIORITY_SYSTEM);
314		if (error != B_OK) {
315			Unprepare();
316			return error;
317		}
318
319		fCache->temporary = 1;
320		fCache->virtual_end = fDeviceSize;
321
322		error = fCache->Commit(fDeviceSize, VM_PRIORITY_SYSTEM);
323		if (error != B_OK) {
324			Unprepare();
325			return error;
326		}
327
328		if (fFilePath != NULL) {
329			error = _LoadFile();
330			if (error != B_OK) {
331				Unprepare();
332				return error;
333			}
334		}
335
336		// no DMA restrictions
337		const dma_restrictions restrictions = {};
338
339		fDMAResource = new(std::nothrow) DMAResource;
340		if (fDMAResource == NULL) {
341			Unprepare();
342			return B_NO_MEMORY;
343		}
344
345		error = fDMAResource->Init(restrictions, B_PAGE_SIZE,
346			kDMAResourceBufferCount, kDMAResourceBounceBufferCount);
347		if (error != B_OK) {
348			Unprepare();
349			return error;
350		}
351
352		fIOScheduler = new(std::nothrow) IOSchedulerSimple(fDMAResource);
353		if (fIOScheduler == NULL) {
354			Unprepare();
355			return B_NO_MEMORY;
356		}
357
358		error = fIOScheduler->Init("ram disk device scheduler");
359		if (error != B_OK) {
360			Unprepare();
361			return error;
362		}
363
364		fIOScheduler->SetCallback(&_DoIOEntry, this);
365
366		return B_OK;
367	}
368
369	void Unprepare()
370	{
371		delete fIOScheduler;
372		fIOScheduler = NULL;
373
374		delete fDMAResource;
375		fDMAResource = NULL;
376
377		if (fCache != NULL) {
378			fCache->Lock();
379			fCache->ReleaseRefAndUnlock();
380			fCache = NULL;
381		}
382	}
383
384	void GetInfo(ram_disk_ioctl_info& _info) const
385	{
386		_info.id = fID;
387		_info.size = fDeviceSize;
388		memset(&_info.path, 0, sizeof(_info.path));
389		if (fFilePath != NULL)
390			strlcpy(_info.path, fFilePath, sizeof(_info.path));
391	}
392
393	status_t Flush()
394	{
395		static const size_t kPageCountPerIteration = 1024;
396		static const size_t kMaxGapSize = 15;
397
398		FileDescriptorCloser fd(open(fFilePath, O_WRONLY));
399		if (!fd.IsSet())
400			return errno;
401
402		vm_page** pages = new(std::nothrow) vm_page*[kPageCountPerIteration];
403		ArrayDeleter<vm_page*> pagesDeleter(pages);
404
405		uint8* buffer = (uint8*)malloc(kPageCountPerIteration * B_PAGE_SIZE);
406		MemoryDeleter bufferDeleter(buffer);
407
408		if (pages == NULL || buffer == NULL)
409			return B_NO_MEMORY;
410
411		// Iterate through all pages of the cache and write those back that have
412		// been modified.
413		AutoLocker<VMCache> locker(fCache);
414
415		status_t error = B_OK;
416
417		for (off_t offset = 0; offset < fDeviceSize;) {
418			// find the first modified page at or after the current offset
419			VMCachePagesTree::Iterator it
420				= fCache->pages.GetIterator(offset / B_PAGE_SIZE, true, true);
421			vm_page* firstModified;
422			while ((firstModified = it.Next()) != NULL
423				&& !firstModified->modified) {
424			}
425
426			if (firstModified == NULL)
427				break;
428
429			if (firstModified->busy) {
430				fCache->WaitForPageEvents(firstModified, PAGE_EVENT_NOT_BUSY,
431					true);
432				continue;
433			}
434
435			pages[0] = firstModified;
436			page_num_t firstPageIndex = firstModified->cache_offset;
437			offset = firstPageIndex * B_PAGE_SIZE;
438
439			// Collect more pages until the gap between two modified pages gets
440			// too large or we hit the end of our array.
441			size_t previousModifiedIndex = 0;
442			size_t previousIndex = 0;
443			while (vm_page* page = it.Next()) {
444				page_num_t index = page->cache_offset - firstPageIndex;
445				if (page->busy
446					|| index >= kPageCountPerIteration
447					|| index - previousModifiedIndex > kMaxGapSize) {
448					break;
449				}
450
451				pages[index] = page;
452
453				// clear page array gap since the previous page
454				if (previousIndex + 1 < index) {
455					memset(pages + previousIndex + 1, 0,
456						(index - previousIndex - 1) * sizeof(vm_page*));
457				}
458
459				previousIndex = index;
460				if (page->modified)
461					previousModifiedIndex = index;
462			}
463
464			// mark all pages we want to write busy
465			size_t pagesToWrite = previousModifiedIndex + 1;
466			for (size_t i = 0; i < pagesToWrite; i++) {
467				if (vm_page* page = pages[i]) {
468					DEBUG_PAGE_ACCESS_START(page);
469					page->busy = true;
470				}
471			}
472
473			locker.Unlock();
474
475			// copy the pages to our buffer
476			for (size_t i = 0; i < pagesToWrite; i++) {
477				if (vm_page* page = pages[i]) {
478					error = vm_memcpy_from_physical(buffer + i * B_PAGE_SIZE,
479						page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE,
480						false);
481					if (error != B_OK) {
482						dprintf("ramdisk: error copying page %" B_PRIu64
483							" data: %s\n", (uint64)page->physical_page_number,
484							strerror(error));
485						break;
486					}
487				} else
488					memset(buffer + i * B_PAGE_SIZE, 0, B_PAGE_SIZE);
489			}
490
491			// write the buffer
492			if (error == B_OK) {
493				ssize_t bytesWritten = pwrite(fd.Get(), buffer,
494					pagesToWrite * B_PAGE_SIZE, offset);
495				if (bytesWritten < 0) {
496					dprintf("ramdisk: error writing pages to file: %s\n",
497						strerror(bytesWritten));
498					error = bytesWritten;
499				}
500				else if ((size_t)bytesWritten != pagesToWrite * B_PAGE_SIZE) {
501					dprintf("ramdisk: error writing pages to file: short "
502						"write (%zd/%zu)\n", bytesWritten,
503						pagesToWrite * B_PAGE_SIZE);
504					error = B_ERROR;
505				}
506			}
507
508			// mark the pages unbusy, on success also unmodified
509			locker.Lock();
510
511			for (size_t i = 0; i < pagesToWrite; i++) {
512				if (vm_page* page = pages[i]) {
513					if (error == B_OK)
514						page->modified = false;
515					fCache->MarkPageUnbusy(page);
516					DEBUG_PAGE_ACCESS_END(page);
517				}
518			}
519
520			if (error != B_OK)
521				break;
522
523			offset += pagesToWrite * B_PAGE_SIZE;
524		}
525
526		return error;
527	}
528
529	status_t Trim(fs_trim_data* trimData)
530	{
531		TRACE("trim_device()\n");
532
533		trimData->trimmed_size = 0;
534
535		const off_t deviceSize = fDeviceSize; // in bytes
536		if (deviceSize < 0)
537			return B_BAD_VALUE;
538
539		STATIC_ASSERT(sizeof(deviceSize) <= sizeof(uint64));
540		ASSERT(deviceSize >= 0);
541
542		// Do not trim past device end
543		for (uint32 i = 0; i < trimData->range_count; i++) {
544			uint64 offset = trimData->ranges[i].offset;
545			uint64& size = trimData->ranges[i].size;
546
547			if (offset >= (uint64)deviceSize)
548				return B_BAD_VALUE;
549			size = min_c(size, (uint64)deviceSize - offset);
550		}
551
552		status_t result = B_OK;
553		uint64 trimmedSize = 0;
554		for (uint32 i = 0; i < trimData->range_count; i++) {
555			uint64 offset = trimData->ranges[i].offset;
556			uint64 length = trimData->ranges[i].size;
557
558			// Round up offset and length to multiple of the page size
559			// The offset is rounded up, so some space may be left
560			// (not trimmed) at the start of the range.
561			offset = (offset + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
562			// Adjust the length for the possibly skipped range
563			length -= offset - trimData->ranges[i].offset;
564			// The length is rounded down, so some space at the end may also
565			// be left (not trimmed).
566			length &= ~(B_PAGE_SIZE - 1);
567
568			if (length == 0)
569				continue;
570
571			TRACE("ramdisk: trim %" B_PRIu64 " bytes from %" B_PRIu64 "\n",
572				length, offset);
573
574			ASSERT(offset % B_PAGE_SIZE == 0);
575			ASSERT(length % B_PAGE_SIZE == 0);
576
577			vm_page** pages = new(std::nothrow) vm_page*[length / B_PAGE_SIZE];
578			if (pages == NULL) {
579				result = B_NO_MEMORY;
580				break;
581			}
582			ArrayDeleter<vm_page*> pagesDeleter(pages);
583
584			_GetPages((off_t)offset, (off_t)length, false, pages);
585
586			AutoLocker<VMCache> locker(fCache);
587			uint64 j;
588			for (j = 0; j < length / B_PAGE_SIZE; j++) {
589				// If we run out of pages (some may already be trimmed), stop.
590				if (pages[j] == NULL)
591					break;
592
593				TRACE("free range %" B_PRIu32 ", page %" B_PRIu64 ", offset %"
594					B_PRIu64 "\n", i, j, offset);
595				if (pages[j]->Cache())
596					fCache->RemovePage(pages[j]);
597				vm_page_free(NULL, pages[j]);
598				trimmedSize += B_PAGE_SIZE;
599			}
600		}
601
602		trimData->trimmed_size = trimmedSize;
603
604		return result;
605	}
606
607
608
609	status_t DoIO(IORequest* request)
610	{
611		return fIOScheduler->ScheduleRequest(request);
612	}
613
614	virtual status_t PublishDevice()
615	{
616		return sDeviceManager->publish_device(Node(), fDeviceName,
617			kRawDeviceModuleName);
618	}
619
620private:
621	static status_t _DoIOEntry(void* data, IOOperation* operation)
622	{
623		return ((RawDevice*)data)->_DoIO(operation);
624	}
625
626	status_t _DoIO(IOOperation* operation)
627	{
628		off_t offset = operation->Offset();
629		generic_size_t length = operation->Length();
630
631		ASSERT(offset % B_PAGE_SIZE == 0);
632		ASSERT(length % B_PAGE_SIZE == 0);
633
634		const generic_io_vec* vecs = operation->Vecs();
635		generic_size_t vecOffset = 0;
636		bool isWrite = operation->IsWrite();
637
638		vm_page** pages = new(std::nothrow) vm_page*[length / B_PAGE_SIZE];
639		if (pages == NULL)
640			return B_NO_MEMORY;
641		ArrayDeleter<vm_page*> pagesDeleter(pages);
642
643		_GetPages(offset, length, isWrite, pages);
644
645		status_t error = B_OK;
646		size_t index = 0;
647
648		while (length > 0) {
649			vm_page* page = pages[index];
650
651			if (isWrite)
652				page->modified = true;
653
654			error = _CopyData(page, vecs, vecOffset, isWrite);
655			if (error != B_OK)
656				break;
657
658			offset += B_PAGE_SIZE;
659			length -= B_PAGE_SIZE;
660			index++;
661		}
662
663		_PutPages(operation->Offset(), operation->Length(), pages,
664			error == B_OK);
665
666		if (error != B_OK) {
667			fIOScheduler->OperationCompleted(operation, error, 0);
668			return error;
669		}
670
671		fIOScheduler->OperationCompleted(operation, B_OK, operation->Length());
672		return B_OK;
673	}
674
675	void _GetPages(off_t offset, off_t length, bool isWrite, vm_page** pages)
676	{
677		// TODO: This method is duplicated in ramfs' DataContainer. Perhaps it
678		// should be put into a common location?
679
680		// get the pages, we already have
681		AutoLocker<VMCache> locker(fCache);
682
683		size_t pageCount = length / B_PAGE_SIZE;
684		size_t index = 0;
685		size_t missingPages = 0;
686
687		while (length > 0) {
688			vm_page* page = fCache->LookupPage(offset);
689			if (page != NULL) {
690				if (page->busy) {
691					fCache->WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true);
692					continue;
693				}
694
695				DEBUG_PAGE_ACCESS_START(page);
696				page->busy = true;
697			} else
698				missingPages++;
699
700			pages[index++] = page;
701			offset += B_PAGE_SIZE;
702			length -= B_PAGE_SIZE;
703		}
704
705		locker.Unlock();
706
707		// For a write we need to reserve the missing pages.
708		if (isWrite && missingPages > 0) {
709			vm_page_reservation reservation;
710			vm_page_reserve_pages(&reservation, missingPages,
711				VM_PRIORITY_SYSTEM);
712
713			for (size_t i = 0; i < pageCount; i++) {
714				if (pages[i] != NULL)
715					continue;
716
717				pages[i] = vm_page_allocate_page(&reservation,
718					PAGE_STATE_WIRED | VM_PAGE_ALLOC_BUSY);
719
720				if (--missingPages == 0)
721					break;
722			}
723
724			vm_page_unreserve_pages(&reservation);
725		}
726	}
727
728	void _PutPages(off_t offset, off_t length, vm_page** pages, bool success)
729	{
730		// TODO: This method is duplicated in ramfs' DataContainer. Perhaps it
731		// should be put into a common location?
732
733		AutoLocker<VMCache> locker(fCache);
734
735		// Mark all pages unbusy. On error free the newly allocated pages.
736		size_t index = 0;
737
738		while (length > 0) {
739			vm_page* page = pages[index++];
740			if (page != NULL) {
741				if (page->CacheRef() == NULL) {
742					if (success) {
743						fCache->InsertPage(page, offset);
744						fCache->MarkPageUnbusy(page);
745						DEBUG_PAGE_ACCESS_END(page);
746					} else
747						vm_page_free(NULL, page);
748				} else {
749					fCache->MarkPageUnbusy(page);
750					DEBUG_PAGE_ACCESS_END(page);
751				}
752			}
753
754			offset += B_PAGE_SIZE;
755			length -= B_PAGE_SIZE;
756		}
757	}
758
759	status_t _CopyData(vm_page* page, const generic_io_vec*& vecs,
760		generic_size_t& vecOffset, bool toPage)
761	{
762		// map page to virtual memory
763		Thread* thread = thread_get_current_thread();
764		uint8* pageData = NULL;
765		void* handle;
766		if (page != NULL) {
767			thread_pin_to_current_cpu(thread);
768			addr_t virtualAddress;
769			status_t error = vm_get_physical_page_current_cpu(
770				page->physical_page_number * B_PAGE_SIZE, &virtualAddress,
771				&handle);
772			if (error != B_OK) {
773				thread_unpin_from_current_cpu(thread);
774				return error;
775			}
776
777			pageData = (uint8*)virtualAddress;
778		}
779
780		status_t error = B_OK;
781		size_t length = B_PAGE_SIZE;
782		while (length > 0) {
783			size_t toCopy = std::min((generic_size_t)length,
784				vecs->length - vecOffset);
785
786			if (toCopy == 0) {
787				vecs++;
788				vecOffset = 0;
789				continue;
790			}
791
792			phys_addr_t vecAddress = vecs->base + vecOffset;
793
794			error = toPage
795				? vm_memcpy_from_physical(pageData, vecAddress, toCopy, false)
796				: (page != NULL
797					? vm_memcpy_to_physical(vecAddress, pageData, toCopy, false)
798					: vm_memset_physical(vecAddress, 0, toCopy));
799			if (error != B_OK)
800				break;
801
802			pageData += toCopy;
803			length -= toCopy;
804			vecOffset += toCopy;
805		}
806
807		if (page != NULL) {
808			vm_put_physical_page_current_cpu((addr_t)pageData, handle);
809			thread_unpin_from_current_cpu(thread);
810		}
811
812		return error;
813	}
814
815	status_t _LoadFile()
816	{
817		static const size_t kPageCountPerIteration = 1024;
818
819		FileDescriptorCloser fd(open(fFilePath, O_RDONLY));
820		if (!fd.IsSet())
821			return errno;
822
823		ArrayDeleter<vm_page*> pages(
824			new(std::nothrow) vm_page*[kPageCountPerIteration]);
825
826		ArrayDeleter<uint8> buffer(
827			new(std::nothrow) uint8[kPageCountPerIteration * B_PAGE_SIZE]);
828			// TODO: Ideally we wouldn't use a buffer to read the file content,
829			// but read into the pages we allocated directly. Unfortunately
830			// there's no API to do that yet.
831
832		if (!pages.IsSet() || !buffer.IsSet())
833			return B_NO_MEMORY;
834
835		status_t error = B_OK;
836
837		page_num_t allocatedPages = 0;
838		off_t offset = 0;
839		off_t sizeRemaining = fDeviceSize;
840		while (sizeRemaining > 0) {
841			// Note: fDeviceSize is B_PAGE_SIZE aligned.
842			size_t pagesToRead = std::min(kPageCountPerIteration,
843				size_t(sizeRemaining / B_PAGE_SIZE));
844
845			// allocate the missing pages
846			if (allocatedPages < pagesToRead) {
847				vm_page_reservation reservation;
848				vm_page_reserve_pages(&reservation,
849					pagesToRead - allocatedPages, VM_PRIORITY_SYSTEM);
850
851				while (allocatedPages < pagesToRead) {
852					pages[allocatedPages++]
853						= vm_page_allocate_page(&reservation, PAGE_STATE_WIRED);
854				}
855
856				vm_page_unreserve_pages(&reservation);
857			}
858
859			// read from the file
860			size_t bytesToRead = pagesToRead * B_PAGE_SIZE;
861			ssize_t bytesRead = pread(fd.Get(), buffer.Get(), bytesToRead,
862				offset);
863			if (bytesRead < 0) {
864				error = bytesRead;
865				break;
866			}
867			size_t pagesRead = (bytesRead + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
868			if (pagesRead < pagesToRead) {
869				error = B_ERROR;
870				break;
871			}
872
873			// clear the last read page, if partial
874			if ((size_t)bytesRead < pagesRead * B_PAGE_SIZE) {
875				memset(buffer.Get() + bytesRead, 0,
876					pagesRead * B_PAGE_SIZE - bytesRead);
877			}
878
879			// copy data to allocated pages
880			for (size_t i = 0; i < pagesRead; i++) {
881				vm_page* page = pages[i];
882				error = vm_memcpy_to_physical(
883					page->physical_page_number * B_PAGE_SIZE,
884					buffer.Get() + i * B_PAGE_SIZE, B_PAGE_SIZE, false);
885				if (error != B_OK)
886					break;
887			}
888
889			if (error != B_OK)
890				break;
891
892			// Add pages to cache. Ignore clear pages, though. Move those to the
893			// beginning of the array, so we can reuse them in the next
894			// iteration.
895			AutoLocker<VMCache> locker(fCache);
896
897			size_t clearPages = 0;
898			for (size_t i = 0; i < pagesRead; i++) {
899				uint64* pageData = (uint64*)(buffer.Get() + i * B_PAGE_SIZE);
900				bool isClear = true;
901				for (size_t k = 0; isClear && k < B_PAGE_SIZE / 8; k++)
902					isClear = pageData[k] == 0;
903
904				if (isClear) {
905					pages[clearPages++] = pages[i];
906				} else {
907					fCache->InsertPage(pages[i], offset + i * B_PAGE_SIZE);
908					DEBUG_PAGE_ACCESS_END(pages[i]);
909				}
910			}
911
912			locker.Unlock();
913
914			// Move any left-over allocated pages to the end of the empty pages
915			// and compute the new allocated pages count.
916			if (pagesRead < allocatedPages) {
917				size_t count = allocatedPages - pagesRead;
918				memcpy(pages.Get() + clearPages, pages.Get() + pagesRead,
919					count * sizeof(vm_page*));
920				clearPages += count;
921			}
922			allocatedPages = clearPages;
923
924			offset += pagesRead * B_PAGE_SIZE;
925			sizeRemaining -= pagesRead * B_PAGE_SIZE;
926		}
927
928		// free left-over allocated pages
929		for (size_t i = 0; i < allocatedPages; i++)
930			vm_page_free(NULL, pages[i]);
931
932		return error;
933	}
934
935private:
936	int32			fID;
937	bool			fUnregistered;
938	off_t			fDeviceSize;
939	char*			fDeviceName;
940	char*			fFilePath;
941	VMCache*		fCache;
942	DMAResource*	fDMAResource;
943	IOScheduler*	fIOScheduler;
944};
945
946
947struct RawDeviceCookie {
948	RawDeviceCookie(RawDevice* device, int openMode)
949		:
950		fDevice(device),
951		fOpenMode(openMode)
952	{
953	}
954
955	RawDevice* Device() const	{ return fDevice; }
956	int OpenMode() const		{ return fOpenMode; }
957
958private:
959	RawDevice*	fDevice;
960	int			fOpenMode;
961};
962
963
964// #pragma mark -
965
966
967static int32
968allocate_raw_device_id()
969{
970	MutexLocker deviceListLocker(sDeviceListLock);
971	for (size_t i = 0; i < sizeof(sUsedRawDeviceIDs) * 8; i++) {
972		if ((sUsedRawDeviceIDs & ((uint64)1 << i)) == 0) {
973			sUsedRawDeviceIDs |= (uint64)1 << i;
974			return (int32)i;
975		}
976	}
977
978	return -1;
979}
980
981
982static void
983free_raw_device_id(int32 id)
984{
985	MutexLocker deviceListLocker(sDeviceListLock);
986	sUsedRawDeviceIDs &= ~((uint64)1 << id);
987}
988
989
990static RawDevice*
991find_raw_device(int32 id)
992{
993	for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
994			RawDevice* device = it.Next();) {
995		if (device->ID() == id)
996			return device;
997	}
998
999	return NULL;
1000}
1001
1002
1003static status_t
1004ioctl_register(ControlDevice* controlDevice, ram_disk_ioctl_register* request)
1005{
1006	KPath path;
1007	uint64 deviceSize = 0;
1008
1009	if (request->path[0] != '\0') {
1010		// check if the path is null-terminated
1011		if (strnlen(request->path, sizeof(request->path))
1012				== sizeof(request->path)) {
1013			return B_BAD_VALUE;
1014		}
1015
1016		// get a normalized file path
1017		status_t error = path.SetTo(request->path, true);
1018		if (error != B_OK) {
1019			dprintf("ramdisk: register: Invalid path \"%s\": %s\n",
1020				request->path, strerror(error));
1021			return B_BAD_VALUE;
1022		}
1023
1024		struct stat st;
1025		if (lstat(path.Path(), &st) != 0) {
1026			dprintf("ramdisk: register: Failed to stat \"%s\": %s\n",
1027				path.Path(), strerror(errno));
1028			return errno;
1029		}
1030
1031		if (!S_ISREG(st.st_mode)) {
1032			dprintf("ramdisk: register: \"%s\" is not a file!\n", path.Path());
1033			return B_BAD_VALUE;
1034		}
1035
1036		deviceSize = st.st_size;
1037	} else {
1038		deviceSize = request->size;
1039	}
1040
1041	return controlDevice->Register(path.Length() > 0 ? path.Path() : NULL,
1042		deviceSize, request->id);
1043}
1044
1045
1046static status_t
1047ioctl_unregister(ControlDevice* controlDevice,
1048	ram_disk_ioctl_unregister* request)
1049{
1050	// find the device in the list and unregister it
1051	MutexLocker locker(sDeviceListLock);
1052	RawDevice* device = find_raw_device(request->id);
1053	if (device == NULL)
1054		return B_ENTRY_NOT_FOUND;
1055
1056	// mark unregistered before we unlock
1057	if (device->IsUnregistered())
1058		return B_BUSY;
1059	device->SetUnregistered(true);
1060	locker.Unlock();
1061
1062	device_node* node = device->Node();
1063	status_t error = sDeviceManager->unpublish_device(node,
1064		device->DeviceName());
1065	if (error != B_OK) {
1066		dprintf("ramdisk: unregister: Failed to unpublish device \"%s\": %s\n",
1067			device->DeviceName(), strerror(error));
1068		return error;
1069	}
1070
1071	error = sDeviceManager->unregister_node(node);
1072	// Note: B_BUSY is OK. The node will removed as soon as possible.
1073	if (error != B_OK && error != B_BUSY) {
1074		dprintf("ramdisk: unregister: Failed to unregister node for device %"
1075			B_PRId32 ": %s\n", request->id, strerror(error));
1076		return error;
1077	}
1078
1079	return B_OK;
1080}
1081
1082
1083static status_t
1084ioctl_info(RawDevice* device, ram_disk_ioctl_info* request)
1085{
1086	device->GetInfo(*request);
1087	return B_OK;
1088}
1089
1090
1091template<typename DeviceType, typename Request>
1092static status_t
1093handle_ioctl(DeviceType* device,
1094	status_t (*handler)(DeviceType*, Request*), void* buffer)
1095{
1096	// copy request to the kernel heap
1097	if (buffer == NULL || !IS_USER_ADDRESS(buffer))
1098		return B_BAD_ADDRESS;
1099
1100	Request* request = new(std::nothrow) Request;
1101	if (request == NULL)
1102		return B_NO_MEMORY;
1103	ObjectDeleter<Request> requestDeleter(request);
1104
1105	if (user_memcpy(request, buffer, sizeof(Request)) != B_OK)
1106		return B_BAD_ADDRESS;
1107
1108	// handle the ioctl
1109	status_t error = handler(device, request);
1110	if (error != B_OK)
1111		return error;
1112
1113	// copy the request back to userland
1114	if (user_memcpy(buffer, request, sizeof(Request)) != B_OK)
1115		return B_BAD_ADDRESS;
1116
1117	return B_OK;
1118}
1119
1120
1121//	#pragma mark - driver
1122
1123
1124static float
1125ram_disk_driver_supports_device(device_node* parent)
1126{
1127	const char* bus = NULL;
1128	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)
1129			== B_OK
1130		&& strcmp(bus, "generic") == 0) {
1131		return 0.8;
1132	}
1133
1134	return -1;
1135}
1136
1137
1138static status_t
1139ram_disk_driver_register_device(device_node* parent)
1140{
1141	device_attr attrs[] = {
1142		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
1143			{.string = "RAM Disk Control Device"}},
1144		{NULL}
1145	};
1146
1147	return sDeviceManager->register_node(parent, kDriverModuleName, attrs, NULL,
1148		NULL);
1149}
1150
1151
1152static status_t
1153ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
1154{
1155	uint64 deviceSize;
1156	if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
1157			false) == B_OK) {
1158		int32 id = -1;
1159		sDeviceManager->get_attr_uint32(node, kDeviceIDItem, (uint32*)&id,
1160			false);
1161		if (id < 0)
1162			return B_ERROR;
1163
1164		const char* filePath = NULL;
1165		sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
1166
1167		RawDevice* device = new(std::nothrow) RawDevice(node);
1168		if (device == NULL)
1169			return B_NO_MEMORY;
1170
1171		status_t error = device->Init(id, filePath, deviceSize);
1172		if (error != B_OK) {
1173			delete device;
1174			return error;
1175		}
1176
1177		*_driverCookie = (Device*)device;
1178	} else {
1179		ControlDevice* device = new(std::nothrow) ControlDevice(node);
1180		if (device == NULL)
1181			return B_NO_MEMORY;
1182
1183		*_driverCookie = (Device*)device;
1184	}
1185
1186	return B_OK;
1187}
1188
1189
1190static void
1191ram_disk_driver_uninit_driver(void* driverCookie)
1192{
1193	Device* device = (Device*)driverCookie;
1194	if (RawDevice* rawDevice = dynamic_cast<RawDevice*>(device))
1195		free_raw_device_id(rawDevice->ID());
1196	delete device;
1197}
1198
1199
1200static status_t
1201ram_disk_driver_register_child_devices(void* driverCookie)
1202{
1203	Device* device = (Device*)driverCookie;
1204	return device->PublishDevice();
1205}
1206
1207
1208//	#pragma mark - control device
1209
1210
1211static status_t
1212ram_disk_control_device_init_device(void* driverCookie, void** _deviceCookie)
1213{
1214	*_deviceCookie = driverCookie;
1215	return B_OK;
1216}
1217
1218
1219static void
1220ram_disk_control_device_uninit_device(void* deviceCookie)
1221{
1222}
1223
1224
1225static status_t
1226ram_disk_control_device_open(void* deviceCookie, const char* path, int openMode,
1227	void** _cookie)
1228{
1229	*_cookie = deviceCookie;
1230	return B_OK;
1231}
1232
1233
1234static status_t
1235ram_disk_control_device_close(void* cookie)
1236{
1237	return B_OK;
1238}
1239
1240
1241static status_t
1242ram_disk_control_device_free(void* cookie)
1243{
1244	return B_OK;
1245}
1246
1247
1248static status_t
1249ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
1250	size_t* _length)
1251{
1252	return B_BAD_VALUE;
1253}
1254
1255
1256static status_t
1257ram_disk_control_device_write(void* cookie, off_t position, const void* data,
1258	size_t* _length)
1259{
1260	return B_BAD_VALUE;
1261}
1262
1263
1264static status_t
1265ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
1266	size_t length)
1267{
1268	ControlDevice* device = (ControlDevice*)cookie;
1269
1270	switch (op) {
1271		case RAM_DISK_IOCTL_REGISTER:
1272			return handle_ioctl(device, &ioctl_register, buffer);
1273
1274		case RAM_DISK_IOCTL_UNREGISTER:
1275			return handle_ioctl(device, &ioctl_unregister, buffer);
1276	}
1277
1278	return B_BAD_VALUE;
1279}
1280
1281
1282//	#pragma mark - raw device
1283
1284
1285static status_t
1286ram_disk_raw_device_init_device(void* driverCookie, void** _deviceCookie)
1287{
1288	RawDevice* device = static_cast<RawDevice*>((Device*)driverCookie);
1289
1290	status_t error = device->Prepare();
1291	if (error != B_OK)
1292		return error;
1293
1294	*_deviceCookie = device;
1295	return B_OK;
1296}
1297
1298
1299static void
1300ram_disk_raw_device_uninit_device(void* deviceCookie)
1301{
1302	RawDevice* device = (RawDevice*)deviceCookie;
1303	device->Unprepare();
1304}
1305
1306
1307static status_t
1308ram_disk_raw_device_open(void* deviceCookie, const char* path, int openMode,
1309	void** _cookie)
1310{
1311	RawDevice* device = (RawDevice*)deviceCookie;
1312
1313	RawDeviceCookie* cookie = new(std::nothrow) RawDeviceCookie(device,
1314		openMode);
1315	if (cookie == NULL)
1316		return B_NO_MEMORY;
1317
1318	*_cookie = cookie;
1319	return B_OK;
1320}
1321
1322
1323static status_t
1324ram_disk_raw_device_close(void* cookie)
1325{
1326	return B_OK;
1327}
1328
1329
1330static status_t
1331ram_disk_raw_device_free(void* _cookie)
1332{
1333	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1334	delete cookie;
1335	return B_OK;
1336}
1337
1338
1339static status_t
1340ram_disk_raw_device_read(void* _cookie, off_t pos, void* buffer,
1341	size_t* _length)
1342{
1343	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1344	RawDevice* device = cookie->Device();
1345
1346	size_t length = *_length;
1347
1348	if (pos >= device->DeviceSize())
1349		return B_BAD_VALUE;
1350	if (pos + (off_t)length > device->DeviceSize())
1351		length = device->DeviceSize() - pos;
1352
1353	IORequest request;
1354	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
1355	if (status != B_OK)
1356		return status;
1357
1358	status = device->DoIO(&request);
1359	if (status != B_OK)
1360		return status;
1361
1362	status = request.Wait(0, 0);
1363	if (status == B_OK)
1364		*_length = length;
1365	return status;
1366}
1367
1368
1369static status_t
1370ram_disk_raw_device_write(void* _cookie, off_t pos, const void* buffer,
1371	size_t* _length)
1372{
1373	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1374	RawDevice* device = cookie->Device();
1375
1376	size_t length = *_length;
1377
1378	if (pos >= device->DeviceSize())
1379		return B_BAD_VALUE;
1380	if (pos + (off_t)length > device->DeviceSize())
1381		length = device->DeviceSize() - pos;
1382
1383	IORequest request;
1384	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
1385	if (status != B_OK)
1386		return status;
1387
1388	status = device->DoIO(&request);
1389	if (status != B_OK)
1390		return status;
1391
1392	status = request.Wait(0, 0);
1393	if (status == B_OK)
1394		*_length = length;
1395
1396	return status;
1397}
1398
1399
1400static status_t
1401ram_disk_raw_device_io(void* _cookie, io_request* request)
1402{
1403	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1404	RawDevice* device = cookie->Device();
1405
1406	return device->DoIO(request);
1407}
1408
1409
1410static status_t
1411ram_disk_raw_device_control(void* _cookie, uint32 op, void* buffer,
1412	size_t length)
1413{
1414	RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1415	RawDevice* device = cookie->Device();
1416
1417	switch (op) {
1418		case B_GET_DEVICE_SIZE:
1419		{
1420			size_t size = device->DeviceSize();
1421			return user_memcpy(buffer, &size, sizeof(size_t));
1422		}
1423
1424		case B_SET_NONBLOCKING_IO:
1425		case B_SET_BLOCKING_IO:
1426			return B_OK;
1427
1428		case B_GET_READ_STATUS:
1429		case B_GET_WRITE_STATUS:
1430		{
1431			bool value = true;
1432			return user_memcpy(buffer, &value, sizeof(bool));
1433		}
1434
1435		case B_GET_GEOMETRY:
1436		case B_GET_BIOS_GEOMETRY:
1437		{
1438			if (buffer == NULL || length > sizeof(device_geometry))
1439				return B_BAD_VALUE;
1440
1441			device_geometry geometry;
1442			geometry.bytes_per_sector = B_PAGE_SIZE;
1443			geometry.sectors_per_track = 1;
1444			geometry.cylinder_count = device->DeviceSize() / B_PAGE_SIZE;
1445				// TODO: We're limited to 2^32 * B_PAGE_SIZE, if we don't use
1446				// sectors_per_track and head_count.
1447			geometry.head_count = 1;
1448			geometry.device_type = B_DISK;
1449			geometry.removable = true;
1450			geometry.read_only = false;
1451			geometry.write_once = false;
1452			geometry.bytes_per_physical_sector = B_PAGE_SIZE;
1453
1454			return user_memcpy(buffer, &geometry, length);
1455		}
1456
1457		case B_GET_MEDIA_STATUS:
1458		{
1459			status_t status = B_OK;
1460			return user_memcpy(buffer, &status, sizeof(status_t));
1461		}
1462
1463		case B_GET_ICON_NAME:
1464			return user_strlcpy((char*)buffer, "devices/drive-ramdisk",
1465				B_FILE_NAME_LENGTH);
1466
1467		case B_GET_VECTOR_ICON:
1468		{
1469			device_icon iconData;
1470			if (length != sizeof(device_icon))
1471				return B_BAD_VALUE;
1472			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
1473				return B_BAD_ADDRESS;
1474
1475			if (iconData.icon_size >= (int32)sizeof(kRamdiskIcon)) {
1476				if (user_memcpy(iconData.icon_data, kRamdiskIcon,
1477						sizeof(kRamdiskIcon)) != B_OK)
1478					return B_BAD_ADDRESS;
1479			}
1480
1481			iconData.icon_size = sizeof(kRamdiskIcon);
1482			return user_memcpy(buffer, &iconData, sizeof(device_icon));
1483		}
1484
1485		case B_SET_UNINTERRUPTABLE_IO:
1486		case B_SET_INTERRUPTABLE_IO:
1487		case B_FLUSH_DRIVE_CACHE:
1488			return B_OK;
1489
1490		case RAM_DISK_IOCTL_FLUSH:
1491		{
1492			status_t error = device->Flush();
1493			if (error != B_OK) {
1494				dprintf("ramdisk: flush: Failed to flush device: %s\n",
1495					strerror(error));
1496				return error;
1497			}
1498
1499			return B_OK;
1500		}
1501
1502		case B_TRIM_DEVICE:
1503		{
1504			// We know the buffer is kernel-side because it has been
1505			// preprocessed in devfs
1506			ASSERT(IS_KERNEL_ADDRESS(buffer));
1507			return device->Trim((fs_trim_data*)buffer);
1508		}
1509
1510		case RAM_DISK_IOCTL_INFO:
1511			return handle_ioctl(device, &ioctl_info, buffer);
1512	}
1513
1514	return B_BAD_VALUE;
1515}
1516
1517
1518// #pragma mark -
1519
1520
1521module_dependency module_dependencies[] = {
1522	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1523	{}
1524};
1525
1526
1527static const struct driver_module_info sChecksumDeviceDriverModule = {
1528	{
1529		kDriverModuleName,
1530		0,
1531		NULL
1532	},
1533
1534	ram_disk_driver_supports_device,
1535	ram_disk_driver_register_device,
1536	ram_disk_driver_init_driver,
1537	ram_disk_driver_uninit_driver,
1538	ram_disk_driver_register_child_devices
1539};
1540
1541static const struct device_module_info sChecksumControlDeviceModule = {
1542	{
1543		kControlDeviceModuleName,
1544		0,
1545		NULL
1546	},
1547
1548	ram_disk_control_device_init_device,
1549	ram_disk_control_device_uninit_device,
1550	NULL,
1551
1552	ram_disk_control_device_open,
1553	ram_disk_control_device_close,
1554	ram_disk_control_device_free,
1555
1556	ram_disk_control_device_read,
1557	ram_disk_control_device_write,
1558	NULL,	// io
1559
1560	ram_disk_control_device_control,
1561
1562	NULL,	// select
1563	NULL	// deselect
1564};
1565
1566static const struct device_module_info sChecksumRawDeviceModule = {
1567	{
1568		kRawDeviceModuleName,
1569		0,
1570		NULL
1571	},
1572
1573	ram_disk_raw_device_init_device,
1574	ram_disk_raw_device_uninit_device,
1575	NULL,
1576
1577	ram_disk_raw_device_open,
1578	ram_disk_raw_device_close,
1579	ram_disk_raw_device_free,
1580
1581	ram_disk_raw_device_read,
1582	ram_disk_raw_device_write,
1583	ram_disk_raw_device_io,
1584
1585	ram_disk_raw_device_control,
1586
1587	NULL,	// select
1588	NULL	// deselect
1589};
1590
1591const module_info* modules[] = {
1592	(module_info*)&sChecksumDeviceDriverModule,
1593	(module_info*)&sChecksumControlDeviceModule,
1594	(module_info*)&sChecksumRawDeviceModule,
1595	NULL
1596};
1597