1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <assert.h> 6#include <fcntl.h> 7#include <inttypes.h> 8#include <stdatomic.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <sys/param.h> 13#include <threads.h> 14 15#include <ddk/binding.h> 16#include <ddk/debug.h> 17#include <ddk/device.h> 18#include <ddk/driver.h> 19#include <ddk/protocol/block.h> 20 21#include <gpt/gpt.h> 22#include <lib/sync/completion.h> 23#include <zircon/device/block.h> 24#include <zircon/threads.h> 25 26#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) 27#define MBR_SIZE 512 28#define MBR_PARTITION_ENTRY_SIZE 16 29#define MBR_NUM_PARTITIONS 4 30#define MBR_BOOT_SIGNATURE 0xAA55 31 32// ATTN: MBR supports 8 bit partition types instead of GUIDs. Here we define 33// mappings between partition type and GUIDs that zircon understands. When 34// the MBR driver receives a request for the type GUID, we lie and return the 35// a mapping from partition type to type GUID. 36static const uint8_t data_guid[GPT_GUID_LEN] = GUID_DATA_VALUE; 37static const uint8_t sys_guid[GPT_GUID_LEN] = GUID_SYSTEM_VALUE; 38#define PARTITION_TYPE_NONE 0x00 39#define PARTITION_TYPE_DATA 0xE9 40#define PARTITION_TYPE_SYS 0xEA 41 42typedef struct __PACKED mbr_partition_entry { 43 uint8_t status; 44 uint8_t chs_addr_start[3]; 45 uint8_t type; 46 uint8_t chs_addr_end[3]; 47 uint32_t start_sector_lba; 48 uint32_t sector_partition_length; 49} mbr_partition_entry_t; 50 51typedef struct __PACKED mbr { 52 uint8_t bootstrap_code[446]; 53 mbr_partition_entry_t partition[MBR_NUM_PARTITIONS]; 54 uint16_t boot_signature; 55} mbr_t; 56 57typedef struct mbrpart_device { 58 zx_device_t* zxdev; 59 zx_device_t* parent; 60 61 block_protocol_t bp; 62 63 mbr_partition_entry_t partition; 64 65 block_info_t info; 66 size_t block_op_size; 67 68 atomic_int writercount; 69} mbrpart_device_t; 70 71static zx_status_t mbr_flush(const mbrpart_device_t* dev); 72 73static zx_status_t mbr_ioctl(void* ctx, uint32_t op, const void* cmd, 74 size_t cmdlen, void* reply, size_t max, 75 size_t* out_actual) { 76 mbrpart_device_t* device = ctx; 77 switch (op) { 78 case IOCTL_BLOCK_GET_INFO: { 79 block_info_t* info = reply; 80 if (max < sizeof(*info)) 81 return ZX_ERR_BUFFER_TOO_SMALL; 82 memcpy(info, &device->info, sizeof(*info)); 83 *out_actual = sizeof(*info); 84 return ZX_OK; 85 } 86 case IOCTL_BLOCK_GET_TYPE_GUID: { 87 char* guid = reply; 88 if (max < GPT_GUID_LEN) 89 return ZX_ERR_BUFFER_TOO_SMALL; 90 if (device->partition.type == PARTITION_TYPE_DATA) { 91 memcpy(guid, data_guid, GPT_GUID_LEN); 92 *out_actual = GPT_GUID_LEN; 93 return ZX_OK; 94 } else if (device->partition.type == PARTITION_TYPE_SYS) { 95 memcpy(guid, sys_guid, GPT_GUID_LEN); 96 *out_actual = GPT_GUID_LEN; 97 return ZX_OK; 98 } else { 99 return ZX_ERR_NOT_FOUND; 100 } 101 } 102 case IOCTL_BLOCK_GET_PARTITION_GUID: { 103 return ZX_ERR_NOT_SUPPORTED; 104 } 105 case IOCTL_BLOCK_GET_NAME: { 106 char* name = reply; 107 memset(name, 0, max); 108 strncpy(name, device_get_name(device->zxdev), max); 109 *out_actual = strnlen(name, max); 110 return ZX_OK; 111 } 112 case IOCTL_DEVICE_SYNC: { 113 return mbr_flush(device); 114 } 115 default: 116 return ZX_ERR_NOT_SUPPORTED; 117 } 118} 119 120static zx_off_t to_parent_offset(mbrpart_device_t* dev, zx_off_t offset) { 121 return offset + (uint64_t)(dev->partition.start_sector_lba) * 122 (uint64_t)dev->info.block_size; 123} 124 125 126static void mbr_query(void* ctx, block_info_t* bi, size_t* bopsz) { 127 mbrpart_device_t* mbr = ctx; 128 memcpy(bi, &mbr->info, sizeof(block_info_t)); 129 *bopsz = mbr->block_op_size; 130} 131 132static void mbr_queue(void* ctx, block_op_t* bop) { 133 mbrpart_device_t* mbr = ctx; 134 135 switch (bop->command & BLOCK_OP_MASK) { 136 case BLOCK_OP_READ: 137 case BLOCK_OP_WRITE: { 138 size_t blocks = bop->rw.length; 139 size_t max = mbr->partition.sector_partition_length; 140 141 // Ensure that the request is in-bounds 142 if ((bop->rw.offset_dev >= max) || 143 ((max - bop->rw.offset_dev) < blocks)) { 144 bop->completion_cb(bop, ZX_ERR_INVALID_ARGS); 145 return; 146 } 147 148 // Adjust for partition starting block 149 bop->rw.offset_dev += mbr->partition.start_sector_lba; 150 break; 151 } 152 case BLOCK_OP_FLUSH: 153 break; 154 default: 155 bop->completion_cb(bop, ZX_ERR_NOT_SUPPORTED); 156 return; 157 } 158 159 mbr->bp.ops->queue(mbr->bp.ctx, bop); 160} 161 162static void mbr_unbind(void* ctx) { 163 mbrpart_device_t* device = ctx; 164 device_remove(device->zxdev); 165} 166 167static void mbr_release(void* ctx) { 168 mbrpart_device_t* device = ctx; 169 free(device); 170} 171 172static zx_off_t mbr_get_size(void* ctx) { 173 mbrpart_device_t* dev = ctx; 174 //TODO: use query() results, *but* fvm returns different query and getsize 175 // results, and the latter are dynamic... 176 return device_get_size(dev->parent); 177} 178 179static zx_protocol_device_t mbr_proto = { 180 .version = DEVICE_OPS_VERSION, 181 .ioctl = mbr_ioctl, 182 .get_size = mbr_get_size, 183 .unbind = mbr_unbind, 184 .release = mbr_release, 185}; 186 187static block_protocol_ops_t block_ops = { 188 .query = mbr_query, 189 .queue = mbr_queue, 190}; 191 192static void mbr_sync_complete(block_op_t* bop, zx_status_t status) { 193 bop->command = status; 194 sync_completion_signal((sync_completion_t*)bop->cookie); 195} 196 197static zx_status_t mbr_flush(const mbrpart_device_t* dev) { 198 block_op_t* bop = calloc(1, dev->block_op_size); 199 if (bop == NULL) { 200 return ZX_ERR_NO_MEMORY; 201 } 202 203 sync_completion_t cplt = SYNC_COMPLETION_INIT; 204 205 bop->command = BLOCK_OP_FLUSH; 206 bop->completion_cb = mbr_sync_complete; 207 bop->cookie = &cplt; 208 209 dev->bp.ops->queue(dev->bp.ctx, bop); 210 sync_completion_wait(&cplt, ZX_TIME_INFINITE); 211 zx_status_t status = bop->command; 212 free(bop); 213 return status; 214} 215 216static zx_status_t vmo_read(zx_handle_t vmo, void* data, uint64_t off, size_t len) { 217 return zx_vmo_read(vmo, data, off, len); 218} 219 220static int mbr_bind_thread(void* arg) { 221 mbrpart_device_t* first_dev = (mbrpart_device_t*)arg; 222 zx_device_t* dev = first_dev->parent; 223 224 // Classic MBR supports 4 partitions. 225 uint8_t partition_count = 0; 226 227 block_protocol_t bp; 228 memcpy(&bp, &first_dev->bp, sizeof(bp)); 229 230 block_info_t block_info; 231 size_t block_op_size; 232 bp.ops->query(bp.ctx, &block_info, &block_op_size); 233 234 zx_handle_t vmo = ZX_HANDLE_INVALID; 235 block_op_t* bop = calloc(1, block_op_size); 236 if (bop == NULL) { 237 goto unbind; 238 } 239 240 // We need to read at least 512B to parse the MBR. Determine if we should 241 // read the device's block size or we should ready exactly 512B. 242 size_t iosize = 0; 243 if (block_info.block_size >= MBR_SIZE) { 244 iosize = block_info.block_size; 245 } else { 246 // Make sure we're reading some multiple of the block size. 247 iosize = DIV_ROUND_UP(MBR_SIZE, block_info.block_size) * block_info.block_size; 248 } 249 250 if (zx_vmo_create(iosize, 0, &vmo) != ZX_OK) { 251 zxlogf(ERROR, "mbr: cannot allocate vmo\n"); 252 goto unbind; 253 } 254 255 256 sync_completion_t cplt = SYNC_COMPLETION_INIT; 257 258 bop->command = BLOCK_OP_READ; 259 bop->rw.vmo = vmo; 260 bop->rw.length = iosize / block_info.block_size; 261 bop->rw.offset_dev = 0; 262 bop->rw.offset_vmo = 0; 263 bop->rw.pages = NULL; 264 bop->completion_cb = mbr_sync_complete; 265 bop->cookie = &cplt; 266 267 bp.ops->queue(bp.ctx, bop); 268 sync_completion_wait(&cplt, ZX_TIME_INFINITE); 269 270 if (bop->command != ZX_OK) { 271 zxlogf(ERROR, "mbr: could not read mbr from device, retcode = %d\n", bop->command); 272 goto unbind; 273 } 274 275 uint8_t buffer[MBR_SIZE]; 276 mbr_t* mbr = (mbr_t*)buffer; 277 if (vmo_read(vmo, buffer, 0, MBR_SIZE) != ZX_OK) { 278 goto unbind; 279 } 280 281 // Validate the MBR boot signature. 282 if (mbr->boot_signature != MBR_BOOT_SIGNATURE) { 283 zxlogf(ERROR, "mbr: invalid mbr boot signature, expected 0x%04x got 0x%04x\n", 284 MBR_BOOT_SIGNATURE, mbr->boot_signature); 285 goto unbind; 286 } 287 288 // Parse the partitions out of the MBR. 289 for (; partition_count < MBR_NUM_PARTITIONS; partition_count++) { 290 mbr_partition_entry_t* entry = &mbr->partition[partition_count]; 291 if (entry->type == PARTITION_TYPE_NONE) { 292 // This partition entry is empty and does not refer to a partition, 293 // skip it. 294 continue; 295 } 296 297 zxlogf(SPEW, "mbr: found partition, entry = %d, type = 0x%02x, " 298 "start = %u, length = %u\n", 299 partition_count + 1, entry->type, entry->start_sector_lba, 300 entry->sector_partition_length); 301 302 mbrpart_device_t* pdev; 303 // use first_dev for first partition 304 if (first_dev) { 305 pdev = first_dev; 306 } else { 307 pdev = calloc(1, sizeof(*pdev)); 308 if (!pdev) { 309 zxlogf(ERROR, "mbr: out of memory\n"); 310 goto unbind; 311 } 312 pdev->parent = dev; 313 memcpy(&pdev->bp, &bp, sizeof(bp)); 314 } 315 316 memcpy(&pdev->partition, entry, sizeof(*entry)); 317 block_info.block_count = pdev->partition.sector_partition_length; 318 memcpy(&pdev->info, &block_info, sizeof(block_info)); 319 pdev->block_op_size = block_op_size; 320 321 if (first_dev) { 322 // make our initial device visible and use if for partition zero 323 device_make_visible(first_dev->zxdev); 324 first_dev = NULL; 325 } else { 326 char name[16]; 327 snprintf(name, sizeof(name), "part-%03u",partition_count); 328 329 device_add_args_t args = { 330 .version = DEVICE_ADD_ARGS_VERSION, 331 .name = name, 332 .ctx = pdev, 333 .ops = &mbr_proto, 334 .proto_id = ZX_PROTOCOL_BLOCK_IMPL, 335 .proto_ops = &block_ops, 336 }; 337 338 if (device_add(dev, &args, &pdev->zxdev) != ZX_OK) { 339 free(pdev); 340 continue; 341 } 342 } 343 } 344 345 free(bop); 346 zx_handle_close(vmo); 347 return 0; 348unbind: 349 free(bop); 350 zx_handle_close(vmo); 351 if (first_dev) { 352 // handle case where no partitions were found 353 device_remove(first_dev->zxdev); 354 } 355 return -1; 356} 357 358static zx_status_t mbr_bind(void* ctx, zx_device_t* parent) { 359 // Make sure the MBR structs are the right size. 360 static_assert(sizeof(mbr_t) == MBR_SIZE, "mbr_t is the wrong size"); 361 static_assert(sizeof(mbr_partition_entry_t) == MBR_PARTITION_ENTRY_SIZE, 362 "mbr_partition_entry_t is the wrong size"); 363 364 // create an invisible device, which will be used for the first partition 365 mbrpart_device_t* device = calloc(1, sizeof(mbrpart_device_t)); 366 if (!device) { 367 return ZX_ERR_NO_MEMORY; 368 } 369 device->parent = parent; 370 371 if (device_get_protocol(parent, ZX_PROTOCOL_BLOCK, &device->bp) != ZX_OK) { 372 zxlogf(ERROR, "mbr: ERROR: block device '%s': does not support block protocol\n", 373 device_get_name(parent)); 374 free(device); 375 return ZX_ERR_NOT_SUPPORTED; 376 } 377 378 char name[128]; 379 snprintf(name, sizeof(name), "part-%03u", 0); 380 381 device_add_args_t args = { 382 .version = DEVICE_ADD_ARGS_VERSION, 383 .name = name, 384 .ctx = device, 385 .ops = &mbr_proto, 386 .proto_id = ZX_PROTOCOL_BLOCK_IMPL, 387 .proto_ops = &block_ops, 388 .flags = DEVICE_ADD_INVISIBLE, 389 }; 390 391 zx_status_t status = device_add(parent, &args, &device->zxdev); 392 if (status != ZX_OK) { 393 free(device); 394 return status; 395 } 396 397 // Read the partition table asyncrhonously. 398 thrd_t t; 399 int thrd_rc = thrd_create_with_name(&t, mbr_bind_thread, device, "mbr-init"); 400 if (thrd_rc != thrd_success) { 401 return thrd_status_to_zx_status(thrd_rc); 402 } 403 return ZX_OK; 404} 405 406static zx_driver_ops_t mbr_driver_ops = { 407 .version = DRIVER_OPS_VERSION, 408 .bind = mbr_bind, 409}; 410 411// clang-format off 412ZIRCON_DRIVER_BEGIN(mbr, mbr_driver_ops, "zircon", "0.1", 2) 413 BI_ABORT_IF_AUTOBIND, 414 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_BLOCK), 415ZIRCON_DRIVER_END(mbr) 416// clang-format on 417