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