source: mainline/uspace/srv/bd/part/guid_part/guid_part.c@ 9bddf37

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

Implement a basic GUID partition table driver.

  • Property mode set to 100644
File size: 9.8 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/ipc.h>
50#include <ipc/bd.h>
51#include <async.h>
52#include <as.h>
53#include <fibril_synch.h>
54#include <devmap.h>
55#include <sys/types.h>
56#include <libblock.h>
57#include <devmap.h>
58#include <errno.h>
59#include <bool.h>
60#include <byteorder.h>
61#include <assert.h>
62#include <macros.h>
63#include <task.h>
64
65#include "gpt.h"
66
67#define NAME "guid_part"
68
69const uint8_t efi_signature[8] = {
70 /* "EFI PART" in ASCII */
71 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
72};
73
74/** Partition */
75typedef struct part {
76 /** Partition entry is in use */
77 bool present;
78 /** Address of first block */
79 bn_t start_addr;
80 /** Number of blocks */
81 bn_t length;
82 /** Device representing the partition (outbound device) */
83 dev_handle_t dev;
84 /** Points to next partition structure. */
85 struct part *next;
86} part_t;
87
88static size_t block_size;
89
90/** Partitioned device (inbound device) */
91static dev_handle_t indev_handle;
92
93/** List of partitions. This structure is an empty head. */
94static part_t plist_head;
95
96static int gpt_init(const char *dev_name);
97static int gpt_read(void);
98static part_t *gpt_part_new(void);
99static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
100static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall);
101static int gpt_bd_read(part_t *p, bn_t ba, size_t cnt, void *buf);
102static int gpt_bd_write(part_t *p, bn_t ba, size_t cnt, const void *buf);
103static int gpt_bsa_translate(part_t *p, bn_t ba, size_t cnt, bn_t *gba);
104
105int main(int argc, char **argv)
106{
107 printf(NAME ": GUID partition table driver\n");
108
109 if (argc != 2) {
110 printf("Expected one argument (device name).\n");
111 return -1;
112 }
113
114 if (gpt_init(argv[1]) != EOK)
115 return -1;
116
117 printf(NAME ": Accepting connections\n");
118 task_retval(0);
119 async_manager();
120
121 /* Not reached */
122 return 0;
123}
124
125static int gpt_init(const char *dev_name)
126{
127 int rc;
128 int i;
129 char *name;
130 dev_handle_t dev;
131 uint64_t size_mb;
132 part_t *part;
133
134 rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
135 if (rc != EOK) {
136 printf(NAME ": could not resolve device `%s'.\n", dev_name);
137 return rc;
138 }
139
140 rc = block_init(indev_handle, 2048);
141 if (rc != EOK) {
142 printf(NAME ": could not init libblock.\n");
143 return rc;
144 }
145
146 /* Determine and verify block size. */
147
148 rc = block_get_bsize(indev_handle, &block_size);
149 if (rc != EOK) {
150 printf(NAME ": error getting block size.\n");
151 return rc;
152 }
153
154 if (block_size < 512 || (block_size % 512) != 0) {
155 printf(NAME ": invalid block size %d.\n");
156 return ENOTSUP;
157 }
158
159 /* Read in partition records. */
160 rc = gpt_read();
161 if (rc != EOK)
162 return rc;
163
164 /* Register the driver with device mapper. */
165 rc = devmap_driver_register(NAME, gpt_connection);
166 if (rc != EOK) {
167 printf(NAME ": Unable to register driver.\n");
168 return rc;
169 }
170
171 /*
172 * Create partition devices.
173 */
174 i = 0;
175 part = plist_head.next;
176
177 while (part != NULL) {
178 /* Skip absent partitions. */
179 if (!part->present) {
180 part = part->next;
181 ++i;
182 continue;
183 }
184
185 asprintf(&name, "%sp%d", dev_name, i);
186 if (name == NULL)
187 return ENOMEM;
188
189 rc = devmap_device_register(name, &dev);
190 if (rc != EOK) {
191 devmap_hangup_phone(DEVMAP_DRIVER);
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: %llu blocks %llu MB.\n",
199 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)
310{
311 size_t comm_size;
312 void *fs_va = NULL;
313 ipc_callid_t callid;
314 ipc_call_t call;
315 ipcarg_t method;
316 dev_handle_t dh;
317 int flags;
318 int retval;
319 bn_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 ipc_answer_0(iid, EINVAL);
337 return;
338 }
339
340 assert(part->present == true);
341
342 /* Answer the IPC_M_CONNECT_ME_TO call. */
343 ipc_answer_0(iid, EOK);
344
345 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
346 ipc_answer_0(callid, EHANGUP);
347 return;
348 }
349
350 fs_va = as_get_mappable_page(comm_size);
351 if (fs_va == NULL) {
352 ipc_answer_0(callid, EHANGUP);
353 return;
354 }
355
356 (void) async_share_out_finalize(callid, fs_va);
357
358 while (1) {
359 callid = async_get_call(&call);
360 method = IPC_GET_METHOD(call);
361 switch (method) {
362 case IPC_M_PHONE_HUNGUP:
363 /* The other side has hung up. */
364 ipc_answer_0(callid, EOK);
365 return;
366 case BD_READ_BLOCKS:
367 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
368 IPC_GET_ARG2(call));
369 cnt = IPC_GET_ARG3(call);
370 if (cnt * block_size > comm_size) {
371 retval = ELIMIT;
372 break;
373 }
374 retval = gpt_bd_read(part, ba, cnt, fs_va);
375 break;
376 case BD_WRITE_BLOCKS:
377 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
378 IPC_GET_ARG2(call));
379 cnt = IPC_GET_ARG3(call);
380 if (cnt * block_size > comm_size) {
381 retval = ELIMIT;
382 break;
383 }
384 retval = gpt_bd_write(part, ba, cnt, fs_va);
385 break;
386 case BD_GET_BLOCK_SIZE:
387 ipc_answer_1(callid, EOK, block_size);
388 continue;
389 case BD_GET_NUM_BLOCKS:
390 ipc_answer_2(callid, EOK, LOWER32(part->length),
391 UPPER32(part->length));
392 continue;
393 default:
394 retval = EINVAL;
395 break;
396 }
397 ipc_answer_0(callid, retval);
398 }
399}
400
401/** Read blocks from partition. */
402static int gpt_bd_read(part_t *p, bn_t ba, size_t cnt, void *buf)
403{
404 bn_t gba;
405
406 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
407 return ELIMIT;
408
409 return block_read_direct(indev_handle, gba, cnt, buf);
410}
411
412/** Write blocks to partition. */
413static int gpt_bd_write(part_t *p, bn_t ba, size_t cnt, const void *buf)
414{
415 bn_t gba;
416
417 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
418 return ELIMIT;
419
420 return block_write_direct(indev_handle, gba, cnt, buf);
421}
422
423/** Translate block segment address with range checking. */
424static int gpt_bsa_translate(part_t *p, bn_t ba, size_t cnt, bn_t *gba)
425{
426 if (ba + cnt > p->length)
427 return ELIMIT;
428
429 *gba = p->start_addr + ba;
430 return EOK;
431}
432
433/**
434 * @}
435 */
Note: See TracBrowser for help on using the repository browser.