source: mainline/uspace/srv/bd/part/guid_part/guid_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: 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 <stdbool.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_fallback_port_handler(gpt_connection, NULL);
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.