guid_part.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <unistd.h>
00049 #include <ipc/bd.h>
00050 #include <async.h>
00051 #include <as.h>
00052 #include <fibril_synch.h>
00053 #include <devmap.h>
00054 #include <sys/types.h>
00055 #include <sys/typefmt.h>
00056 #include <inttypes.h>
00057 #include <libblock.h>
00058 #include <devmap.h>
00059 #include <errno.h>
00060 #include <bool.h>
00061 #include <byteorder.h>
00062 #include <assert.h>
00063 #include <macros.h>
00064 #include <task.h>
00065 
00066 #include "gpt.h"
00067 
00068 #define NAME "guid_part"
00069 
00070 const uint8_t efi_signature[8] = {
00071         /* "EFI PART" in ASCII */
00072         0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
00073 };
00074 
00076 typedef struct part {
00078         bool present;
00080         aoff64_t start_addr;
00082         aoff64_t length;
00084         devmap_handle_t dev;
00086         struct part *next;
00087 } part_t;
00088 
00089 static size_t block_size;
00090 
00092 static devmap_handle_t indev_handle;
00093 
00095 static part_t plist_head;
00096 
00097 static int gpt_init(const char *dev_name);
00098 static int gpt_read(void);
00099 static part_t *gpt_part_new(void);
00100 static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part);
00101 static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall);
00102 static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf);
00103 static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf);
00104 static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
00105 
00106 int main(int argc, char **argv)
00107 {
00108         printf(NAME ": GUID partition table driver\n");
00109 
00110         if (argc != 2) {
00111                 printf("Expected one argument (device name).\n");
00112                 return -1;
00113         }
00114 
00115         if (gpt_init(argv[1]) != EOK)
00116                 return -1;
00117 
00118         printf(NAME ": Accepting connections\n");
00119         task_retval(0);
00120         async_manager();
00121 
00122         /* Not reached */
00123         return 0;
00124 }
00125 
00126 static int gpt_init(const char *dev_name)
00127 {
00128         int rc;
00129         int i;
00130         char *name;
00131         devmap_handle_t dev;
00132         uint64_t size_mb;
00133         part_t *part;
00134 
00135         rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
00136         if (rc != EOK) {
00137                 printf(NAME ": could not resolve device `%s'.\n", dev_name);
00138                 return rc;
00139         }
00140 
00141         rc = block_init(indev_handle, 2048);
00142         if (rc != EOK)  {
00143                 printf(NAME ": could not init libblock.\n");
00144                 return rc;
00145         }
00146 
00147         /* Determine and verify block size. */
00148 
00149         rc = block_get_bsize(indev_handle, &block_size);
00150         if (rc != EOK) {
00151                 printf(NAME ": error getting block size.\n");
00152                 return rc;
00153         }
00154 
00155         if (block_size < 512 || (block_size % 512) != 0) {
00156                 printf(NAME ": invalid block size %zu.\n", block_size);
00157                 return ENOTSUP;
00158         }
00159 
00160         /* Read in partition records. */
00161         rc = gpt_read();
00162         if (rc != EOK)
00163                 return rc;
00164 
00165         /* Register the driver with device mapper. */
00166         rc = devmap_driver_register(NAME, gpt_connection);
00167         if (rc != EOK) {
00168                 printf(NAME ": Unable to register driver.\n");
00169                 return rc;
00170         }
00171 
00172         /*
00173          * Create partition devices.
00174          */
00175         i = 0;
00176         part = plist_head.next;
00177 
00178         while (part != NULL) {
00179                 /* Skip absent partitions. */
00180                 if (!part->present) {
00181                         part = part->next;
00182                         ++i;
00183                         continue;
00184                 }
00185 
00186                 asprintf(&name, "%sp%d", dev_name, i);
00187                 if (name == NULL)
00188                         return ENOMEM;
00189 
00190                 rc = devmap_device_register(name, &dev);
00191                 if (rc != EOK) {
00192                         printf(NAME ": Unable to register device %s.\n", name);
00193                         return rc;
00194                 }
00195 
00196                 size_mb = (part->length * block_size + 1024 * 1024 - 1)
00197                     / (1024 * 1024);
00198                 printf(NAME ": Registered device %s: %" PRIu64 " blocks "
00199                     "%" PRIuOFF64 " MB.\n", name, part->length, size_mb);
00200 
00201                 part->dev = dev;
00202                 free(name);
00203 
00204                 part = part->next;
00205                 ++i;
00206         }
00207 
00208         return EOK;
00209 }
00210 
00212 static int gpt_read(void)
00213 {
00214         int i, rc;
00215         gpt_header_t *gpt_hdr;
00216         gpt_entry_t *etable;
00217         uint64_t ba;
00218         uint32_t bcnt;
00219         uint32_t esize;
00220         uint32_t num_entries;
00221         uint32_t entry;
00222         part_t *prev, *p;
00223 
00224         gpt_hdr = malloc(block_size);
00225         if (gpt_hdr == NULL) {
00226                 printf(NAME ": Failed allocating memory.\n");
00227                 return ENOMEM;
00228         }
00229 
00230         rc = block_read_direct(indev_handle, GPT_HDR_BA, 1, gpt_hdr);
00231         if (rc != EOK) {
00232                 printf(NAME ": Failed reading GPT header block.\n");
00233                 return rc;
00234         }
00235 
00236         for (i = 0; i < 8; ++i) {
00237                 if (gpt_hdr->efi_signature[i] != efi_signature[i]) {
00238                         printf(NAME ": Invalid GPT signature.\n");
00239                         return EINVAL;
00240                 }
00241         }
00242 
00243         plist_head.next = NULL;
00244         prev = &plist_head;
00245 
00246         num_entries = uint32_t_le2host(gpt_hdr->num_entries);
00247         ba = uint64_t_le2host(gpt_hdr->entry_lba);
00248         esize = uint32_t_le2host(gpt_hdr->entry_size);
00249         bcnt = (num_entries / esize) + ((num_entries % esize != 0) ? 1 : 0);
00250 
00251         etable = malloc(num_entries * esize);
00252         if (etable == NULL) {
00253                 free(gpt_hdr);
00254                 printf(NAME ": Failed allocating memory.\n");
00255                 return ENOMEM;
00256         }
00257 
00258         rc = block_read_direct(indev_handle, ba, bcnt, etable);
00259         if (rc != EOK) {
00260                 printf(NAME ": Failed reading GPT entries.\n");
00261                 return rc;
00262         }
00263 
00264         for (entry = 0; entry < num_entries; ++entry) {
00265                 p = gpt_part_new();
00266                 if (p == NULL)
00267                         return ENOMEM;
00268 
00269                 gpt_pte_to_part(&etable[entry], p);
00270                 prev->next = p;
00271                 prev = p;
00272         }
00273 
00274         free(etable);
00275 
00276         return EOK;
00277 }
00278 
00280 static part_t *gpt_part_new(void)
00281 {
00282         return malloc(sizeof(part_t));
00283 }
00284 
00286 static void gpt_pte_to_part(const gpt_entry_t *pte, part_t *part)
00287 {
00288         uint64_t sa, len;
00289         int i;
00290 
00291         /* Partition spans addresses [start_lba, end_lba] (inclusive). */
00292         sa = uint64_t_le2host(pte->start_lba);
00293         len = uint64_t_le2host(pte->end_lba) + 1 - sa;
00294 
00295         part->start_addr = sa;
00296         part->length     = len;
00297 
00298         part->present = false;
00299 
00300         for (i = 0; i < 8; ++i) {
00301                 if (pte->part_type[i] != 0x00)
00302                         part->present = true;
00303         }
00304 
00305         part->dev = 0;
00306         part->next = NULL;
00307 }
00308 
00309 static void gpt_connection(ipc_callid_t iid, ipc_call_t *icall)
00310 {
00311         size_t comm_size;
00312         void *fs_va = NULL;
00313         ipc_callid_t callid;
00314         ipc_call_t call;
00315         sysarg_t method;
00316         devmap_handle_t dh;
00317         unsigned int flags;
00318         int retval;
00319         aoff64_t ba;
00320         size_t cnt;
00321         part_t *part;
00322 
00323         /* Get the device handle. */
00324         dh = IPC_GET_ARG1(*icall);
00325 
00326         /* 
00327          * Determine which partition device is the client connecting to.
00328          * A linear search is not terribly fast, but we only do this
00329          * once for each connection.
00330          */
00331         part = plist_head.next;
00332         while (part != NULL && part->dev != dh)
00333                 part = part->next;
00334 
00335         if (part == NULL) {
00336                 async_answer_0(iid, EINVAL);
00337                 return;
00338         }
00339 
00340         assert(part->present == true);
00341 
00342         /* Answer the IPC_M_CONNECT_ME_TO call. */
00343         async_answer_0(iid, EOK);
00344 
00345         if (!async_share_out_receive(&callid, &comm_size, &flags)) {
00346                 async_answer_0(callid, EHANGUP);
00347                 return;
00348         }
00349 
00350         fs_va = as_get_mappable_page(comm_size);
00351         if (fs_va == NULL) {
00352                 async_answer_0(callid, EHANGUP);
00353                 return;
00354         }
00355 
00356         (void) async_share_out_finalize(callid, fs_va);
00357 
00358         while (1) {
00359                 callid = async_get_call(&call);
00360                 method = IPC_GET_IMETHOD(call);
00361                 switch (method) {
00362                 case IPC_M_PHONE_HUNGUP:
00363                         /* The other side has hung up. */
00364                         async_answer_0(callid, EOK);
00365                         return;
00366                 case BD_READ_BLOCKS:
00367                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00368                             IPC_GET_ARG2(call));
00369                         cnt = IPC_GET_ARG3(call);
00370                         if (cnt * block_size > comm_size) {
00371                                 retval = ELIMIT;
00372                                 break;
00373                         }
00374                         retval = gpt_bd_read(part, ba, cnt, fs_va);
00375                         break;
00376                 case BD_WRITE_BLOCKS:
00377                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00378                             IPC_GET_ARG2(call));
00379                         cnt = IPC_GET_ARG3(call);
00380                         if (cnt * block_size > comm_size) {
00381                                 retval = ELIMIT;
00382                                 break;
00383                         }
00384                         retval = gpt_bd_write(part, ba, cnt, fs_va);
00385                         break;
00386                 case BD_GET_BLOCK_SIZE:
00387                         async_answer_1(callid, EOK, block_size);
00388                         continue;
00389                 case BD_GET_NUM_BLOCKS:
00390                         async_answer_2(callid, EOK, LOWER32(part->length),
00391                             UPPER32(part->length));
00392                         continue;
00393                 default:
00394                         retval = EINVAL;
00395                         break;
00396                 }
00397                 async_answer_0(callid, retval);
00398         }
00399 }
00400 
00402 static int gpt_bd_read(part_t *p, aoff64_t ba, size_t cnt, void *buf)
00403 {
00404         aoff64_t gba;
00405 
00406         if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
00407                 return ELIMIT;
00408 
00409         return block_read_direct(indev_handle, gba, cnt, buf);
00410 }
00411 
00413 static int gpt_bd_write(part_t *p, aoff64_t ba, size_t cnt, const void *buf)
00414 {
00415         aoff64_t gba;
00416 
00417         if (gpt_bsa_translate(p, ba, cnt, &gba) != EOK)
00418                 return ELIMIT;
00419 
00420         return block_write_direct(indev_handle, gba, cnt, buf);
00421 }
00422 
00424 static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba)
00425 {
00426         if (ba + cnt > p->length)
00427                 return ELIMIT;
00428 
00429         *gba = p->start_addr + ba;
00430         return EOK;
00431 }
00432 

Generated on Thu Jun 2 07:45:49 2011 for HelenOS/USB by  doxygen 1.4.7