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);
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)41 virtual ssize_t WriteAt(void* cookie, off_t pos, const void* buffer,
42 size_t bufferSize) { return B_UNSUPPORTED; }
Size() const43 virtual off_t Size() const {
44 return (*(uint32*)(&fBlockIo->Regs()->config[0])
45 + ((uint64)(*(uint32*)(&fBlockIo->Regs()->config[4])) << 32)
46 )*kVirtioBlockSectorSize;
47 }
48
BlockSize() const49 uint32 BlockSize() const { return kVirtioBlockSectorSize; }
ReadOnly() const50 bool ReadOnly() const { return false; }
51 private:
52 ObjectDeleter<VirtioDevice> fBlockIo;
53 };
54
55
VirtioBlockDevice(VirtioDevice * blockIo)56 VirtioBlockDevice::VirtioBlockDevice(VirtioDevice* blockIo)
57 :
58 fBlockIo(blockIo)
59 {
60 dprintf("+VirtioBlockDevice\n");
61 }
62
63
~VirtioBlockDevice()64 VirtioBlockDevice::~VirtioBlockDevice()
65 {
66 dprintf("-VirtioBlockDevice\n");
67 }
68
69
70 ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)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*
CreateVirtioBlockDev(int id)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
get_next_check_sum_offset(int32 index,off_t maxSize)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
compute_check_sum(Node * device,off_t offset)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
platform_add_boot_device(struct stage2_args * args,NodeList * devicesList)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
platform_add_block_devices(struct stage2_args * args,NodeList * devicesList)178 platform_add_block_devices(struct stage2_args* args, NodeList* devicesList)
179 {
180 return B_ENTRY_NOT_FOUND;
181 }
182
183
184 status_t
platform_get_boot_partitions(struct stage2_args * args,Node * bootDevice,NodeList * list,NodeList * partitionList)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
platform_register_boot_device(Node * device)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
platform_cleanup_devices()227 platform_cleanup_devices()
228 {
229 }
230