mbr_part.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 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 
00056 #include <stdio.h>
00057 #include <stdlib.h>
00058 #include <unistd.h>
00059 #include <ipc/bd.h>
00060 #include <async.h>
00061 #include <as.h>
00062 #include <fibril_synch.h>
00063 #include <devmap.h>
00064 #include <sys/types.h>
00065 #include <sys/typefmt.h>
00066 #include <inttypes.h>
00067 #include <libblock.h>
00068 #include <devmap.h>
00069 #include <errno.h>
00070 #include <bool.h>
00071 #include <byteorder.h>
00072 #include <assert.h>
00073 #include <macros.h>
00074 #include <task.h>
00075 
00076 #define NAME "mbr_part"
00077 
00078 enum {
00080         N_PRIMARY       = 4,
00081 
00083         BR_SIGNATURE    = 0xAA55
00084 };
00085 
00086 enum ptype {
00088         PT_UNUSED       = 0x00,
00090         PT_EXTENDED     = 0x05,
00091 };
00092 
00094 typedef struct part {
00096         bool present;
00098         aoff64_t start_addr;
00100         aoff64_t length;
00102         devmap_handle_t dev;
00104         struct part *next;
00105 } part_t;
00106 
00108 typedef struct {
00109         uint8_t status;
00111         uint8_t first_chs[3];
00113         uint8_t ptype;
00115         uint8_t last_chs[3];
00117         uint32_t first_lba;
00119         uint32_t length;
00120 } __attribute__((packed)) pt_entry_t;
00121 
00123 typedef struct {
00124         /* Area for boot code */
00125         uint8_t code_area[440];
00126 
00127         /* Optional media ID */
00128         uint32_t media_id;
00129 
00130         uint16_t pad0;
00131 
00133         pt_entry_t pte[N_PRIMARY];
00134 
00136         uint16_t signature;
00137 } __attribute__((packed)) br_block_t;
00138 
00139 
00140 static size_t block_size;
00141 
00143 static devmap_handle_t indev_handle;
00144 
00146 static part_t plist_head;
00147 
00148 static int mbr_init(const char *dev_name);
00149 static int mbr_part_read(void);
00150 static part_t *mbr_part_new(void);
00151 static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part);
00152 static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall);
00153 static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf);
00154 static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf);
00155 static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
00156 
00157 int main(int argc, char **argv)
00158 {
00159         printf(NAME ": PC MBR partition driver\n");
00160 
00161         if (argc != 2) {
00162                 printf("Expected one argument (device name).\n");
00163                 return -1;
00164         }
00165 
00166         if (mbr_init(argv[1]) != EOK)
00167                 return -1;
00168 
00169         printf(NAME ": Accepting connections\n");
00170         task_retval(0);
00171         async_manager();
00172 
00173         /* Not reached */
00174         return 0;
00175 }
00176 
00177 static int mbr_init(const char *dev_name)
00178 {
00179         int rc;
00180         int i;
00181         char *name;
00182         devmap_handle_t dev;
00183         uint64_t size_mb;
00184         part_t *part;
00185 
00186         rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
00187         if (rc != EOK) {
00188                 printf(NAME ": could not resolve device `%s'.\n", dev_name);
00189                 return rc;
00190         }
00191 
00192         rc = block_init(indev_handle, 2048);
00193         if (rc != EOK)  {
00194                 printf(NAME ": could not init libblock.\n");
00195                 return rc;
00196         }
00197 
00198         /* Determine and verify block size. */
00199 
00200         rc = block_get_bsize(indev_handle, &block_size);
00201         if (rc != EOK) {
00202                 printf(NAME ": error getting block size.\n");
00203                 return rc;
00204         }
00205 
00206         if (block_size < 512 || (block_size % 512) != 0) {
00207                 printf(NAME ": invalid block size %zu.\n", block_size);
00208                 return ENOTSUP;
00209         }
00210 
00211         /* Read in partition records. */
00212         rc = mbr_part_read();
00213         if (rc != EOK)
00214                 return rc;
00215 
00216         /* Register the driver with device mapper. */
00217         rc = devmap_driver_register(NAME, mbr_connection);
00218         if (rc != EOK) {
00219                 printf(NAME ": Unable to register driver.\n");
00220                 return rc;
00221         }
00222 
00223         /*
00224          * Create partition devices.
00225          */
00226         i = 0;
00227         part = plist_head.next;
00228 
00229         while (part != NULL) {
00230                 /* Skip absent partitions. */
00231                 if (!part->present) {
00232                         part = part->next;
00233                         ++i;
00234                         continue;
00235                 }
00236 
00237                 asprintf(&name, "%sp%d", dev_name, i);
00238                 if (name == NULL)
00239                         return ENOMEM;
00240 
00241                 rc = devmap_device_register(name, &dev);
00242                 if (rc != EOK) {
00243                         printf(NAME ": Unable to register device %s.\n", name);
00244                         return rc;
00245                 }
00246 
00247                 size_mb = (part->length * block_size + 1024 * 1024 - 1)
00248                     / (1024 * 1024);
00249                 printf(NAME ": Registered device %s: %" PRIuOFF64 " blocks "
00250                     "%" PRIu64 " MB.\n", name, part->length, size_mb);
00251 
00252                 part->dev = dev;
00253                 free(name);
00254 
00255                 part = part->next;
00256                 ++i;
00257         }
00258 
00259         return EOK;
00260 }
00261 
00263 static int mbr_part_read(void)
00264 {
00265         int i, rc;
00266         br_block_t *brb;
00267         uint16_t sgn;
00268         uint32_t ba;
00269         part_t *ext_part, cp;
00270         uint32_t base;
00271         part_t *prev, *p;
00272 
00273         brb = malloc(sizeof(br_block_t));
00274         if (brb == NULL) {
00275                 printf(NAME ": Failed allocating memory.\n");
00276                 return ENOMEM;
00277         }
00278 
00279         /*
00280          * Read primary partition entries.
00281          */
00282 
00283         rc = block_read_direct(indev_handle, 0, 1, brb);
00284         if (rc != EOK) {
00285                 printf(NAME ": Failed reading MBR block.\n");
00286                 return rc;
00287         }
00288 
00289         sgn = uint16_t_le2host(brb->signature);
00290         if (sgn != BR_SIGNATURE) {
00291                 printf(NAME ": Invalid boot record signature 0x%04" PRIX16
00292                     ".\n", sgn);
00293                 return EINVAL;
00294         }
00295 
00296         ext_part = NULL;
00297         plist_head.next = NULL;
00298         prev = &plist_head;
00299 
00300         for (i = 0; i < N_PRIMARY; ++i) {
00301                 p = mbr_part_new();
00302                 if (p == NULL)
00303                         return ENOMEM;
00304 
00305                 mbr_pte_to_part(0, &brb->pte[i], p);
00306                 prev->next = p;
00307                 prev = p;
00308 
00309                 if (brb->pte[i].ptype == PT_EXTENDED) {
00310                         p->present = false;
00311                         ext_part = p;
00312                 }
00313         }
00314 
00315         if (ext_part == NULL)
00316                 return EOK;
00317 
00318         printf("Extended partition found.\n");
00319 
00320         /*
00321          * Read extended partition entries.
00322          */
00323 
00324         cp.start_addr = ext_part->start_addr;
00325         cp.length = ext_part->length;
00326         base = ext_part->start_addr;
00327 
00328         do {
00329                 /*
00330                  * Addressing in the EBR chain is relative to the beginning
00331                  * of the extended partition.
00332                  */
00333                 ba = cp.start_addr;
00334                 rc = block_read_direct(indev_handle, ba, 1, brb);
00335                 if (rc != EOK) {
00336                         printf(NAME ": Failed reading EBR block at %"
00337                             PRIu32 ".\n", ba);
00338                         return rc;
00339                 }
00340 
00341                 sgn = uint16_t_le2host(brb->signature);
00342                 if (sgn != BR_SIGNATURE) {
00343                         printf(NAME ": Invalid boot record signature 0x%04"
00344                             PRIX16 " in EBR at %" PRIu32 ".\n", sgn, ba);
00345                         return EINVAL;
00346                 }
00347 
00348                 p = mbr_part_new();
00349                 if (p == NULL)
00350                         return ENOMEM;
00351 
00352                 /* First PTE is the logical partition itself. */
00353                 mbr_pte_to_part(base, &brb->pte[0], p);
00354                 prev->next = p;
00355                 prev = p;
00356 
00357                 /* Second PTE describes next chain element. */
00358                 mbr_pte_to_part(base, &brb->pte[1], &cp);
00359         } while (cp.present);
00360 
00361         return EOK;
00362 }
00363 
00365 static part_t *mbr_part_new(void)
00366 {
00367         return malloc(sizeof(part_t));
00368 }
00369 
00371 static void mbr_pte_to_part(uint32_t base, const pt_entry_t *pte, part_t *part)
00372 {
00373         uint32_t sa, len;
00374 
00375         sa = uint32_t_le2host(pte->first_lba);
00376         len = uint32_t_le2host(pte->length);
00377 
00378         part->start_addr = base + sa;
00379         part->length     = len;
00380 
00381         part->present = (pte->ptype != PT_UNUSED) ? true : false;
00382 
00383         part->dev = 0;
00384         part->next = NULL;
00385 }
00386 
00387 static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall)
00388 {
00389         size_t comm_size;
00390         void *fs_va = NULL;
00391         ipc_callid_t callid;
00392         ipc_call_t call;
00393         sysarg_t method;
00394         devmap_handle_t dh;
00395         unsigned int flags;
00396         int retval;
00397         uint64_t ba;
00398         size_t cnt;
00399         part_t *part;
00400 
00401         /* Get the device handle. */
00402         dh = IPC_GET_ARG1(*icall);
00403 
00404         /* 
00405          * Determine which partition device is the client connecting to.
00406          * A linear search is not terribly fast, but we only do this
00407          * once for each connection.
00408          */
00409         part = plist_head.next;
00410         while (part != NULL && part->dev != dh)
00411                 part = part->next;
00412 
00413         if (part == NULL) {
00414                 async_answer_0(iid, EINVAL);
00415                 return;
00416         }
00417 
00418         assert(part->present == true);
00419 
00420         /* Answer the IPC_M_CONNECT_ME_TO call. */
00421         async_answer_0(iid, EOK);
00422 
00423         if (!async_share_out_receive(&callid, &comm_size, &flags)) {
00424                 async_answer_0(callid, EHANGUP);
00425                 return;
00426         }
00427 
00428         fs_va = as_get_mappable_page(comm_size);
00429         if (fs_va == NULL) {
00430                 async_answer_0(callid, EHANGUP);
00431                 return;
00432         }
00433 
00434         (void) async_share_out_finalize(callid, fs_va);
00435 
00436         while (1) {
00437                 callid = async_get_call(&call);
00438                 method = IPC_GET_IMETHOD(call);
00439                 switch (method) {
00440                 case IPC_M_PHONE_HUNGUP:
00441                         /* The other side has hung up. */
00442                         async_answer_0(callid, EOK);
00443                         return;
00444                 case BD_READ_BLOCKS:
00445                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00446                             IPC_GET_ARG2(call));
00447                         cnt = IPC_GET_ARG3(call);
00448                         if (cnt * block_size > comm_size) {
00449                                 retval = ELIMIT;
00450                                 break;
00451                         }
00452                         retval = mbr_bd_read(part, ba, cnt, fs_va);
00453                         break;
00454                 case BD_WRITE_BLOCKS:
00455                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00456                             IPC_GET_ARG2(call));
00457                         cnt = IPC_GET_ARG3(call);
00458                         if (cnt * block_size > comm_size) {
00459                                 retval = ELIMIT;
00460                                 break;
00461                         }
00462                         retval = mbr_bd_write(part, ba, cnt, fs_va);
00463                         break;
00464                 case BD_GET_BLOCK_SIZE:
00465                         async_answer_1(callid, EOK, block_size);
00466                         continue;
00467                 case BD_GET_NUM_BLOCKS:
00468                         async_answer_2(callid, EOK, LOWER32(part->length),
00469                             UPPER32(part->length));
00470                         continue;
00471                 default:
00472                         retval = EINVAL;
00473                         break;
00474                 }
00475                 async_answer_0(callid, retval);
00476         }
00477 }
00478 
00480 static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
00481 {
00482         uint64_t gba;
00483 
00484         if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
00485                 return ELIMIT;
00486 
00487         return block_read_direct(indev_handle, gba, cnt, buf);
00488 }
00489 
00491 static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
00492 {
00493         uint64_t gba;
00494 
00495         if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
00496                 return ELIMIT;
00497 
00498         return block_write_direct(indev_handle, gba, cnt, buf);
00499 }
00500 
00502 static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
00503 {
00504         if (ba + cnt > p->length)
00505                 return ELIMIT;
00506 
00507         *gba = p->start_addr + ba;
00508         return EOK;
00509 }
00510 

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