123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2010 Free Software Foundation, Inc.
- *
- * GRUB 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 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/loader.h>
- #include <grub/file.h>
- #include <grub/err.h>
- #include <grub/device.h>
- #include <grub/disk.h>
- #include <grub/misc.h>
- #include <grub/types.h>
- #include <grub/partition.h>
- #include <grub/msdos_partition.h>
- #include <grub/scsi.h>
- #include <grub/dl.h>
- #include <grub/command.h>
- #include <grub/i18n.h>
- #include <grub/video.h>
- #include <grub/mm.h>
- #include <grub/cpu/relocator.h>
- #include <grub/extcmd.h>
- #include <grub/verify.h>
- GRUB_MOD_LICENSE ("GPLv3+");
- static grub_dl_t my_mod;
- static struct grub_relocator *rel;
- static grub_uint32_t eip = 0xffffffff;
- #define GRUB_PLAN9_TARGET 0x100000
- #define GRUB_PLAN9_ALIGN 4096
- #define GRUB_PLAN9_CONFIG_ADDR 0x001200
- #define GRUB_PLAN9_CONFIG_PATH_SIZE 0x000040
- #define GRUB_PLAN9_CONFIG_MAGIC "ZORT 0\r\n"
- static const struct grub_arg_option options[] =
- {
- {"map", 'm', GRUB_ARG_OPTION_REPEATABLE,
- /* TRANSLATORS: it's about guessing which GRUB disk
- is which Plan9 disk. If your language has no
- word "mapping" you can use another word which
- means that the GRUBDEVICE and PLAN9DEVICE are
- actually the same device, just named differently
- in OS and GRUB. */
- N_("Override guessed mapping of Plan9 devices."),
- N_("GRUBDEVICE=PLAN9DEVICE"),
- ARG_TYPE_STRING},
- {0, 0, 0, 0, 0, 0}
- };
- struct grub_plan9_header
- {
- grub_uint32_t magic;
- #define GRUB_PLAN9_MAGIC 0x1eb
- grub_uint32_t text_size;
- grub_uint32_t data_size;
- grub_uint32_t bss_size;
- grub_uint32_t sectiona;
- grub_uint32_t entry_addr;
- grub_uint32_t zero;
- grub_uint32_t sectionb;
- };
- static grub_err_t
- grub_plan9_boot (void)
- {
- struct grub_relocator32_state state = {
- .eax = 0,
- .eip = eip,
- .ebx = 0,
- .ecx = 0,
- .edx = 0,
- .edi = 0,
- .esp = 0,
- .ebp = 0,
- .esi = 0
- };
- grub_video_set_mode ("text", 0, 0);
- return grub_relocator32_boot (rel, state, 0);
- }
- static grub_err_t
- grub_plan9_unload (void)
- {
- grub_relocator_unload (rel);
- rel = NULL;
- grub_dl_unref (my_mod);
- return GRUB_ERR_NONE;
- }
- /* Context for grub_cmd_plan9. */
- struct grub_cmd_plan9_ctx
- {
- grub_extcmd_context_t ctxt;
- grub_file_t file;
- char *pmap;
- grub_size_t pmapalloc;
- grub_size_t pmapptr;
- int noslash;
- int prefixescnt[5];
- char *bootdisk, *bootpart;
- };
- static const char prefixes[5][10] = {
- "dos", "plan9", "ntfs", "linux", "linuxswap"
- };
- #include <grub/err.h>
- static inline grub_err_t
- grub_extend_alloc (grub_size_t sz, grub_size_t *allocated, char **ptr)
- {
- void *n;
- if (sz < *allocated)
- return GRUB_ERR_NONE;
- *allocated = 2 * sz;
- n = grub_realloc (*ptr, *allocated);
- if (!n)
- return grub_errno;
- *ptr = n;
- return GRUB_ERR_NONE;
- }
- /* Helper for grub_cmd_plan9. */
- static int
- fill_partition (grub_disk_t disk, const grub_partition_t partition, void *data)
- {
- struct grub_cmd_plan9_ctx *fill_ctx = data;
- int file_disk = 0;
- int pstart, pend;
- if (!fill_ctx->noslash)
- {
- if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
- &fill_ctx->pmap))
- return 1;
- fill_ctx->pmap[fill_ctx->pmapptr++] = '/';
- }
- fill_ctx->noslash = 0;
- file_disk = fill_ctx->file->device->disk
- && disk->id == fill_ctx->file->device->disk->id
- && disk->dev->id == fill_ctx->file->device->disk->dev->id;
- pstart = fill_ctx->pmapptr;
- if (grub_strcmp (partition->partmap->name, "plan") == 0)
- {
- unsigned ptr = partition->index + sizeof ("part ") - 1;
- grub_err_t err;
- disk->partition = partition->parent;
- do
- {
- if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
- &fill_ctx->pmap))
- return 1;
- err = grub_disk_read (disk, 1, ptr, 1,
- fill_ctx->pmap + fill_ctx->pmapptr);
- if (err)
- {
- disk->partition = 0;
- return err;
- }
- ptr++;
- fill_ctx->pmapptr++;
- }
- while (grub_isalpha (fill_ctx->pmap[fill_ctx->pmapptr - 1])
- || grub_isdigit (fill_ctx->pmap[fill_ctx->pmapptr - 1]));
- fill_ctx->pmapptr--;
- }
- else
- {
- char name[50];
- int c = 0;
- if (grub_strcmp (partition->partmap->name, "msdos") == 0)
- {
- switch (partition->msdostype)
- {
- case GRUB_PC_PARTITION_TYPE_PLAN9:
- c = 1;
- break;
- case GRUB_PC_PARTITION_TYPE_NTFS:
- c = 2;
- break;
- case GRUB_PC_PARTITION_TYPE_MINIX:
- case GRUB_PC_PARTITION_TYPE_LINUX_MINIX:
- case GRUB_PC_PARTITION_TYPE_EXT2FS:
- c = 3;
- break;
- case GRUB_PC_PARTITION_TYPE_LINUX_SWAP:
- c = 4;
- break;
- }
- }
- if (fill_ctx->prefixescnt[c] == 0)
- grub_strcpy (name, prefixes[c]);
- else
- grub_snprintf (name, sizeof (name), "%s.%d", prefixes[c],
- fill_ctx->prefixescnt[c]);
- fill_ctx->prefixescnt[c]++;
- if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (name) + 1,
- &fill_ctx->pmapalloc, &fill_ctx->pmap))
- return 1;
- grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, name);
- fill_ctx->pmapptr += grub_strlen (name);
- }
- pend = fill_ctx->pmapptr;
- if (grub_extend_alloc (fill_ctx->pmapptr + 2 + 25 + 5 + 25,
- &fill_ctx->pmapalloc, &fill_ctx->pmap))
- return 1;
- fill_ctx->pmap[fill_ctx->pmapptr++] = ' ';
- grub_snprintf (fill_ctx->pmap + fill_ctx->pmapptr, 25 + 5 + 25,
- "%" PRIuGRUB_UINT64_T " %" PRIuGRUB_UINT64_T,
- grub_partition_get_start (partition),
- grub_partition_get_start (partition)
- + grub_partition_get_len (partition));
- if (file_disk && grub_partition_get_start (partition)
- == grub_partition_get_start (fill_ctx->file->device->disk->partition)
- && grub_partition_get_len (partition)
- == grub_partition_get_len (fill_ctx->file->device->disk->partition))
- {
- grub_free (fill_ctx->bootpart);
- fill_ctx->bootpart = grub_strndup (fill_ctx->pmap + pstart,
- pend - pstart);
- }
- fill_ctx->pmapptr += grub_strlen (fill_ctx->pmap + fill_ctx->pmapptr);
- return 0;
- }
- /* Helper for grub_cmd_plan9. */
- static int
- fill_disk (const char *name, void *data)
- {
- struct grub_cmd_plan9_ctx *fill_ctx = data;
- grub_device_t dev;
- char *plan9name = NULL;
- unsigned i;
- int file_disk = 0;
- dev = grub_device_open (name);
- if (!dev)
- {
- grub_print_error ();
- return 0;
- }
- if (!dev->disk)
- {
- grub_device_close (dev);
- return 0;
- }
- file_disk = fill_ctx->file->device->disk
- && dev->disk->id == fill_ctx->file->device->disk->id
- && dev->disk->dev->id == fill_ctx->file->device->disk->dev->id;
- for (i = 0;
- fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i]; i++)
- if (grub_strncmp (name, fill_ctx->ctxt->state[0].args[i],
- grub_strlen (name)) == 0
- && fill_ctx->ctxt->state[0].args[i][grub_strlen (name)] == '=')
- break;
- if (fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i])
- plan9name = grub_strdup (fill_ctx->ctxt->state[0].args[i]
- + grub_strlen (name) + 1);
- else
- switch (dev->disk->dev->id)
- {
- case GRUB_DISK_DEVICE_BIOSDISK_ID:
- if (dev->disk->id & 0x80)
- plan9name = grub_xasprintf ("sdB%u",
- (unsigned) (dev->disk->id & 0x7f));
- else
- plan9name = grub_xasprintf ("fd%u",
- (unsigned) (dev->disk->id & 0x7f));
- break;
- /* Shouldn't happen as Plan9 doesn't work on these platforms. */
- case GRUB_DISK_DEVICE_OFDISK_ID:
- case GRUB_DISK_DEVICE_EFIDISK_ID:
- /* Plan9 doesn't see those. */
- default:
- /* Not sure how to handle those. */
- case GRUB_DISK_DEVICE_NAND_ID:
- if (!file_disk)
- {
- grub_device_close (dev);
- return 0;
- }
- /* if it's the disk the kernel is loaded from we need to name
- it nevertheless. */
- plan9name = grub_strdup ("sdZ0");
- break;
- case GRUB_DISK_DEVICE_ATA_ID:
- {
- unsigned unit;
- if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
- unit = 0;
- else
- unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1, 0, 0);
- plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
- }
- break;
- case GRUB_DISK_DEVICE_SCSI_ID:
- if (((dev->disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xff)
- == GRUB_SCSI_SUBSYSTEM_PATA)
- {
- unsigned unit;
- if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
- unit = 0;
- else
- unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1,
- 0, 0);
- plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
- break;
- }
- /* FIXME: how does Plan9 number controllers?
- We probably need save the SCSI devices and sort them */
- plan9name
- = grub_xasprintf ("sd0%u", (unsigned)
- ((dev->disk->id >> GRUB_SCSI_ID_BUS_SHIFT)
- & 0xf));
- break;
- }
- if (!plan9name)
- {
- grub_print_error ();
- grub_device_close (dev);
- return 0;
- }
- if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (plan9name)
- + sizeof ("part="), &fill_ctx->pmapalloc,
- &fill_ctx->pmap))
- {
- grub_free (plan9name);
- grub_device_close (dev);
- return 1;
- }
- grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, plan9name);
- fill_ctx->pmapptr += grub_strlen (plan9name);
- if (!file_disk)
- grub_free (plan9name);
- else
- {
- grub_free (fill_ctx->bootdisk);
- fill_ctx->bootdisk = plan9name;
- }
- grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, "part=");
- fill_ctx->pmapptr += sizeof ("part=") - 1;
- fill_ctx->noslash = 1;
- grub_memset (fill_ctx->prefixescnt, 0, sizeof (fill_ctx->prefixescnt));
- if (grub_partition_iterate (dev->disk, fill_partition, fill_ctx))
- {
- grub_device_close (dev);
- return 1;
- }
- if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
- &fill_ctx->pmap))
- {
- grub_device_close (dev);
- return 1;
- }
- fill_ctx->pmap[fill_ctx->pmapptr++] = '\n';
- grub_device_close (dev);
- return 0;
- }
- static grub_err_t
- grub_cmd_plan9 (grub_extcmd_context_t ctxt, int argc, char *argv[])
- {
- struct grub_cmd_plan9_ctx fill_ctx = {
- .ctxt = ctxt,
- .file = 0,
- .pmap = NULL,
- .pmapalloc = 256,
- .pmapptr = 0,
- .noslash = 1,
- .bootdisk = NULL,
- .bootpart = NULL
- };
- void *mem;
- grub_size_t memsize, padsize;
- struct grub_plan9_header hdr;
- char *config, *configptr;
- grub_size_t configsize;
- char *bootpath = NULL;
- if (argc == 0)
- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
- grub_dl_ref (my_mod);
- rel = grub_relocator_new ();
- if (!rel)
- goto fail;
- fill_ctx.file = grub_file_open (argv[0], GRUB_FILE_TYPE_PLAN9_KERNEL);
- if (! fill_ctx.file)
- goto fail;
- fill_ctx.pmap = grub_malloc (fill_ctx.pmapalloc);
- if (!fill_ctx.pmap)
- goto fail;
- if (grub_disk_dev_iterate (fill_disk, &fill_ctx))
- goto fail;
- if (grub_extend_alloc (fill_ctx.pmapptr + 1, &fill_ctx.pmapalloc,
- &fill_ctx.pmap))
- goto fail;
- fill_ctx.pmap[fill_ctx.pmapptr] = 0;
- {
- char *file_name = grub_strchr (argv[0], ')');
- if (file_name)
- file_name++;
- else
- file_name = argv[0];
- if (*file_name)
- file_name++;
- if (fill_ctx.bootpart)
- bootpath = grub_xasprintf ("%s!%s!%s", fill_ctx.bootdisk,
- fill_ctx.bootpart, file_name);
- else
- bootpath = grub_xasprintf ("%s!%s", fill_ctx.bootdisk, file_name);
- grub_free (fill_ctx.bootdisk);
- grub_free (fill_ctx.bootpart);
- }
- if (!bootpath)
- goto fail;
- if (grub_file_read (fill_ctx.file, &hdr,
- sizeof (hdr)) != (grub_ssize_t) sizeof (hdr))
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- goto fail;
- }
- if (grub_be_to_cpu32 (hdr.magic) != GRUB_PLAN9_MAGIC
- || hdr.zero)
- {
- grub_error (GRUB_ERR_BAD_OS, "unsupported Plan9");
- goto fail;
- }
- memsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
- GRUB_PLAN9_ALIGN);
- memsize += ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN);
- memsize += ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size), GRUB_PLAN9_ALIGN);
- eip = grub_be_to_cpu32 (hdr.entry_addr) & 0xfffffff;
- /* path */
- configsize = GRUB_PLAN9_CONFIG_PATH_SIZE;
- /* magic */
- configsize += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
- {
- int i;
- for (i = 1; i < argc; i++)
- configsize += grub_strlen (argv[i]) + 1;
- }
- configsize += (sizeof ("bootfile=") - 1) + grub_strlen (bootpath) + 1;
- configsize += fill_ctx.pmapptr;
- /* Terminating \0. */
- configsize++;
- {
- grub_relocator_chunk_t ch;
- grub_err_t err;
- err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_CONFIG_ADDR,
- configsize);
- if (err)
- goto fail;
- config = get_virtual_current_address (ch);
- }
- grub_memset (config, 0, GRUB_PLAN9_CONFIG_PATH_SIZE);
- grub_strncpy (config, bootpath, GRUB_PLAN9_CONFIG_PATH_SIZE - 1);
- configptr = config + GRUB_PLAN9_CONFIG_PATH_SIZE;
- grub_memcpy (configptr, GRUB_PLAN9_CONFIG_MAGIC,
- sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1);
- configptr += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
- configptr = grub_stpcpy (configptr, "bootfile=");
- configptr = grub_stpcpy (configptr, bootpath);
- *configptr++ = '\n';
- char *cmdline = configptr;
- {
- int i;
- for (i = 1; i < argc; i++)
- {
- configptr = grub_stpcpy (configptr, argv[i]);
- *configptr++ = '\n';
- }
- }
- {
- grub_err_t err;
- *configptr = '\0';
- err = grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
- if (err)
- goto fail;
- }
- configptr = grub_stpcpy (configptr, fill_ctx.pmap);
- {
- grub_relocator_chunk_t ch;
- grub_err_t err;
- err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_TARGET,
- memsize);
- if (err)
- goto fail;
- mem = get_virtual_current_address (ch);
- }
- {
- grub_uint8_t *ptr;
- ptr = mem;
- grub_memcpy (ptr, &hdr, sizeof (hdr));
- ptr += sizeof (hdr);
- if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.text_size))
- != (grub_ssize_t) grub_be_to_cpu32 (hdr.text_size))
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- goto fail;
- }
- ptr += grub_be_to_cpu32 (hdr.text_size);
- padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
- GRUB_PLAN9_ALIGN) - grub_be_to_cpu32 (hdr.text_size)
- - sizeof (hdr);
- grub_memset (ptr, 0, padsize);
- ptr += padsize;
- if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.data_size))
- != (grub_ssize_t) grub_be_to_cpu32 (hdr.data_size))
- {
- if (!grub_errno)
- grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
- argv[0]);
- goto fail;
- }
- ptr += grub_be_to_cpu32 (hdr.data_size);
- padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN)
- - grub_be_to_cpu32 (hdr.data_size);
- grub_memset (ptr, 0, padsize);
- ptr += padsize;
- grub_memset (ptr, 0, ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size),
- GRUB_PLAN9_ALIGN));
- }
- grub_loader_set (grub_plan9_boot, grub_plan9_unload, 1);
- return GRUB_ERR_NONE;
- fail:
- grub_free (fill_ctx.pmap);
- if (fill_ctx.file)
- grub_file_close (fill_ctx.file);
- grub_plan9_unload ();
- return grub_errno;
- }
- static grub_extcmd_t cmd;
- GRUB_MOD_INIT(plan9)
- {
- cmd = grub_register_extcmd ("plan9", grub_cmd_plan9,
- GRUB_COMMAND_OPTIONS_AT_START,
- N_("KERNEL ARGS"), N_("Load Plan9 kernel."),
- options);
- my_mod = mod;
- }
- GRUB_MOD_FINI(plan9)
- {
- grub_unregister_extcmd (cmd);
- }
|