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

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

Remove the two-phase way of creating virtual memory areas (first asking for a mappable address and then mapping it) which was prone to race conditions when two or more calls to as_get_mappable_page() and as_area_create() were interleaved. This for example caused the e1k driver to randomly fail.

The memory area related syscalls and IPC calls have all been altered to accept a special value (void *) -1, representing a demand to atomically search for a mappable address space "hole" and map to it.

Individual changes:

  • IPC_M_SHARE_OUT: the destination address space area is supplied by the kernel, the callee only specifies the lower bound

(the address is returned to the callee via a pointer in an IPC reply argument)

  • IPC_M_SHARE_IN: the destination address space ares is supplied by the kernel, the callee only specifies the lower bound

(the address is returned to the caller as usual via an IPC argument)

  • SYS_AS_GET_UNMAPPED_AREA was removed
  • dummy implementations of SYS_PHYSMEM_UNMAP and SYS_IOSPACE_DISABLE were added for the sake of symmetry (they do nothing yet)
  • SYS_PHYSMEM_MAP and SYS_DMAMEM_MAP were altered to accept (void *) -1 as address space area base and a lower bound
  • kernel as_area_create() and as_area_share() were altered to accept (void *) -1 as address space area base and a lower bound
  • uspace libraries and programs were altered to reflect the new API
  • 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 <ipc/bd.h>
