source: mainline/uspace/srv/bd/part/mbr_part/mbr_part.c@ 1c635d6

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

Standards-compliant boolean type.

  • Property mode set to 100644
File size: 11.4 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 <async.h>
60#include <as.h>
61#include <bd_srv.h>
62#include <fibril_synch.h>
63#include <loc.h>
64#include <sys/types.h>
65#include <sys/typefmt.h>
66#include <inttypes.h>
67#include <block.h>
68#include <errno.h>
69#include <stdbool.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 aoff64_t start_addr;
98 /** Number of blocks */
99 aoff64_t length;
100 /** Device representing the partition (outbound device) */
101 service_id_t dsid;
102 /** Block device service sturcture */
103 bd_srvs_t bds;
104 /** Points to next partition structure. */
105 struct part *next;
106} part_t;
107
108/** Structure of a partition table entry */
109typedef struct {
110 uint8_t status;
111 /** CHS of fist block in partition */
112 uint8_t first_chs[3];
113 /** Partition type */
114 uint8_t ptype;
115 /** CHS of last block in partition */
116 uint8_t last_chs[3];
117 /** LBA of first block in partition */
118 uint32_t first_lba;
119 /** Number of blocks in partition */
120 uint32_t length;
121} __attribute__((packed)) pt_entry_t;
122
123/** Structure of a boot-record block */
124typedef struct {
125 /* Area for boot code */
126 uint8_t code_area[440];
127
128 /* Optional media ID */
129 uint32_t media_id;
130
131 uint16_t pad0;
132
133 /** Partition table entries */
134 pt_entry_t pte[N_PRIMARY];
135
136 /** Boot record block signature (@c BR_SIGNATURE) */
137 uint16_t signature;
138} __attribute__((packed)) br_block_t;
139
140
141static size_t block_size;
142
143/** Partitioned device (inbound device) */
144static service_id_t indev_sid;
145
146/** List of partitions. This structure is an empty head. */
147static part_t plist_head;
148
149static int mbr_init(const char *dev_name);
150static int mbr_part_read(void);
151static part_t *mbr_part_new(void);
152static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part);
153static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
154static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
155
156static int mbr_bd_open(bd_srvs_t *, bd_srv_t *);
157static int mbr_bd_close(bd_srv_t *);
158static int mbr_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
159static int mbr_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
160static int mbr_bd_get_block_size(bd_srv_t *, size_t *);
161static int mbr_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
162
163static bd_ops_t mbr_bd_ops = {
164 .open = mbr_bd_open,
165 .close = mbr_bd_close,
166 .read_blocks = mbr_bd_read_blocks,
167 .write_blocks = mbr_bd_write_blocks,
168 .get_block_size = mbr_bd_get_block_size,
169 .get_num_blocks = mbr_bd_get_num_blocks
170};
171
172static part_t *bd_srv_part(bd_srv_t *bd)
173{
174 return (part_t *)bd->srvs->sarg;
175}
176
177int main(int argc, char **argv)
178{
179 printf(NAME ": PC MBR partition driver\n");
180
181 if (argc != 2) {
182 printf("Expected one argument (device name).\n");
183 return -1;
184 }
185
186 if (mbr_init(argv[1]) != EOK)
187 return -1;
188
189 printf(NAME ": Accepting connections\n");
190 task_retval(0);
191 async_manager();
192
193 /* Not reached */
194 return 0;
195}
196
197static int mbr_init(const char *dev_name)
198{
199 int rc;
200 int i;
201 char *name;
202 service_id_t dsid;
203 uint64_t size_mb;
204 part_t *part;
205
206 rc = loc_service_get_id(dev_name, &indev_sid, 0);
207 if (rc != EOK) {
208 printf(NAME ": could not resolve device `%s'.\n", dev_name);
209 return rc;
210 }
211
212 rc = block_init(EXCHANGE_SERIALIZE, indev_sid, 2048);
213 if (rc != EOK) {
214 printf(NAME ": could not init libblock.\n");
215 return rc;
216 }
217
218 /* Determine and verify block size. */
219
220 rc = block_get_bsize(indev_sid, &block_size);
221 if (rc != EOK) {
222 printf(NAME ": error getting block size.\n");
223 return rc;
224 }
225
226 if (block_size < 512 || (block_size % 512) != 0) {
227 printf(NAME ": invalid block size %zu.\n", block_size);
228 return ENOTSUP;
229 }
230
231 /* Read in partition records. */
232 rc = mbr_part_read();
233 if (rc != EOK)
234 return rc;
235
236 /* Register server with location service. */
237 async_set_client_connection(mbr_connection);
238 rc = loc_server_register(NAME);
239 if (rc != EOK) {
240 printf(NAME ": Unable to register server.\n");
241 return rc;
242 }
243
244 /*
245 * Create partition devices.
246 */
247 i = 0;
248 part = plist_head.next;
249
250 while (part != NULL) {
251 /* Skip absent partitions. */
252 if (!part->present) {
253 part = part->next;
254 ++i;
255 continue;
256 }
257
258 asprintf(&name, "%sp%d", dev_name, i);
259 if (name == NULL)
260 return ENOMEM;
261
262 rc = loc_service_register(name, &dsid);
263 if (rc != EOK) {
264 printf(NAME ": Unable to register service %s.\n", name);
265 return rc;
266 }
267
268 size_mb = (part->length * block_size + 1024 * 1024 - 1)
269 / (1024 * 1024);
270 printf(NAME ": Registered device %s: %" PRIuOFF64 " blocks "
271 "%" PRIu64 " MB.\n", name, part->length, size_mb);
272
273 part->dsid = dsid;
274 free(name);
275
276 part = part->next;
277 ++i;
278 }
279
280 return EOK;
281}
282
283/** Read in partition records. */
284static int mbr_part_read(void)
285{
286 int i, rc;
287 br_block_t *brb;
288 uint16_t sgn;
289 uint32_t ba;
290 part_t *ext_part, cp;
291 uint32_t base;
292 part_t *prev, *p;
293
294 brb = malloc(sizeof(br_block_t));
295 if (brb == NULL) {
296 printf(NAME ": Failed allocating memory.\n");
297 return ENOMEM;
298 }
299
300 /*
301 * Read primary partition entries.
302 */
303
304 rc = block_read_direct(indev_sid, 0, 1, brb);
305 if (rc != EOK) {
306 printf(NAME ": Failed reading MBR block.\n");
307 return rc;
308 }
309
310 sgn = uint16_t_le2host(brb->signature);
311 if (sgn != BR_SIGNATURE) {
312 printf(NAME ": Invalid boot record signature 0x%04" PRIX16
313 ".\n", sgn);
314 return EINVAL;
315 }
316
317 ext_part = NULL;
318 plist_head.next = NULL;
319 prev = &plist_head;
320
321 for (i = 0; i < N_PRIMARY; ++i) {
322 p = mbr_part_new();
323 if (p == NULL)
324 return ENOMEM;
325
326 mbr_pte_to_part(0, &brb->pte[i], p);
327 prev->next = p;
328 prev = p;
329
330 if (brb->pte[i].ptype == PT_EXTENDED) {
331 p->present = false;
332 ext_part = p;
333 }
334 }
335
336 if (ext_part == NULL)
337 return EOK;
338
339 printf("Extended partition found.\n");
340
341 /*
342 * Read extended partition entries.
343 */
344
345 cp.start_addr = ext_part->start_addr;
346 cp.length = ext_part->length;
347 base = ext_part->start_addr;
348
349 do {
350 /*
351 * Addressing in the EBR chain is relative to the beginning
352 * of the extended partition.
353 */
354 ba = cp.start_addr;
355 rc = block_read_direct(indev_sid, ba, 1, brb);
356 if (rc != EOK) {
357 printf(NAME ": Failed reading EBR block at %"
358 PRIu32 ".\n", ba);
359 return rc;
360 }
361
362 sgn = uint16_t_le2host(brb->signature);
363 if (sgn != BR_SIGNATURE) {
364 printf(NAME ": Invalid boot record signature 0x%04"
365 PRIX16 " in EBR at %" PRIu32 ".\n", sgn, ba);
366 return EINVAL;
367 }
368
369 p = mbr_part_new();
370 if (p == NULL)
371 return ENOMEM;
372
373 /* First PTE is the logical partition itself. */
374 mbr_pte_to_part(base, &brb->pte[0], p);
375 prev->next = p;
376 prev = p;
377
378 /* Second PTE describes next chain element. */
379 mbr_pte_to_part(base, &brb->pte[1], &cp);
380 } while (cp.present);
381
382 return EOK;
383}
384
385/** Allocate a new @c part_t structure. */
386static part_t *mbr_part_new(void)
387{
388 return malloc(sizeof(part_t));
389}
390
391/** Parse partition table entry. */
392static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part)
393{
394 uint32_t sa, len;
395
396 sa = uint32_t_le2host(pte->first_lba);
397 len = uint32_t_le2host(pte->length);
398
399 part->start_addr = base + sa;
400 part->length = len;
401
402 part->present = (pte->ptype != PT_UNUSED) ? true : false;
403
404 bd_srvs_init(&part->bds);
405 part->bds.ops = &mbr_bd_ops;
406 part->bds.sarg = part;
407
408 part->dsid = 0;
409 part->next = NULL;
410}
411
412static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
413{
414 service_id_t dh;
415 part_t *part;
416
417 /* Get the device handle. */
418 dh = IPC_GET_ARG1(*icall);
419
420 /*
421 * Determine which partition device is the client connecting to.
422 * A linear search is not terribly fast, but we only do this
423 * once for each connection.
424 */
425 part = plist_head.next;
426 while (part != NULL && part->dsid != dh)
427 part = part->next;
428
429 if (part == NULL) {
430 async_answer_0(iid, EINVAL);
431 return;
432 }
433
434 assert(part->present == true);
435 bd_conn(iid, icall, &part->bds);
436}
437
438/** Open device. */
439static int mbr_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
440{
441 return EOK;
442}
443
444/** Close device. */
445static int mbr_bd_close(bd_srv_t *bd)
446{
447 return EOK;
448}
449
450/** Read blocks from partition. */
451static int mbr_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
452 size_t size)
453{
454 part_t *p = bd_srv_part(bd);
455 aoff64_t gba;
456
457 if (cnt * block_size < size)
458 return EINVAL;
459
460 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
461 return ELIMIT;
462
463 return block_read_direct(indev_sid, gba, cnt, buf);
464}
465
466/** Write blocks to partition. */
467static int mbr_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
468 const void *buf, size_t size)
469{
470 part_t *p = bd_srv_part(bd);
471 aoff64_t gba;
472
473 if (cnt * block_size < size)
474 return EINVAL;
475
476 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
477 return ELIMIT;
478
479 return block_write_direct(indev_sid, gba, cnt, buf);
480}
481
482/** Get device block size. */
483static int mbr_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
484{
485 *rsize = block_size;
486 return EOK;
487}
488
489/** Get number of blocks on device. */
490static int mbr_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
491{
492 part_t *part = bd_srv_part(bd);
493
494 *rnb = part->length;
495 return EOK;
496}
497
498/** Translate block segment address with range checking. */
499static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
500{
501 if (ba + cnt > p->length)
502 return ELIMIT;
503
504 *gba = p->start_addr + ba;
505 return EOK;
506}
507
508/**
509 * @}
510 */
Note: See TracBrowser for help on using the repository browser.