source: mainline/uspace/srv/bd/part/mbr_part/mbr_part.c@ 9934f7d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9934f7d was 9934f7d, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Add extra argument to async connection handlers that can be used for passing
information from async_connect_to_me() to the handler.

  • Property mode set to 100644
File size: 11.6 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/bd.h>
60#include <async.h>
61#include <as.h>
62#include <fibril_synch.h>
63#include <devmap.h>
64#include <sys/types.h>
65#include <sys/typefmt.h>
66#include <inttypes.h>
67#include <libblock.h>
68#include <devmap.h>
69#include <errno.h>
70#include <bool.h>
71#include <byteorder.h>
72#include <assert.h>
73#include <macros.h>
74#include <task.h>
75
76#define NAME "mbr_part"
77
78enum {
79 /** Number of primary partition records */
80 N_PRIMARY = 4,
81
82 /** Boot record signature */
83 BR_SIGNATURE = 0xAA55
84};
85
86enum ptype {
87 /** Unused partition entry */
88 PT_UNUSED = 0x00,
89 /** Extended partition */
90 PT_EXTENDED = 0x05,
91};
92
93/** Partition */
94typedef struct part {
95 /** Primary partition entry is in use */
96 bool present;
97 /** Address of first block */
98 aoff64_t start_addr;
99 /** Number of blocks */
100 aoff64_t length;
101 /** Device representing the partition (outbound device) */
102 devmap_handle_t dev;
103 /** Points to next partition structure. */
104 struct part *next;
105} part_t;
106
107/** Structure of a partition table entry */
108typedef struct {
109 uint8_t status;
110 /** CHS of fist block in partition */
111 uint8_t first_chs[3];
112 /** Partition type */
113 uint8_t ptype;
114 /** CHS of last block in partition */
115 uint8_t last_chs[3];
116 /** LBA of first block in partition */
117 uint32_t first_lba;
118 /** Number of blocks in partition */
119 uint32_t length;
120} __attribute__((packed)) pt_entry_t;
121
122/** Structure of a boot-record block */
123typedef struct {
124 /* Area for boot code */
125 uint8_t code_area[440];
126
127 /* Optional media ID */
128 uint32_t media_id;
129
130 uint16_t pad0;
131
132 /** Partition table entries */
133 pt_entry_t pte[N_PRIMARY];
134
135 /** Boot record block signature (@c BR_SIGNATURE) */
136 uint16_t signature;
137} __attribute__((packed)) br_block_t;
138
139
140static size_t block_size;
141
142/** Partitioned device (inbound device) */
143static devmap_handle_t indev_handle;
144
145/** List of partitions. This structure is an empty head. */
146static part_t plist_head;
147
148static int mbr_init(const char *dev_name);
149static int mbr_part_read(void);
150static part_t *mbr_part_new(void);
151static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part);
152static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
153static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf);
154static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf);
155static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
156
157int main(int argc, char **argv)
158{
159 printf(NAME ": PC MBR partition driver\n");
160
161 if (argc != 2) {
162 printf("Expected one argument (device name).\n");
163 return -1;
164 }
165
166 if (mbr_init(argv[1]) != EOK)
167 return -1;
168
169 printf(NAME ": Accepting connections\n");
170 task_retval(0);
171 async_manager();
172
173 /* Not reached */
174 return 0;
175}
176
177static int mbr_init(const char *dev_name)
178{
179 int rc;
180 int i;
181 char *name;
182 devmap_handle_t dev;
183 uint64_t size_mb;
184 part_t *part;
185
186 rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
187 if (rc != EOK) {
188 printf(NAME ": could not resolve device `%s'.\n", dev_name);
189 return rc;
190 }
191
192 rc = block_init(EXCHANGE_SERIALIZE, indev_handle, 2048);
193 if (rc != EOK) {
194 printf(NAME ": could not init libblock.\n");
195 return rc;
196 }
197
198 /* Determine and verify block size. */
199
200 rc = block_get_bsize(indev_handle, &block_size);
201 if (rc != EOK) {
202 printf(NAME ": error getting block size.\n");
203 return rc;
204 }
205
206 if (block_size < 512 || (block_size % 512) != 0) {
207 printf(NAME ": invalid block size %zu.\n", block_size);
208 return ENOTSUP;
209 }
210
211 /* Read in partition records. */
212 rc = mbr_part_read();
213 if (rc != EOK)
214 return rc;
215
216 /* Register the driver with device mapper. */
217 rc = devmap_driver_register(NAME, mbr_connection);
218 if (rc != EOK) {
219 printf(NAME ": Unable to register driver.\n");
220 return rc;
221 }
222
223 /*
224 * Create partition devices.
225 */
226 i = 0;
227 part = plist_head.next;
228
229 while (part != NULL) {
230 /* Skip absent partitions. */
231 if (!part->present) {
232 part = part->next;
233 ++i;
234 continue;
235 }
236
237 asprintf(&name, "%sp%d", dev_name, i);
238 if (name == NULL)
239 return ENOMEM;
240
241 rc = devmap_device_register(name, &dev);
242 if (rc != EOK) {
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: %" PRIuOFF64 " blocks "
250 "%" PRIu64 " MB.\n", 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%04" PRIX16
292 ".\n", sgn);
293 return EINVAL;
294 }
295
296 ext_part = NULL;
297 plist_head.next = NULL;
298 prev = &plist_head;
299
300 for (i = 0; i < N_PRIMARY; ++i) {
301 p = mbr_part_new();
302 if (p == NULL)
303 return ENOMEM;
304
305 mbr_pte_to_part(0, &brb->pte[i], p);
306 prev->next = p;
307 prev = p;
308
309 if (brb->pte[i].ptype == PT_EXTENDED) {
310 p->present = false;
311 ext_part = p;
312 }
313 }
314
315 if (ext_part == NULL)
316 return EOK;
317
318 printf("Extended partition found.\n");
319
320 /*
321 * Read extended partition entries.
322 */
323
324 cp.start_addr = ext_part->start_addr;
325 cp.length = ext_part->length;
326 base = ext_part->start_addr;
327
328 do {
329 /*
330 * Addressing in the EBR chain is relative to the beginning
331 * of the extended partition.
332 */
333 ba = cp.start_addr;
334 rc = block_read_direct(indev_handle, ba, 1, brb);
335 if (rc != EOK) {
336 printf(NAME ": Failed reading EBR block at %"
337 PRIu32 ".\n", ba);
338 return rc;
339 }
340
341 sgn = uint16_t_le2host(brb->signature);
342 if (sgn != BR_SIGNATURE) {
343 printf(NAME ": Invalid boot record signature 0x%04"
344 PRIX16 " in EBR at %" PRIu32 ".\n", sgn, ba);
345 return EINVAL;
346 }
347
348 p = mbr_part_new();
349 if (p == NULL)
350 return ENOMEM;
351
352 /* First PTE is the logical partition itself. */
353 mbr_pte_to_part(base, &brb->pte[0], p);
354 prev->next = p;
355 prev = p;
356
357 /* Second PTE describes next chain element. */
358 mbr_pte_to_part(base, &brb->pte[1], &cp);
359 } while (cp.present);
360
361 return EOK;
362}
363
364/** Allocate a new @c part_t structure. */
365static part_t *mbr_part_new(void)
366{
367 return malloc(sizeof(part_t));
368}
369
370/** Parse partition table entry. */
371static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part)
372{
373 uint32_t sa, len;
374
375 sa = uint32_t_le2host(pte->first_lba);
376 len = uint32_t_le2host(pte->length);
377
378 part->start_addr = base + sa;
379 part->length = len;
380
381 part->present = (pte->ptype != PT_UNUSED) ? true : false;
382
383 part->dev = 0;
384 part->next = NULL;
385}
386
387static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
388{
389 size_t comm_size;
390 void *fs_va = NULL;
391 ipc_callid_t callid;
392 ipc_call_t call;
393 sysarg_t method;
394 devmap_handle_t dh;
395 unsigned int flags;
396 int retval;
397 uint64_t ba;
398 size_t cnt;
399 part_t *part;
400
401 /* Get the device handle. */
402 dh = IPC_GET_ARG1(*icall);
403
404 /*
405 * Determine which partition device is the client connecting to.
406 * A linear search is not terribly fast, but we only do this
407 * once for each connection.
408 */
409 part = plist_head.next;
410 while (part != NULL && part->dev != dh)
411 part = part->next;
412
413 if (part == NULL) {
414 async_answer_0(iid, EINVAL);
415 return;
416 }
417
418 assert(part->present == true);
419
420 /* Answer the IPC_M_CONNECT_ME_TO call. */
421 async_answer_0(iid, EOK);
422
423 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
424 async_answer_0(callid, EHANGUP);
425 return;
426 }
427
428 fs_va = as_get_mappable_page(comm_size);
429 if (fs_va == NULL) {
430 async_answer_0(callid, EHANGUP);
431 return;
432 }
433
434 (void) async_share_out_finalize(callid, fs_va);
435
436 while (1) {
437 callid = async_get_call(&call);
438 method = IPC_GET_IMETHOD(call);
439
440 if (!method) {
441 /* The other side has hung up. */
442 async_answer_0(callid, EOK);
443 return;
444 }
445
446 switch (method) {
447 case BD_READ_BLOCKS:
448 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
449 IPC_GET_ARG2(call));
450 cnt = IPC_GET_ARG3(call);
451 if (cnt * block_size > comm_size) {
452 retval = ELIMIT;
453 break;
454 }
455 retval = mbr_bd_read(part, ba, cnt, fs_va);
456 break;
457 case BD_WRITE_BLOCKS:
458 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
459 IPC_GET_ARG2(call));
460 cnt = IPC_GET_ARG3(call);
461 if (cnt * block_size > comm_size) {
462 retval = ELIMIT;
463 break;
464 }
465 retval = mbr_bd_write(part, ba, cnt, fs_va);
466 break;
467 case BD_GET_BLOCK_SIZE:
468 async_answer_1(callid, EOK, block_size);
469 continue;
470 case BD_GET_NUM_BLOCKS:
471 async_answer_2(callid, EOK, LOWER32(part->length),
472 UPPER32(part->length));
473 continue;
474 default:
475 retval = EINVAL;
476 break;
477 }
478 async_answer_0(callid, retval);
479 }
480}
481
482/** Read blocks from partition. */
483static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
484{
485 uint64_t gba;
486
487 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
488 return ELIMIT;
489
490 return block_read_direct(indev_handle, gba, cnt, buf);
491}
492
493/** Write blocks to partition. */
494static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
495{
496 uint64_t gba;
497
498 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
499 return ELIMIT;
500
501 return block_write_direct(indev_handle, gba, cnt, buf);
502}
503
504/** Translate block segment address with range checking. */
505static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
506{
507 if (ba + cnt > p->length)
508 return ELIMIT;
509
510 *gba = p->start_addr + ba;
511 return EOK;
512}
513
514/**
515 * @}
516 */
Note: See TracBrowser for help on using the repository browser.