60#include <async.h>
61#include <as.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 <libblock.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 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 /** 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 service_id_t indef_sid;
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, void *arg);
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 service_id_t dsid;
182 uint64_t size_mb;
183 part_t *part;
184
185 rc = loc_service_get_id(dev_name, &indef_sid, 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(EXCHANGE_SERIALIZE, indef_sid, 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(indef_sid, &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 %zu.\n", block_size);
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 server with location service. */
216 rc = loc_server_register(NAME, mbr_connection);
217 if (rc != EOK) {
218 printf(NAME ": Unable to register server.\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 = loc_service_register(name, &dsid);
241 if (rc != EOK) {
242 printf(NAME ": Unable to register service %s.\n", name);
243 return rc;
244 }
245
246 size_mb = (part->length * block_size + 1024 * 1024 - 1)
247 / (1024 * 1024);
248 printf(NAME ": Registered device %s: %" PRIuOFF64 " blocks "
249 "%" PRIu64 " MB.\n", name, part->length, size_mb);
250
251 part->dsid = dsid;
252 free(name);
253
254 part = part->next;
255 ++i;
256 }
257
258 return EOK;
259}
260
261/** Read in partition records. */
262static int mbr_part_read(void)
263{
264 int i, rc;
265 br_block_t *brb;
266 uint16_t sgn;
267 uint32_t ba;
268 part_t *ext_part, cp;
269 uint32_t base;
270 part_t *prev, *p;
271
272 brb = malloc(sizeof(br_block_t));
273 if (brb == NULL) {
274 printf(NAME ": Failed allocating memory.\n");
275 return ENOMEM;
276 }
277
278 /*
279 * Read primary partition entries.
280 */
281
282 rc = block_read_direct(indef_sid, 0, 1, brb);
283 if (rc != EOK) {
284 printf(NAME ": Failed reading MBR block.\n");
285 return rc;
286 }
287
288 sgn = uint16_t_le2host(brb->signature);
289 if (sgn != BR_SIGNATURE) {
290 printf(NAME ": Invalid boot record signature 0x%04" PRIX16
291 ".\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(indef_sid, ba, 1, brb);
334 if (rc != EOK) {
335 printf(NAME ": Failed reading EBR block at %"
336 PRIu32 ".\n", ba);
337 return rc;
338 }
339
340 sgn = uint16_t_le2host(brb->signature);
341 if (sgn != BR_SIGNATURE) {
342 printf(NAME ": Invalid boot record signature 0x%04"
343 PRIX16 " in EBR at %" PRIu32 ".\n", sgn, ba);
344 return EINVAL;
345 }
346
347 p = mbr_part_new();
348 if (p == NULL)
349 return ENOMEM;
350
351 /* First PTE is the logical partition itself. */
352 mbr_pte_to_part(base, &brb->pte[0], p);
353 prev->next = p;
354 prev = p;
355
356 /* Second PTE describes next chain element. */
357 mbr_pte_to_part(base, &brb->pte[1], &cp);
358 } while (cp.present);
359
360 return EOK;
361}
362
363/** Allocate a new @c part_t structure. */
364static part_t *mbr_part_new(void)
365{
366 return malloc(sizeof(part_t));
367}
368
369/** Parse partition table entry. */
370static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part)
371{
372 uint32_t sa, len;
373
374 sa = uint32_t_le2host(pte->first_lba);
375 len = uint32_t_le2host(pte->length);
376
377 part->start_addr = base + sa;
378 part->length = len;
379
380 part->present = (pte->ptype != PT_UNUSED) ? true : false;
381
382 part->dsid = 0;
383 part->next = NULL;
384}
385
386static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
387{
388 size_t comm_size;
389 void *fs_va = NULL;
390 ipc_callid_t callid;
391 ipc_call_t call;
392 sysarg_t method;
393 service_id_t dh;
394 unsigned int flags;
395 int retval;
396 uint64_t ba;
397 size_t cnt;
398 part_t *part;
399
400 /* Get the device handle. */
401 dh = IPC_GET_ARG1(*icall);
402
403 /*
404 * Determine which partition device is the client connecting to.
405 * A linear search is not terribly fast, but we only do this
406 * once for each connection.
407 */
408 part = plist_head.next;
409 while (part != NULL && part->dsid != dh)
410 part = part->next;
411
412 if (part == NULL) {
413 async_answer_0(iid, EINVAL);
414 return;
415 }
416
417 assert(part->present == true);
418
419 /* Answer the IPC_M_CONNECT_ME_TO call. */
420 async_answer_0(iid, EOK);
421
422 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
423 async_answer_0(callid, EHANGUP);
424 return;
425 }
426
427 (void) async_share_out_finalize(callid, &fs_va);
428 if (fs_va == (void *) -1) {
429 async_answer_0(callid, EHANGUP);
430 return;
431 }
432
433 while (1) {
434 callid = async_get_call(&call);
435 method = IPC_GET_IMETHOD(call);
436
437 if (!method) {
438 /* The other side has hung up. */
439 async_answer_0(callid, EOK);
440 return;
441 }
442
443 switch (method) {
444 case BD_READ_BLOCKS:
445 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
446 IPC_GET_ARG2(call));
447 cnt = IPC_GET_ARG3(call);
448 if (cnt * block_size > comm_size) {
449 retval = ELIMIT;
450 break;
451 }
452 retval = mbr_bd_read(part, ba, cnt, fs_va);
453 break;
454 case BD_WRITE_BLOCKS:
455 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
456 IPC_GET_ARG2(call));
457 cnt = IPC_GET_ARG3(call);
458 if (cnt * block_size > comm_size) {
459 retval = ELIMIT;
460 break;
461 }
462 retval = mbr_bd_write(part, ba, cnt, fs_va);
463 break;
464 case BD_GET_BLOCK_SIZE:
465 async_answer_1(callid, EOK, block_size);
466 continue;
467 case BD_GET_NUM_BLOCKS:
468 async_answer_2(callid, EOK, LOWER32(part->length),
469 UPPER32(part->length));
470 continue;
471 default:
472 retval = EINVAL;
473 break;
474 }
475 async_answer_0(callid, retval);
476 }
477}
478
479/** Read blocks from partition. */
480static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
481{
482 uint64_t gba;
483
484 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
485 return ELIMIT;
486
487 return block_read_direct(indef_sid, gba, cnt, buf);
488}
489
490/** Write blocks to partition. */
491static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
492{
493 uint64_t gba;
494
495 if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
496 return ELIMIT;
497
498 return block_write_direct(indef_sid, gba, cnt, buf);
499}
500
501/** Translate block segment address with range checking. */
502static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
503{
504 if (ba + cnt > p->length)
505 return ELIMIT;
506
507 *gba = p->start_addr + ba;
508 return EOK;
509}
510
511/**
512 * @}
513 */
Note: See TracBrowser for help on using the repository browser.