status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, size_t len) { LTRACEF("parent %s, sub %s, startblock %u, len %zd\n", parent_dev, subdev, startblock, len); bdev_t *parent = bio_open(parent_dev); if (!parent) return -1; /* make sure we're able to do this */ if (startblock + len > parent->block_count) return -1; subdev_t *sub = malloc(sizeof(subdev_t)); bio_initialize_bdev(&sub->dev, subdev, parent->block_size, len); sub->parent = parent; sub->offset = startblock; sub->dev.read = &subdev_read; sub->dev.read_block = &subdev_read_block; sub->dev.write = &subdev_write; sub->dev.write_block = &subdev_write_block; sub->dev.erase = &subdev_erase; sub->dev.close = &subdev_close; bio_register_device(&sub->dev); return 0; }
void stm32_flash_init(void) { // XXX detect here flash.size = 1024*1024; flash.geometry[0].start = ERASE_RANGE0_START; flash.geometry[0].size = ERASE_RANGE0_END - ERASE_RANGE0_START; flash.geometry[0].erase_size = _32K; flash.geometry[0].erase_size = log2_uint(_32K); flash.geometry[1].start = ERASE_RANGE1_START; flash.geometry[1].size = ERASE_RANGE1_END - ERASE_RANGE1_START; flash.geometry[1].erase_size = _128K; flash.geometry[1].erase_size = log2_uint(_128K); flash.geometry[2].start = ERASE_RANGE2_START; flash.geometry[2].size = ERASE_RANGE2_END - ERASE_RANGE2_START; flash.geometry[2].erase_size = _256K; flash.geometry[2].erase_size = log2_uint(_256K); /* construct the block device */ bio_initialize_bdev(&flash.bdev, "flash0", PROGRAM_SIZE, flash.size / PROGRAM_SIZE, 3, flash.geometry); /* override our block device hooks */ flash.bdev.read = &stm32_flash_bdev_read; flash.bdev.read_block = &stm32_flash_bdev_read_block; //flash.bdev.write = &stm32_flash_bdev_write; flash.bdev.write_block = &stm32_flash_bdev_write_block; flash.bdev.erase = &stm32_flash_bdev_erase; flash.bdev.ioctl = &stm32_flash_ioctl; bio_register_device(&flash.bdev); }
bdev_t* create_membdev(const char *name, void *ptr, size_t len, bool publish) { mem_bdev_t *mem = malloc(sizeof(mem_bdev_t)); /* set up the base device */ bio_initialize_bdev(&mem->dev, name, BLOCKSIZE, len / BLOCKSIZE); /* our bits */ mem->ptr = ptr; mem->dev.read = mem_bdev_read; mem->dev.read_block = mem_bdev_read_block; mem->dev.write = mem_bdev_write; mem->dev.write_block = mem_bdev_write_block; /* register it */ if(publish) bio_register_device(&mem->dev); else mem->dev.ref = 1; return &mem->dev; }
status_t virtio_block_init(struct virtio_device *dev, uint32_t host_features) { LTRACEF("dev %p, host_features 0x%x\n", dev, host_features); /* allocate a new block device */ struct virtio_block_dev *bdev = malloc(sizeof(struct virtio_block_dev)); if (!bdev) return ERR_NO_MEMORY; mutex_init(&bdev->lock); event_init(&bdev->io_event, false, EVENT_FLAG_AUTOUNSIGNAL); bdev->dev = dev; dev->priv = bdev; bdev->blk_req = memalign(sizeof(struct virtio_blk_req), sizeof(struct virtio_blk_req)); #if WITH_KERNEL_VM arch_mmu_query((vaddr_t)bdev->blk_req, &bdev->blk_req_phys, NULL); #else bdev->blk_freq_phys = (uint64_t)(uintptr_t)bdev->blk_req; #endif LTRACEF("blk_req structure at %p (0x%lx phys)\n", bdev->blk_req, bdev->blk_req_phys); #if WITH_KERNEL_VM arch_mmu_query((vaddr_t)&bdev->blk_response, &bdev->blk_response_phys, NULL); #else bdev->blk_response_phys = (uint64_t)(uintptr_t)&bdev->blk_response; #endif /* make sure the device is reset */ virtio_reset_device(dev); volatile struct virtio_blk_config *config = (struct virtio_blk_config *)dev->config_ptr; LTRACEF("capacity 0x%llx\n", config->capacity); LTRACEF("size_max 0x%x\n", config->size_max); LTRACEF("seg_max 0x%x\n", config->seg_max); LTRACEF("blk_size 0x%x\n", config->blk_size); /* ack and set the driver status bit */ virtio_status_acknowledge_driver(dev); // XXX check features bits and ack/nak them /* allocate a virtio ring */ virtio_alloc_ring(dev, 0, 256); /* set our irq handler */ dev->irq_driver_callback = &virtio_block_irq_driver_callback; /* set DRIVER_OK */ virtio_status_driver_ok(dev); /* construct the block device */ static uint8_t found_index = 0; char buf[16]; snprintf(buf, sizeof(buf), "virtio%u", found_index++); bio_initialize_bdev(&bdev->bdev, buf, config->blk_size, config->capacity, 0, NULL); /* override our block device hooks */ bdev->bdev.read_block = &virtio_bdev_read_block; bdev->bdev.write_block = &virtio_bdev_write_block; bio_register_device(&bdev->bdev); printf("found virtio block device of size %lld\n", config->capacity * config->blk_size); return NO_ERROR; }
status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, bnum_t block_count) { status_t err = NO_ERROR; bdev_t *parent = NULL; subdev_t *sub = NULL; size_t geometry_count; bio_erase_geometry_info_t* geometry; LTRACEF("parent \"%s\", sub \"%s\", startblock %u, count %u\n", parent_dev, subdev, startblock, block_count); // Make sure our parent exists parent = bio_open(parent_dev); if (!parent) { LTRACEF("Failed to find parent \"%s\"\n", parent_dev); BAIL(ERR_NOT_FOUND); } // Allocate our sub-device. sub = malloc(sizeof(subdev_t)); if (!sub) { LTRACEF("Failed to allocate subdevice\n"); BAIL(ERR_NO_MEMORY); } /* Make sure we're able to do this. If the device has a specified erase * geometry, the specified sub-region must exist entirely with one of our * parent's erase regions, and be aligned to an erase unit boundary. * Otherwise, the specified region must simply exist within the block range * of the device. */ if (parent->geometry_count && parent->geometry) { uint64_t byte_start = ((uint64_t)startblock << parent->block_shift); uint64_t byte_size = ((uint64_t)block_count << parent->block_shift); const bio_erase_geometry_info_t* geo = NULL; LTRACEF("Searching geometry for region which contains @[0x%llx, 0x%llx)\n", byte_start, byte_start + byte_size); // Start by finding the erase region which completely contains the requested range. for (size_t i = 0; i < parent->geometry_count; ++i) { geo = parent->geometry + i; LTRACEF("Checking geometry @[0x%llx, 0x%llx) erase size 0x%zx\n", geo->start, geo->start + geo->size, geo->erase_size); if (bio_contains_range(geo->start, geo->size, byte_start, byte_size)) break; } if (!geo) { LTRACEF("No suitable erase region found\n"); BAIL(ERR_INVALID_ARGS); } // Now check out alignment. uint64_t erase_mask = ((uint64_t)0x1 << geo->erase_shift) - 1; if ((byte_start & erase_mask) || (byte_size & erase_mask)) { LTRACEF("Requested region has improper alignment/length\n"); BAIL(ERR_INVALID_ARGS); } geometry_count = 1; geometry = &sub->geometry; geometry->start = 0; geometry->size = byte_size; geometry->erase_size = geo->erase_size; geometry->erase_shift = geo->erase_shift; } else { bnum_t endblock = startblock + block_count; if ((endblock < startblock) || (endblock > parent->block_count)) BAIL(ERR_INVALID_ARGS); geometry_count = 0; geometry = NULL; } bio_initialize_bdev(&sub->dev, subdev, parent->block_size, block_count, geometry_count, geometry); sub->parent = parent; sub->offset = startblock; sub->dev.read = &subdev_read; sub->dev.read_block = &subdev_read_block; sub->dev.write = &subdev_write; sub->dev.write_block = &subdev_write_block; sub->dev.erase = &subdev_erase; sub->dev.close = &subdev_close; bio_register_device(&sub->dev); bailout: if (err < 0) { if (NULL != parent) bio_close(parent); free(sub); } return err; }