source: mainline/uspace/srv/bd/part/guid_part/guid_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: 10.0 KB
Line 
1/*
2 * Copyright (c) 2010 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 GUID partition table driver
36 *
37 * Handles the GUID partitioning scheme. Uses a block device
38 * and provides one for each partition.
39 *
40 * References:
41 * UEFI Specification Version 2.3, Chapter 5 GUID Partition Table (GPT)
42 * Format, http://www.uefi.org/specs/
43 *
44 */
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <ipc/bd.h>
50#include <async.h>
51#include <as.h>
52#include <fibril_synch.h>
53#include <devmap.h>
54#include <sys/types.h>
55#include <sys/typefmt.h>
56#include <inttypes.h>
57#include <libblock.h>
58#include <devmap.h>
59#include <errno.h>
60#include <bool.h>
61#include <byteorder.h>
62#include <assert.h>
63#include <macros.h>
64#include <task.h>
65
66#include "gpt.h"
67
68#define NAME "guid_part"
69
70const uint8_t efi_signature[8] = {
71 /* "EFI PART" in ASCII */
72 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
73};
74
75/** Partition */
76typedef struct part {
77 /** Partition entry is in use */
78 bool present;
79 /** Address of first block */
80 aoff64_t start_addr;
81 /** Number of blocks */
82 aoff64_t length;
83 /** Device representing the partition (outbound device) */
84 devmap_handle_t dev;
85 /** Points to next partition structure. */
86 struct part *next;
87} part_t;
88
89static size_t block_size;
90
91/** Partitioned device (inbound device) */
92static devmap_handle_t indev_handle;
93
94/** List of partitions. This structure is an empty head. */
95static part_t plist_head;
96
97static int gpt_init(const char *dev_name);
98static int gpt_read(void);
99static part_t *gpt_part_new(void);
100static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
101static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
102static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf);
103static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf);
104static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
105
106int main(int argc, char **argv)
107{
108 printf(NAME ": GUID partition table driver\n");
109
110 if (argc != 2) {
111 printf("Expected one argument (device name).\n");
112 return -1;
113 }
114
115 if (gpt_init(argv[1]) != EOK)
116 return -1;
117
118 printf(NAME ": Accepting connections\n");
119 task_retval(0);
120 async_manager();
121
122 /* Not reached */
123 return 0;
124}
125
126static int gpt_init(const char *dev_name)
127{
128 int rc;
129 int i;
130 char *name;
131 devmap_handle_t dev;
132 uint64_t size_mb;
133 part_t *part;
134
135 rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
136 if (rc != EOK) {
137 printf(NAME ": could not resolve device `%s'.\n", dev_name);
138 return rc;
139 }
140
141 rc = block_init(EXCHANGE_SERIALIZE, indev_handle, 2048);
142 if (rc != EOK) {
143 printf(NAME ": could not init libblock.\n");
144 return rc;
145 }
146
147 /* Determine and verify block size. */
148
149 rc = block_get_bsize(indev_handle, &block_size);
150 if (rc != EOK) {
151 printf(NAME ": error getting block size.\n");
152 return rc;
153 }
154
155 if (block_size < 512 || (block_size % 512) != 0) {
156 printf(NAME ": invalid block size %zu.\n", block_size);
157 return ENOTSUP;
158 }
159
160 /* Read in partition records. */
161 rc = gpt_read();
162 if (rc != EOK)
163 return rc;
164
165 /* Register the driver with device mapper. */
166 rc = devmap_driver_register(NAME, gpt_connection);
167 if (rc != EOK) {
168 printf(NAME ": Unable to register driver.\n");
169 return rc;
170 }
171
172 /*
173 * Create partition devices.
174 */
175 i = 0;
176 part = plist_head.next;
177
178 while (part != NULL) {
179 /* Skip absent partitions. */
180 if (!part->present) {
181 part = part->next;
182 ++i;
183 continue;
184 }
185
186 asprintf(&name, "%sp%d", dev_name, i);
187 if (name == NULL)
188 return ENOMEM;
189
190 rc = devmap_device_register(name, &dev);
191 if (rc != EOK) {
192 printf(NAME ": Unable to register device %s.\n", name);
193 return rc;
194 }
195
196 size_mb = (part->length * block_size + 1024 * 1024 - 1)
197 / (1024 * 1024);
198 printf(NAME ": Registered device %s: %" PRIu64 " blocks "
199 "%" PRIuOFF64 " MB.\n", name, part->length, size_mb);
200
201 part->dev = dev;
202 free(name);
203
204 part = part->next;
205 ++i;
206 }
207
208 return EOK;
209}
210
211/** Read in partition records. */
212static int gpt_read(void)
213{
214 int i, rc;
215 gpt_header_t *gpt_hdr;
216 gpt_entry_t *etable;
217 uint64_t ba;
218 uint32_t bcnt;
219 uint32_t esize;
220 uint32_t num_entries;
221 uint32_t entry;
222 part_t *prev, *p;
223
224 gpt_hdr = malloc(block_size);
225 if (gpt_hdr == NULL) {
226 printf(NAME ": Failed allocating memory.\n");
227 return ENOMEM;
228 }
229
230 rc = block_read_direct(indev_handle, GPT_HDR_BA, 1, gpt_hdr);
231 if (rc != EOK) {
232 printf(NAME ": Failed reading GPT header block.\n");
233 return rc;
234 }
235
236 for (i = 0; i < 8; ++i) {
237 if (gpt_hdr->efi_signature[i] != efi_signature[i]) {
238 printf(NAME ": Invalid GPT signature.\n");
239 return EINVAL;
240 }
241 }
242
243 plist_head.next = NULL;
244 prev = &plist_head;
245
246 num_entries = uint32_t_le2host(gpt_hdr->num_entries);
247 ba = uint64_t_le2host(gpt_hdr->entry_lba);
248 esize = uint32_t_le2host(gpt_hdr->entry_size);
249 bcnt = (num_entries / esize) + ((num_entries % esize != 0) ? 1 : 0);
250
251 etable = malloc(num_entries * esize);
252 if (etable == NULL) {
253 free(gpt_hdr);
254 printf(NAME ": Failed allocating memory.\n");
255 return ENOMEM;
256 }
257
258 rc = block_read_direct(indev_handle, ba, bcnt, etable);
259 if (rc != EOK) {
260 printf(NAME ": Failed reading GPT entries.\n");
261 return rc;
262 }
263
264 for (entry = 0; entry < num_entries; ++entry) {
265 p = gpt_part_new();
266 if (p == NULL)
267 return ENOMEM;
268
269 gpt_pte_to_part(&etable[entry], p);
270 prev->next = p;
271 prev = p;
272 }
273
274 free(etable);
275
276 return EOK;
277}
278
279/** Allocate a new @c part_t structure. */
280static part_t *gpt_part_new(void)
281{
282 return malloc(sizeof(part_t));
283}
284
285/** Parse partition table entry. */
286static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part)
287{
288 uint64_t sa, len;
289 int i;
290
291 /* Partition spans addresses [start_lba, end_lba] (inclusive). */
292 sa = uint64_t_le2host(pte->start_lba);
293 len = uint64_t_le2host(pte->end_lba) + 1 - sa;
294
295 part->start_addr = sa;
296 part->length = len;
297
298 part->present = false;
299
300 for (i = 0; i < 8; ++i) {
301 if (pte->part_type[i] != 0x00)
302 part->present = true;
303 }
304
305 part->dev = 0;
306 part->next = NULL;
307}
308
309static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
310{
311 size_t comm_size;
312 void *fs_va = NULL;
313 ipc_callid_t callid;
314 ipc_call_t call;
315 sysarg_t method;
316 devmap_handle_t dh;
317 unsigned int flags;
318 int retval;
319 aoff64_t ba;
320 size_t cnt;
321 part_t *part;
322
323 /* Get the device handle. */
324 dh = IPC_GET_ARG1(*icall);
325
326 /*
327 * Determine which partition device is the client connecting to.
328 * A linear search is not terribly fast, but we only do this
329 * once for each connection.
330 */
331 part = plist_head.next;
332 while (part != NULL && part->dev != dh)
333 part = part->next;
334
335 if (part == NULL) {
336 async_answer_0(iid, EINVAL);
337 return;
338 }
339
340 assert(part->present == true);
341
342 /* Answer the IPC_M_CONNECT_ME_TO call. */
343 async_answer_0(iid, EOK);
344
345 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
346 async_answer_0(callid, EHANGUP);
347 return;
348 }
349
350 fs_va = as_get_mappable_page(comm_size);
351 if (fs_va == NULL) {
352 async_answer_0(callid, EHANGUP);
353 return;
354 }
355
356 (void) async_share_out_finalize(callid, fs_va);
357
358 while (true) {
359 callid = async_get_call(&call);
360 method = IPC_GET_IMETHOD(call);
361
362 if (!method) {
363 /* The other side has hung up. */
364 async_answer_0(callid, EOK);
365 return;
366 }
367
368 switch (method) {
369 case BD_READ_BLOCKS:
370 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
371 IPC_GET_ARG2(call));
372 cnt = IPC_GET_ARG3(call);
373 if (cnt * block_size > comm_size) {
374 retval = ELIMIT;
375 break;
376 }
377 retval = gpt_bd_read(part, ba, cnt, fs_va);
378 break;
379 case BD_WRITE_BLOCKS:
380 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
381 IPC_GET_ARG2(call));
382 cnt = IPC_GET_ARG3(call);
383 if (cnt * block_size > comm_size) {
384 retval = ELIMIT;
385 break;
386 }
387 retval = gpt_bd_write(part, ba, cnt, fs_va);
388 break;
389 case BD_GET_BLOCK_SIZE:
390 async_answer_1(callid, EOK, block_size);
391 continue;
392 case BD_GET_NUM_BLOCKS:
393 async_answer_2(callid, EOK, LOWER32(part->length),
394 UPPER32(part->length));
395 continue;
396 default:
397 retval = EINVAL;
398 break;
399 }
400 async_answer_0(callid, retval);
401 }
402}
403
404/** Read blocks from partition. */
405static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf)
406{
407 aoff64_t gba;
408
409 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
410 return ELIMIT;
411
412 return block_read_direct(indev_handle, gba, cnt, buf);
413}
414
415/** Write blocks to partition. */
416static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf)
417{
418 aoff64_t gba;
419
420 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
421 return ELIMIT;
422
423 return block_write_direct(indev_handle, gba, cnt, buf);
424}
425
426/** Translate block segment address with range checking. */
427static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba)
428{
429 if (ba + cnt > p->length)
430 return ELIMIT;
431
432 *gba = p->start_addr + ba;
433 return EOK;
434}
435
436/**
437 * @}
438 */
Note: See TracBrowser for help on using the repository browser.