Raphael S.Carvalho
2013-Oct-18 05:45 UTC
[syslinux] [RFC/PATCH 3/3] Wire up MultiFS support.
From: Raphael S. Carvalho <raphael.scarv at gmail.com> This patch finishes the MultiFS support. init_multifs gets called in the main (startup) function of ldlinux.c32, so MultiFS will be initialized automatically. init_multifs calls enable_multifs (lives in the core) to hook get_fs_info. Subsequent accesses will callback the get_fs_info living in ldlinux.c32. Signed-off-by: Raphael S. Carvalho <raphael.scarv at gmail.com> --- com32/elflink/ldlinux/ldlinux.c | 2 + com32/include/syslinux/multifs_utils.h | 51 +++++ com32/lib/syslinux/multifs_utils.c | 323 ++++++++++++++++++++++++++++++++ mk/lib.mk | 1 + 4 files changed, 377 insertions(+), 0 deletions(-) create mode 100644 com32/include/syslinux/multifs_utils.h create mode 100644 com32/lib/syslinux/multifs_utils.c diff --git a/com32/elflink/ldlinux/ldlinux.c b/com32/elflink/ldlinux/ldlinux.c index 76d117c..bc63a5a 100644 --- a/com32/elflink/ldlinux/ldlinux.c +++ b/com32/elflink/ldlinux/ldlinux.c @@ -303,6 +303,8 @@ __export int main(int argc __unused, char **argv) const char *cmdline; size_t count = 0; + init_multifs(); /* Init MultiFS support */ + ldlinux_console_init(); parse_configs(&argv[1]); diff --git a/com32/include/syslinux/multifs_utils.h b/com32/include/syslinux/multifs_utils.h new file mode 100644 index 0000000..59ba85a --- /dev/null +++ b/com32/include/syslinux/multifs_utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Andre Ericson <de.ericson at gmail.com> + * Copyright (C) 2012 Paulo Cavalcanti <pcacjr at zytor.com> + * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef MULTIDISK_UTILS_H +#define MULTIDISK_UTILS_H + +#include <syslinux/partiter.h> +#include "fs.h" + +struct part_node { + int partition; + struct fs_info *fs; + struct part_node *next; +}; + +struct queue_head { + struct part_node *first; + struct part_node *last; +}; + +/* + * Needs to keep ROOT_FS_OPS after fs_init() + * to be used by multidisk + */ +extern const struct fs_ops **p_ops; + +/* + * Used to initialize MultiFS support + */ +extern void enable_multifs(void *); +extern void init_multifs(void); + +#endif /* MULTIDISK_UTILS_H */ \ No newline at end of file diff --git a/com32/lib/syslinux/multifs_utils.c b/com32/lib/syslinux/multifs_utils.c new file mode 100644 index 0000000..692c8a8 --- /dev/null +++ b/com32/lib/syslinux/multifs_utils.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2012 Andre Ericson <de.ericson at gmail.com> + * Copyright (C) 2012 Paulo Cavalcanti <pcacjr at zytor.com> + * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include <syslinux/multifs_utils.h> +#include "core.h" +#include "disk.h" +#include "cache.h" +#include "minmax.h" + +/* 0x80 - 0xFF + * BIOS limitation */ +#define DISK_ID_OFFSET 0x80 +#define DISKS_MAX 128 + +/* MaxTransfer for MultiFS access */ +#define MAX_TRANSFER 127 + +static struct queue_head *parts_info[DISKS_MAX]; + +/* + * Store info about the FS into a specific queue to be used later. + * + * @ret: 0 on success, -1 on failure. + */ +static int add_fs(struct fs_info *fs, uint8_t disk, uint8_t partition) +{ + struct queue_head *head = parts_info[disk]; + struct part_node *node; + + node = malloc(sizeof(struct part_node)); + if (!node) + return -1; + node->fs = fs; + node->next = NULL; + node->partition = partition; + + if (!head) { + head = malloc(sizeof(struct queue_head)); + if (!head) { + free(node); + return -1; + } + head->first = head->last = node; + parts_info[disk] = head; + return 0; + } + head->last->next = node; + head->last = node; + return 0; +} + +/* + * Check if the FS was previously allocated. + * + * @ret: return the fs on success, NULL on failure. + */ +static struct fs_info *get_fs(uint8_t disk, uint8_t partition) +{ + struct part_node *i; + + for (i = parts_info[disk]->first; i; i = i->next) { + if (i->partition == partition) + return i->fs; + } + return NULL; +} + +/* + * Attempt to find a partition based on drive and partition numbers. + * + * @ret: 0 on success, -1 on failure. + */ +static int find_partition(struct part_iter **_iter, struct disk_info *diskinfo, + int partition) +{ + struct part_iter *iter = NULL; + + if (!(iter = pi_begin(diskinfo, 0))) + return -1; + + do { + if (iter->index == partition) + break; + } while (!pi_next(iter)); + + if (iter->status) { + dprintf("MultiFS: Request disk/partition combination not found.\n"); + goto bail; + } + dprintf("MultiFS: found 0x%llx at idex: %i and partition %i\n", + iter->abs_lba, iter->index, partition); + + *_iter = iter; + return 0; +bail: + pi_del(&iter); + return -1; +} + +/* + * Get a number till the delimiter is found. + * + * @ret: addr to delimiter+1 on success, NULL on failure. + */ +static const char *get_num(const char *p, char delimiter, uint8_t *data) +{ + uint32_t n = 0; + + while (*p) { + if (*p < '0' || *p > '9') + break; + + n = (n * 10) + (*p - '0'); + p++; + + if (*p == delimiter) { + p++; /* Skip delimiter */ + *data = min(n, UINT8_MAX); /* Avoid overflow */ + return p; + } + } + return NULL; +} + +/* + * Parse MultiFS path. Syntax: + * (hd[disk number]:[partition number])/path/to/file + * + * @ret: Returns syntax validity. + */ +static int parse_multifs_path(const char **path, uint8_t *hdd, + uint8_t *partition) +{ + const char *p = *path; + static const char *cwd = "."; + + *hdd = *partition = 0; + p++; /* Skip open parentheses */ + + /* Get hd number (Range: 0 - (DISKS_MAX - 1)) */ + if (*p != 'h' || *(p + 1) != 'd') + return -1; + + p += 2; /* Skip 'h' and 'd' */ + p = get_num(p, ',', hdd); + if (!p) + return -1; + if (*hdd >= DISKS_MAX) { + printf("MultiFS: hdd is out of range: 0-%d\n", DISKS_MAX - 1); + return -1; + } + + /* Get partition number (Range: 0 - 0xFF) */ + p = get_num(p, ')', partition); + if (!p) + return -1; + + if (*p == '\0') { + /* Assume it's a cwd request */ + p = cwd; + } + + *path = p; + dprintf("MultiFS: hdd: %u partition: %u path: %s\n", + *hdd, *partition, *path); + return 0; +} + +/* + * Set up private struct based on paramaters. + * This structure will be used later to set up a device + * to (disk x:partition y). + * + * @devno: Device number (range: 0 - (DISKS_MAX - 1)). + * @part_start: Start LBA. + * @bsHeads: Number of heads. + * @bsSecPerTrack: Sectors per track. + */ +static void *get_private(uint8_t devno, uint64_t part_start, + uint16_t bsHeads, uint16_t bsSecPerTrack) +{ + static com32sys_t regs; + static struct bios_disk_private priv; + + priv.regs = ®s; + + regs.edx.b[0] = devno; + regs.edx.b[1] = 0; // TODO: cdrom ... always 0??? + regs.ecx.l = part_start & 0xFFFFFFFF; + regs.ebx.l = part_start >> 32; + regs.esi.w[0] = bsHeads; + regs.edi.w[0] = bsSecPerTrack; + regs.ebp.l = MAX_TRANSFER; // TODO: should it be pre-defined??? + + return (void *) &priv; +} + +/* + * 1) Set up a new device based on the disk and the partition. + * 2) Find which file system is installed in this device. + * 3) Set up fs_info based on the fs, add to queue, and return it. + * 4) Subsequent accesses to the same disk and partition will get + * fs_info from the queue. + * + * It handles the important stuff to get the MultiFS support working. + */ +static struct fs_info *get_fs_info(const char **path) +{ + const struct fs_ops **ops; + struct fs_info *fsp; + struct disk_info diskinfo; + struct part_iter *iter = NULL; + struct device *dev = NULL; + void *private; + int blk_shift = -1; + uint8_t disk_devno, hdd, partition; + + if (parse_multifs_path(path, &hdd, &partition)) { + printf("MultiFS: Syntax invalid: %s\n", *path); + return NULL; + } + + fsp = get_fs(hdd, partition - 1); + if (fsp) + return fsp; + + fsp = malloc(sizeof(struct fs_info)); + if (!fsp) + return NULL; + + disk_devno = DISK_ID_OFFSET + hdd; + if (disk_get_params(disk_devno, &diskinfo)) + goto bail; + + if (find_partition(&iter, &diskinfo, partition)) { + printf("MultiFS: Failed to get disk/partition: %s\n", *path); + goto bail; + } + private = get_private(disk_devno, iter->abs_lba, diskinfo.head, + diskinfo.spt); + + /* Default name for the root directory */ + fsp->cwd_name[0] = '/'; + fsp->cwd_name[1] = '\0'; + + ops = p_ops; + while ((blk_shift < 0) && *ops) { + /* set up the fs stucture */ + fsp->fs_ops = *ops; + + /* + * This boldly assumes that we don't mix FS_NODEV filesystems + * with FS_DEV filesystems... + */ + if (fsp->fs_ops->fs_flags & FS_NODEV) { + fsp->fs_dev = NULL; + } else { + if (!dev) { + dev = device_init(private); + if (!dev) + goto bail; + } + fsp->fs_dev = dev; + } + /* invoke the fs-specific init code */ + blk_shift = fsp->fs_ops->fs_init(fsp); + ops++; + } + if (blk_shift < 0) { + printf("MultiFS: No valid file system found!\n"); + goto free_dev; + } + + /* add fs_info into hdd queue */ + if (add_fs(fsp, hdd, partition - 1)) + goto free_dev; + + /* initialize the cache */ + if (fsp->fs_dev && fsp->fs_dev->cache_data) + cache_init(fsp->fs_dev, blk_shift); + + /* start out in the root directory */ + if (fsp->fs_ops->iget_root) { + fsp->root = fsp->fs_ops->iget_root(fsp); + fsp->cwd = get_inode(fsp->root); + } + + return fsp; +free_dev: + free(dev->disk); + free(dev->cache_data); + free(dev); +bail: + free(fsp); + return NULL; +} + +/* + * Initialize MultiFS support + */ +void init_multifs(void) +{ + enable_multifs(&get_fs_info); + dprintf("MultiFS support was enabled successfully!\n"); +} \ No newline at end of file diff --git a/mk/lib.mk b/mk/lib.mk index 861daf8..a1d5896 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -107,6 +107,7 @@ LIBOTHER_OBJS = \ \ sys/x86_init_fpu.o math/pow.o math/strtod.o \ syslinux/disk.o syslinux/utility.o syslinux/partiter.o \ + syslinux/multifs_utils.o \ \ syslinux/setup_data.o -- 1.7.2.5