source: mainline/uspace/srv/part/mbr_part/mbr_part.c@ 3f35634c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3f35634c was 0da4e41, checked in by Jakub Jermar <jakub@…>, 16 years ago

ipc_data_*() and ipc_share_*(), respectively, should be renamed to
async_data_*() and async_share_*(), respectively, because these functions are
using the async framework.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bd
30 * @{
31 */
32
33/**
34 * @file
35 * @brief PC MBR partition driver
36 *
37 * Handles the PC MBR partitioning scheme. Uses a block device
38 * and provides one for each partition.
39 *
40 * Limitations:
41 *
42 * Only works with boot records using LBA. CHS-only records are not
43 * supported.
44 *
45 * Referemces:
46 *
47 * The source of MBR structures for this driver have been the following
48 * Wikipedia articles:
49 * - http://en.wikipedia.org/wiki/Master_Boot_Record
50 * - http://en.wikipedia.org/wiki/Extended_boot_record
51 *
52 * The fact that the extended partition has type 0x05 is pure observation.
53 * (TODO: can it have any other type number?)
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <unistd.h>
59#include <ipc/ipc.h>
60#include <ipc/bd.h>
61#include <async.h>
62#include <as.h>
63#include <fibril_sync.h>
64#include <devmap.h>
65#include <sys/types.h>
66#include <libblock.h>
67#include <devmap.h>
68#include <errno.h>
69#include <bool.h>
70#include <byteorder.h>
71#include <assert.h>
72#include <macros.h>
73#include <task.h>
74
75#define NAME "mbr_part"
76
77enum {
78 /** Number of primary partition records */
79 N_PRIMARY = 4,
80
81 /** Boot record signature */
82 BR_SIGNATURE = 0xAA55
83};
84
85enum ptype {
86 /** Unused partition entry */
87 PT_UNUSED = 0x00,
88 /** Extended partition */
89 PT_EXTENDED = 0x05,
90};
91
92/** Partition */
93typedef struct part {
94 /** Primary partition entry is in use */
95 bool present;
96 /** Address of first block */
97 bn_t start_addr;
98 /** Number of blocks */
99 bn_t length;
100 /** Device representing the partition (outbound device) */
101 dev_handle_t dev;
102 /** Points to next partition structure. */
103 struct part *next;
104} part_t;
105
106/** Structure of a partition table entry */
107typedef struct {
108 uint8_t status;
109 /** CHS of fist block in partition */
110 uint8_t first_chs[3];
111 /** Partition type */
112 uint8_t ptype;
113 /** CHS of last block in partition */
114 uint8_t last_chs[3];
115 /** LBA of first block in partition */
116 uint32_t first_lba;
117 /** Number of blocks in partition */
118 uint32_t length;
119} __attribute__((packed)) pt_entry_t;
120
121/** Structure of a boot-record block */
122typedef struct {
123 /* Area for boot code */
124 uint8_t code_area[440];
125
126 /* Optional media ID */
127 uint32_t media_id;
128
129 uint16_t pad0;
130
131 /** Partition table entries */
132 pt_entry_t pte[N_PRIMARY];
133
134 /** Boot record block signature (@c BR_SIGNATURE) */
135 uint16_t signature;
136} __attribute__((packed)) br_block_t;
137
138
139static size_t block_size;
140
141/** Partitioned device (inbound device) */
142static dev_handle_t indev_handle;
143
144/** List of partitions. This structure is an empty head. */
145static part_t plist_head;
146
147static int mbr_init(const char *dev_name);
148static int mbr_part_read(void);
149static part_t *mbr_part_new(void);
150static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part);
151static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall);
152static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf);
153static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf);
154static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
155
156int main(int argc, char **argv)
157{
158 printf(NAME ": PC MBR partition driver\n");
159
160 if (argc != 2) {
161 printf("Expected one argument (device name).\n");
162 return -1;
163 }
164
165 if (mbr_init(argv[1]) != EOK)
166 return -1;
167
168 printf(NAME ": Accepting connections\n");
169 task_retval(0);
170 async_manager();
171
172 /* Not reached */
173 return 0;
174}
175
176static int mbr_init(const char *dev_name)
177{
178 int rc;
179 int i;
180 char *name;
181 dev_handle_t dev;
182 uint64_t size_mb;
183 part_t *part;
184
185 rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
186 if (rc != EOK) {
187 printf(NAME ": could not resolve device `%s'.\n", dev_name);
188 return rc;
189 }
190
191 rc = block_init(indev_handle, 2048);
192 if (rc != EOK) {
193 printf(NAME ": could not init libblock.\n");
194 return rc;
195 }
196
197 /* Determine and verify block size. */
198
199 rc = block_get_bsize(indev_handle, &block_size);
200 if (rc != EOK) {
201 printf(NAME ": error getting block size.\n");
202 return rc;
203 }
204
205 if (block_size < 512 || (block_size % 512) != 0) {
206 printf(NAME ": invalid block size %d.\n");
207 return ENOTSUP;
208 }
209
210 /* Read in partition records. */
211 rc = mbr_part_read();
212 if (rc != EOK)
213 return rc;
214
215 /* Register the driver with device mapper. */
216 rc = devmap_driver_register(NAME, mbr_connection);
217 if (rc != EOK) {
218 printf(NAME ": Unable to register driver.\n");
219 return rc;
220 }
221
222 /*
223 * Create partition devices.
224 */
225 i = 0;
226 part = plist_head.next;
227
228 while (part != NULL) {
229 /* Skip absent partitions. */
230 if (!part->present) {
231 part = part->next;
232 ++i;
233 continue;
234 }
235
236 asprintf(&name, "%sp%d", dev_name, i);
237 if (name == NULL)
238 return ENOMEM;
239
240 rc = devmap_device_register(name, &dev);
241 if (rc != EOK) {
242 devmap_hangup_phone(DEVMAP_DRIVER);
243 printf(NAME ": Unable to register device %s.\n", name);
244 return rc;
245 }
246
247 size_mb = (part->length * block_size + 1024 * 1024 - 1)
248 / (1024 * 1024);
249 printf(NAME ": Registered device %s: %llu blocks %llu MB.\n",
250 name, part->length, size_mb);
251
252 part->dev = dev;
253 free(name);
254
255 part = part->next;
256 ++i;
257 }
258
259 return EOK;
260}
261
262/** Read in partition records. */
263static int mbr_part_read(void)
264{
265 int i, rc;
266 br_block_t *brb;
267 uint16_t sgn;
268 uint32_t ba;
269 part_t *ext_part, cp;
270 uint32_t base;
271 part_t *prev, *p;
272
273 brb = malloc(sizeof(br_block_t));
274 if (brb == NULL) {
275 printf(NAME ": Failed allocating memory.\n");
276 return ENOMEM;
277 }
278
279 /*
280 * Read primary partition entries.
281 */
282
283 rc = block_read_direct(indev_handle, 0, 1, brb);
284 if (rc != EOK) {
285 printf(NAME ": Failed reading MBR block.\n");
286 return rc;
287 }
288
289 sgn = uint16_t_le2host(brb->signature);
290 if (sgn != BR_SIGNATURE) {
291 printf(NAME ": Invalid boot record signature 0x%04X.\n", sgn);
292 return EINVAL;
293 }
294
295 ext_part = NULL;
296 plist_head.next = NULL;
297 prev = &plist_head;
298
299 for (i = 0; i < N_PRIMARY; ++i) {
300 p = mbr_part_new();
301 if (p == NULL)
302 return ENOMEM;
303
304 mbr_pte_to_part(0, &brb->pte[i], p);
305 prev->next = p;
306 prev = p;
307
308 if (brb->pte[i].ptype == PT_EXTENDED) {
309 p->present = false;
310 ext_part = p;
311 }
312 }
313
314 if (ext_part == NULL)
315 return EOK;
316
317 printf("Extended partition found.\n");
318
319 /*
320 * Read extended partition entries.
321 */
322
323 cp.start_addr = ext_part->start_addr;
324 cp.length = ext_part->length;
325 base = ext_part->start_addr;
326
327 do {
328 /*
329 * Addressing in the EBR chain is relative to the beginning
330 * of the extended partition.
331 */
332 ba = cp.start_addr;
333 rc = block_read_direct(indev_handle, ba, 1, brb);
334 if (rc != EOK) {
335 printf(NAME ": Failed reading EBR block at %u.\n", ba);
336 return rc;
337 }
338
339 sgn = uint16_t_le2host(brb->signature);
340 if (sgn != BR_SIGNATURE) {
341 printf(NAME ": Invalid boot record signature 0x%04X "
342 " in EBR at %u.\n", sgn, ba);
343 return EINVAL;
344 }
345
346 p = mbr_part_new();
347 if (p == NULL)
348 return ENOMEM;
349
350 /* First PTE is the logical partition itself. */
351 mbr_pte_to_part(base, &brb->pte[0], p);
352 prev->next = p;
353 prev = p;
354
355 /* Second PTE describes next chain element. */
356 mbr_pte_to_part(base, &brb->pte[1], &cp);
357 } while (cp.present);
358
359 return EOK;
360}
361
362/** Allocate a new @c part_t structure. */
363static part_t *mbr_part_new(void)
364{
365 return malloc(sizeof(part_t));
366}
367
368/** Parse partition table entry. */
369static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part)
370{
371 uint32_t sa, len;
372
373 sa = uint32_t_le2host(pte->first_lba);
374 len = uint32_t_le2host(pte->length);
375
376 part->start_addr = base + sa;
377 part->length = len;
378
379 part->present = (pte->ptype != PT_UNUSED) ? true : false;
380
381 part->dev = 0;
382 part->next = NULL;
383}
384
385static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall)
386{
387 size_t comm_size;
388 void *fs_va = NULL;
389 ipc_callid_t callid;
390 ipc_call_t call;
391 ipcarg_t method;
392 dev_handle_t dh;
393 int flags;
394 int retval;
395 uint64_t ba;
396 size_t cnt;
397 part_t *part;
398
399 /* Get the device handle. */
400 dh = IPC_GET_ARG1(*icall);
401
402 /*
403 * Determine which partition device is the client connecting to.
404 * A linear search is not terribly fast, but we only do this
405 * once for each connection.
406 */
407 part = plist_head.next;
408 while (part != NULL && part->dev != dh)
409 part = part->next;
410
411 if (part == NULL) {
412 ipc_answer_0(iid, EINVAL);
413 return;
414 }
415
416 assert(part->present == true);
417
418 /* Answer the IPC_M_CONNECT_ME_TO call. */
419 ipc_answer_0(iid, EOK);
420
421 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
422 ipc_answer_0(callid, EHANGUP);
423 return;
424 }
425
426 fs_va = as_get_mappable_page(comm_size);
427 if (fs_va == NULL) {
428 ipc_answer_0(callid, EHANGUP);
429 return;
430 }
431
432 (void) async_share_out_finalize(callid, fs_va);
433
434 while (1) {
435 callid = async_get_call(&call);
436 method = IPC_GET_METHOD(call);
437 switch (method) {
438 case IPC_M_PHONE_HUNGUP:
439 /* The other side has hung up. */
440 ipc_answer_0(callid, EOK);
441 return;
442 case BD_READ_BLOCKS:
443 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
444 IPC_GET_ARG2(call));
445 cnt = IPC_GET_ARG3(call);
446 if (cnt * block_size > comm_size) {
447 retval = ELIMIT;
448 break;
449 }
450 retval = mbr_bd_read(part, ba, cnt, fs_va);
451 break;
452 case BD_WRITE_BLOCKS:
453 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
454 IPC_GET_ARG2(call));
455 cnt = IPC_GET_ARG3(call);
456 if (cnt * block_size > comm_size) {
457 retval = ELIMIT;
458 break;
459 }
460 retval = mbr_bd_write(part, ba, cnt, fs_va);
461 break;
462 case BD_GET_BLOCK_SIZE:
463 ipc_answer_1(callid, EOK, block_size);
464 continue;
465
466 default:
467 retval = EINVAL;
468 break;
469 }
470 ipc_answer_0(callid, retval);
471 }
472}
473
474/** Read blocks from partition. */
475static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
476{
477 uint64_t gba;
478
479 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
480 return ELIMIT;
481
482 return block_read_direct(indev_handle, gba, cnt, buf);
483}
484
485/** Write blocks to partition. */
486static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
487{
488 uint64_t gba;
489
490 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
491 return ELIMIT;
492
493 return block_write_direct(indev_handle, gba, cnt, buf);
494}
495
496/** Translate block segment address with range checking. */
497static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
498{
499 if (ba + cnt > p->length)
500 return ELIMIT;
501
502 *gba = p->start_addr + ba;
503 return EOK;
504}
505
506/**
507 * @}
508 */
Note: See TracBrowser for help on using the repository browser.