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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b688fd8 was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • Property mode set to 100644
File size: 11.5 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_fallback_port_handler(mbr_connection, NULL);
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.