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