blob: ddda5e15de468a609e43ec7ec6c248d7728d9b3c [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2009 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <err.h>
25#include <trace.h>
26#include <stdlib.h>
27#include <lib/bio.h>
28
29#define LOCAL_TRACE 0
30
31typedef struct {
32 // inheirit the usual bits
33 bdev_t dev;
34
35 // we're a subdevice of this
36 bdev_t *parent;
37
38 // Storage for our erase geometry info. Subdevices are only permitted to
39 // have homogeneous erase geometry.
40 bio_erase_geometry_info_t geometry;
41
42 // we're this many blocks into it
43 bnum_t offset;
44} subdev_t;
45
46static ssize_t subdev_read(struct bdev *_dev, void *buf, off_t offset, size_t len)
47{
48 subdev_t *subdev = (subdev_t *)_dev;
49
50 return bio_read(subdev->parent, buf, offset + ((off_t)subdev->offset * subdev->dev.block_size), len);
51}
52
53static ssize_t subdev_read_block(struct bdev *_dev, void *buf, bnum_t block, uint count)
54{
55 subdev_t *subdev = (subdev_t *)_dev;
56
57 return bio_read_block(subdev->parent, buf, block + subdev->offset, count);
58}
59
60static ssize_t subdev_write(struct bdev *_dev, const void *buf, off_t offset, size_t len)
61{
62 subdev_t *subdev = (subdev_t *)_dev;
63
64 return bio_write(subdev->parent, buf, offset + ((off_t)subdev->offset * subdev->dev.block_size), len);
65}
66
67static ssize_t subdev_write_block(struct bdev *_dev, const void *buf, bnum_t block, uint count)
68{
69 subdev_t *subdev = (subdev_t *)_dev;
70
71 return bio_write_block(subdev->parent, buf, block + subdev->offset, count);
72}
73
74static ssize_t subdev_erase(struct bdev *_dev, off_t offset, size_t len)
75{
76 subdev_t *subdev = (subdev_t *)_dev;
77
78 return bio_erase(subdev->parent, offset + ((off_t)subdev->offset * subdev->dev.block_size), len);
79}
80
81static void subdev_close(struct bdev *_dev)
82{
83 subdev_t *subdev = (subdev_t *)_dev;
84
85 bio_close(subdev->parent);
86 subdev->parent = NULL;
87}
88
89static int subdev_ioctl(struct bdev *_dev, int request, void *argp)
90{
91 subdev_t *subdev = (subdev_t *)_dev;
92
93 return bio_ioctl(subdev->parent, request, argp);
94}
95
96#define BAIL(__err) do { err = __err; goto bailout; } while (0)
97status_t bio_publish_subdevice(const char *parent_dev,
98 const char *subdev,
99 bnum_t startblock,
100 bnum_t block_count)
101{
102 status_t err = NO_ERROR;
103 bdev_t *parent = NULL;
104 subdev_t *sub = NULL;
105 size_t geometry_count;
106 bio_erase_geometry_info_t* geometry;
107
108 LTRACEF("parent \"%s\", sub \"%s\", startblock %u, count %u\n", parent_dev, subdev, startblock, block_count);
109
110 // Make sure our parent exists
111 parent = bio_open(parent_dev);
112 if (!parent) {
113 LTRACEF("Failed to find parent \"%s\"\n", parent_dev);
114 BAIL(ERR_NOT_FOUND);
115 }
116
117 // Allocate our sub-device.
118 sub = malloc(sizeof(subdev_t));
119 if (!sub) {
120 LTRACEF("Failed to allocate subdevice\n");
121 BAIL(ERR_NO_MEMORY);
122 }
123
124 /* Make sure we're able to do this. If the device has a specified erase
125 * geometry, the specified sub-region must exist entirely with one of our
126 * parent's erase regions, and be aligned to an erase unit boundary.
127 * Otherwise, the specified region must simply exist within the block range
128 * of the device.
129 */
130 if (parent->geometry_count && parent->geometry) {
131 uint64_t byte_start = ((uint64_t)startblock << parent->block_shift);
132 uint64_t byte_size = ((uint64_t)block_count << parent->block_shift);
133 const bio_erase_geometry_info_t* geo = NULL;
134
135 LTRACEF("Searching geometry for region which contains @[0x%llx, 0x%llx)\n",
136 byte_start, byte_start + byte_size);
137
138 // Start by finding the erase region which completely contains the requested range.
139 for (size_t i = 0; i < parent->geometry_count; ++i) {
140 geo = parent->geometry + i;
141
142 LTRACEF("Checking geometry @[0x%llx, 0x%llx) erase size 0x%zx\n",
143 geo->start,
144 geo->start + geo->size,
145 geo->erase_size);
146
147 if (bio_contains_range(geo->start, geo->size, byte_start, byte_size))
148 break;
149 }
150
151 if (!geo) {
152 LTRACEF("No suitable erase region found\n");
153 BAIL(ERR_INVALID_ARGS);
154 }
155
156 // Now check out alignment.
157 uint64_t erase_mask = ((uint64_t)0x1 << geo->erase_shift) - 1;
158 if ((byte_start & erase_mask) || (byte_size & erase_mask)) {
159 LTRACEF("Requested region has improper alignment/length\n");
160 BAIL(ERR_INVALID_ARGS);
161 }
162
163 geometry_count = 1;
164 geometry = &sub->geometry;
165
166 geometry->start = 0;
167 geometry->size = byte_size;
168 geometry->erase_size = geo->erase_size;
169 geometry->erase_shift = geo->erase_shift;
170 } else {
171 bnum_t endblock = startblock + block_count;
172
173 if ((endblock < startblock) || (endblock > parent->block_count))
174 BAIL(ERR_INVALID_ARGS);
175
176 geometry_count = 0;
177 geometry = NULL;
178 }
179
180 bio_initialize_bdev(&sub->dev, subdev,
181 parent->block_size, block_count,
182 geometry_count, geometry, BIO_FLAGS_NONE);
183
184 sub->parent = parent;
185 sub->dev.erase_byte = parent->erase_byte;
186 sub->offset = startblock;
187
188 sub->dev.read = &subdev_read;
189 sub->dev.read_block = &subdev_read_block;
190 sub->dev.write = &subdev_write;
191 sub->dev.write_block = &subdev_write_block;
192 sub->dev.erase = &subdev_erase;
193 sub->dev.close = &subdev_close;
194 sub->dev.ioctl = &subdev_ioctl;
195
196 bio_register_device(&sub->dev);
197
198bailout:
199 if (err < 0) {
200 if (NULL != parent)
201 bio_close(parent);
202 free(sub);
203 }
204
205 return err;
206}
207#undef BAIL