/*-1* Copyright (c) 1998 Michael Smith <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <stand.h>27#include <sys/param.h>28#include <sys/reboot.h>29#include <sys/linker.h>30#include <machine/bootinfo.h>31#include <machine/cpufunc.h>32#include <machine/metadata.h>33#include <machine/psl.h>34#include <machine/specialreg.h>35#include "bootstrap.h"36#include "modinfo.h"37#include "libi386.h"38#include "btxv86.h"3940#ifdef LOADER_GELI_SUPPORT41#include "geliboot.h"42#endif4344/*45* Check to see if this CPU supports long mode.46*/47static int48bi_checkcpu(void)49{50char *cpu_vendor;51int vendor[3];52int eflags;53unsigned int regs[4];5455/* Check for presence of "cpuid". */56eflags = read_eflags();57write_eflags(eflags ^ PSL_ID);58if (!((eflags ^ read_eflags()) & PSL_ID))59return (0);6061/* Fetch the vendor string. */62do_cpuid(0, regs);63vendor[0] = regs[1];64vendor[1] = regs[3];65vendor[2] = regs[2];66cpu_vendor = (char *)vendor;6768/* Check for vendors that support AMD features. */69if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 &&70strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 &&71strncmp(cpu_vendor, HYGON_VENDOR_ID, 12) != 0 &&72strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0)73return (0);7475/* Has to support AMD features. */76do_cpuid(0x80000000, regs);77if (!(regs[0] >= 0x80000001))78return (0);7980/* Check for long mode. */81do_cpuid(0x80000001, regs);82return (regs[3] & AMDID_LM);83}8485/*86* Load the information expected by an amd64 kernel.87*88* - The 'boothowto' argument is constructed89* - The 'bootdev' argument is constructed90* - The 'bootinfo' struct is constructed, and copied into the kernel space.91* - The kernel environment is copied into kernel space.92* - Module metadata are formatted and placed in kernel space.93*/94int95bi_load64(char *args, vm_offset_t *modulep,96vm_offset_t *kernendp, int add_smap)97{98struct preloaded_file *xp, *kfp;99struct i386_devdesc *rootdev;100struct file_metadata *md;101uint64_t kernend;102uint64_t envp;103uint64_t module;104uint64_t addr;105vm_offset_t size;106char *rootdevname;107int howto;108109if (!bi_checkcpu()) {110printf("CPU doesn't support long mode\n");111return (EINVAL);112}113114howto = bi_getboothowto(args);115116/*117* Allow the environment variable 'rootdev' to override the supplied device118* This should perhaps go to MI code and/or have $rootdev tested/set by119* MI code before launching the kernel.120*/121rootdevname = getenv("rootdev");122i386_getdev((void **)(&rootdev), rootdevname, NULL);123if (rootdev == NULL) { /* bad $rootdev/$currdev */124printf("can't determine root device\n");125return(EINVAL);126}127128/* Try reading the /etc/fstab file to select the root device */129getrootmount(devformat(&rootdev->dd));130131addr = 0;132/* find the last module in the chain */133for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {134if (addr < (xp->f_addr + xp->f_size))135addr = xp->f_addr + xp->f_size;136}137/* pad to a page boundary */138addr = md_align(addr);139140addr = build_font_module(addr);141142/* place the metadata before anything */143module = *modulep = addr;144145kfp = file_findfile(NULL, md_kerntype);146if (kfp == NULL)147panic("can't find kernel file");148kernend = 0; /* fill it in later */149file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);150file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);151file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);152file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module);153if (add_smap != 0)154bios_addsmapdata(kfp);155#ifdef LOADER_GELI_SUPPORT156geli_export_key_metadata(kfp);157#endif158bi_load_vbe_data(kfp);159160size = md_copymodules(0, true);161162/* copy our environment */163envp = md_align(addr + size);164addr = md_copyenv(envp);165166/* set kernend */167kernend = md_align(addr);168*kernendp = kernend;169170/* patch MODINFOMD_KERNEND */171md = file_findmetadata(kfp, MODINFOMD_KERNEND);172bcopy(&kernend, md->md_data, sizeof kernend);173174/* patch MODINFOMD_ENVP */175md = file_findmetadata(kfp, MODINFOMD_ENVP);176bcopy(&envp, md->md_data, sizeof envp);177178/* copy module list and metadata */179(void)md_copymodules(*modulep, true);180181return(0);182}183184185