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

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

libblock.{c|h} → block.{c|h}

  • 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 <async.h>
50#include <as.h>
51#include <bd_srv.h>
52#include <fibril_synch.h>
53#include <loc.h>
54#include <sys/types.h>
55#include <sys/typefmt.h>
56#include <inttypes.h>
57#include <block.h>
58#include <loc.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 /** Service representing the partition (outbound device) */
84 service_id_t dsid;
85 /** Block device service structure */
86 bd_srvs_t bds;
87 /** Points to next partition structure. */
88 struct part *next;
89} part_t;
90
91static size_t block_size;
92
93/** Partitioned device (inbound device) */
94static service_id_t indev_sid;
95
96/** List of partitions. This structure is an empty head. */
97static part_t plist_head;
98
99static int gpt_init(const char *dev_name);
100static int gpt_read(void);
101static part_t *gpt_part_new(void);
102static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
103static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg);
104static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
105
106static int gpt_bd_open(bd_srvs_t *, bd_srv_t *);
107static int gpt_bd_close(bd_srv_t *);
108static int gpt_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
109static int gpt_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
110static int gpt_bd_get_block_size(bd_srv_t *, size_t *);
111static int gpt_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
112
113static bd_ops_t gpt_bd_ops = {
114 .open = gpt_bd_open,
115 .close = gpt_bd_close,
116 .read_blocks = gpt_bd_read_blocks,
117 .write_blocks = gpt_bd_write_blocks,
118 .get_block_size = gpt_bd_get_block_size,
119 .get_num_blocks = gpt_bd_get_num_blocks
120};
121
122static part_t *bd_srv_part(bd_srv_t *bd)
123{
124 return (part_t *)bd->srvs->sarg;
125}
126
127int main(int argc, char **argv)
128{
129 printf(NAME ": GUID partition table driver\n");
130
131 if (argc != 2) {
132 printf("Expected one argument (device name).\n");
133 return -1;
134 }
135
136 if (gpt_init(argv[1]) != EOK)
137 return -1;
138
139 printf(NAME ": Accepting connections\n");
140 task_retval(0);
141 async_manager();
142
143 /* Not reached */
144 return 0;
145}
146
147static int gpt_init(const char *dev_name)
148{
149 int rc;
150 int i;
151 char *name;
152 service_id_t dsid;
153 uint64_t size_mb;
154 part_t *part;
155
156 rc = loc_service_get_id(dev_name, &indev_sid, 0);
157 if (rc != EOK) {
158 printf(NAME ": could not resolve device `%s'.\n", dev_name);
159 return rc;
160 }
161
162 rc = block_init(EXCHANGE_SERIALIZE, indev_sid, 2048);
163 if (rc != EOK) {
164 printf(NAME ": could not init libblock.\n");
165 return rc;
166 }
167
168 /* Determine and verify block size. */
169
170 rc = block_get_bsize(indev_sid, &block_size);
171 if (rc != EOK) {
172 printf(NAME ": error getting block size.\n");
173 return rc;
174 }
175
176 if (block_size < 512 || (block_size % 512) != 0) {
177 printf(NAME ": invalid block size %zu.\n", block_size);
178 return ENOTSUP;
179 }
180
181 /* Read in partition records. */
182 rc = gpt_read();
183 if (rc != EOK)
184 return rc;
185
186 /* Register server with location service. */
187 async_set_client_connection(gpt_connection);
188 rc = loc_server_register(NAME);
189 if (rc != EOK) {
190 printf(NAME ": Unable to register server.\n");
191 return rc;
192 }
193
194 /*
195 * Create partition devices.
196 */
197 i = 0;
198 part = plist_head.next;
199
200 while (part != NULL) {
201 /* Skip absent partitions. */
202 if (!part->present) {
203 part = part->next;
204 ++i;
205 continue;
206 }
207
208 asprintf(&name, "%sp%d", dev_name, i);
209 if (name == NULL)
210 return ENOMEM;
211
212 rc = loc_service_register(name, &dsid);
213 if (rc != EOK) {
214 printf(NAME ": Unable to register service %s.\n", name);
215 return rc;
216 }
217
218 size_mb = (part->length * block_size + 1024 * 1024 - 1)
219 / (1024 * 1024);
220 printf(NAME ": Registered device %s: %" PRIu64 " blocks "
221 "%" PRIuOFF64 " MB.\n", name, part->length, size_mb);
222
223 part->dsid = dsid;
224 free(name);
225
226 part = part->next;
227 ++i;
228 }
229
230 return EOK;
231}
232
233/** Read in partition records. */
234static int gpt_read(void)
235{
236 int i, rc;
237 gpt_header_t *gpt_hdr;
238 gpt_entry_t *etable;
239 uint64_t ba;
240 uint32_t bcnt;
241 uint32_t esize;
242 uint32_t num_entries;
243 uint32_t entry;
244 part_t *prev, *p;
245
246 gpt_hdr = malloc(block_size);
247 if (gpt_hdr == NULL) {
248 printf(NAME ": Failed allocating memory.\n");
249 return ENOMEM;
250 }
251
252 rc = block_read_direct(indev_sid, GPT_HDR_BA, 1, gpt_hdr);
253 if (rc != EOK) {
254 printf(NAME ": Failed reading GPT header block.\n");
255 return rc;
256 }
257
258 for (i = 0; i < 8; ++i) {
259 if (gpt_hdr->efi_signature[i] != efi_signature[i]) {
260 printf(NAME ": Invalid GPT signature.\n");
261 return EINVAL;
262 }
263 }
264
265 plist_head.next = NULL;
266 prev = &plist_head;
267
268 num_entries = uint32_t_le2host(gpt_hdr->num_entries);
269 ba = uint64_t_le2host(gpt_hdr->entry_lba);
270 esize = uint32_t_le2host(gpt_hdr->entry_size);
271 bcnt = (num_entries / esize) + ((num_entries % esize != 0) ? 1 : 0);
272
273 etable = malloc(num_entries * esize);
274 if (etable == NULL) {
275 free(gpt_hdr);
276 printf(NAME ": Failed allocating memory.\n");
277 return ENOMEM;
278 }
279
280 rc = block_read_direct(indev_sid, ba, bcnt, etable);
281 if (rc != EOK) {
282 printf(NAME ": Failed reading GPT entries.\n");
283 return rc;
284 }
285
286 for (entry = 0; entry < num_entries; ++entry) {
287 p = gpt_part_new();
288 if (p == NULL)
289 return ENOMEM;
290
291 gpt_pte_to_part(&etable[entry], p);
292 prev->next = p;
293 prev = p;
294 }
295
296 free(etable);
297
298 return EOK;
299}
300
301/** Allocate a new @c part_t structure. */
302static part_t *gpt_part_new(void)
303{
304 return malloc(sizeof(part_t));
305}
306
307/** Parse partition table entry. */
308static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part)
309{
310 uint64_t sa, len;
311 int i;
312
313 /* Partition spans addresses [start_lba, end_lba] (inclusive). */
314 sa = uint64_t_le2host(pte->start_lba);
315 len = uint64_t_le2host(pte->end_lba) + 1 - sa;
316
317 part->start_addr = sa;
318 part->length = len;
319
320 part->present = false;
321
322 for (i = 0; i < 8; ++i) {
323 if (pte->part_type[i] != 0x00)
324 part->present = true;
325 }
326
327 bd_srvs_init(&part->bds);
328 part->bds.ops = &gpt_bd_ops;
329 part->bds.sarg = part;
330
331 part->dsid = 0;
332 part->next = NULL;
333}
334
335static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
336{
337 service_id_t dh;
338 part_t *part;
339
340 /* Get the device handle. */
341 dh = IPC_GET_ARG1(*icall);
342
343 /*
344 * Determine which partition device is the client connecting to.
345 * A linear search is not terribly fast, but we only do this
346 * once for each connection.
347 */
348 part = plist_head.next;
349 while (part != NULL && part->dsid != dh)
350 part = part->next;
351
352 if (part == NULL) {
353 async_answer_0(iid, EINVAL);
354 return;
355 }
356
357 assert(part->present == true);
358
359 bd_conn(iid, icall, &part->bds);
360}
361
362/** Open device. */
363static int gpt_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
364{
365 return EOK;
366}
367
368/** Close device. */
369static int gpt_bd_close(bd_srv_t *bd)
370{
371 return EOK;
372}
373
374/** Read blocks from partition. */
375static int gpt_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
376 size_t size)
377{
378 part_t *p = bd_srv_part(bd);
379 aoff64_t gba;
380
381 if (cnt * block_size < size)
382 return EINVAL;
383
384 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
385 return ELIMIT;
386
387 return block_read_direct(indev_sid, gba, cnt, buf);
388}
389
390/** Write blocks to partition. */
391static int gpt_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
392 const void *buf, size_t size)
393{
394 part_t *p = bd_srv_part(bd);
395 aoff64_t gba;
396
397 if (cnt * block_size < size)
398 return EINVAL;
399
400 if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
401 return ELIMIT;
402
403 return block_write_direct(indev_sid, gba, cnt, buf);
404}
405
406/** Get device block size. */
407static int gpt_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
408{
409 *rsize = block_size;
410 return EOK;
411}
412
413/** Get number of blocks on device. */
414static int gpt_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
415{
416 part_t *part = bd_srv_part(bd);
417
418 *rnb = part->length;
419 return EOK;
420}
421
422
423/** Translate block segment address with range checking. */
424static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_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.