138465Smsmith/*- 238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438465Smsmith * SUCH DAMAGE. 2538465Smsmith */ 2638465Smsmith 27119482Sobrien#include <sys/cdefs.h> 28119482Sobrien__FBSDID("$FreeBSD$"); 29119482Sobrien 3039902Smsmith#include <stand.h> 3139902Smsmith#include <sys/param.h> 3238465Smsmith#include <sys/reboot.h> 3340146Speter#include <sys/linker.h> 3439902Smsmith#include <machine/bootinfo.h> 35183667Sjhb#include <machine/cpufunc.h> 36183667Sjhb#include <machine/psl.h> 37183667Sjhb#include <machine/specialreg.h> 3838465Smsmith#include "bootstrap.h" 3939902Smsmith#include "libi386.h" 4039902Smsmith#include "btxv86.h" 4138465Smsmith 4238465Smsmith/* 4338465Smsmith * Copy module-related data into the load area, where it can be 4438465Smsmith * used as a directory for loaded modules. 4538465Smsmith * 4638465Smsmith * Module data is presented in a self-describing format. Each datum 4772640Sasmodai * is preceded by a 32-bit identifier and a 32-bit size field. 4838465Smsmith * 4938465Smsmith * Currently, the following data are saved: 5038465Smsmith * 5138465Smsmith * MOD_NAME (variable) module name (string) 5238465Smsmith * MOD_TYPE (variable) module type (string) 5344572Sdcs * MOD_ARGS (variable) module parameters (string) 5438465Smsmith * MOD_ADDR sizeof(vm_offset_t) module load address 5538465Smsmith * MOD_SIZE sizeof(size_t) module size 5638465Smsmith * MOD_METADATA (variable) type-specific metadata 5738465Smsmith */ 58114379Speter#define COPY32(v, a, c) { \ 5940107Smsmith u_int32_t x = (v); \ 60114379Speter if (c) \ 61114379Speter i386_copyin(&x, a, sizeof(x)); \ 6240107Smsmith a += sizeof(x); \ 6338465Smsmith} 6438465Smsmith 65114379Speter#define MOD_STR(t, a, s, c) { \ 66114379Speter COPY32(t, a, c); \ 67114379Speter COPY32(strlen(s) + 1, a, c); \ 68114379Speter if (c) \ 69114379Speter i386_copyin(s, a, strlen(s) + 1); \ 70114379Speter a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ 7140107Smsmith} 7240107Smsmith 73114379Speter#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) 74114379Speter#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) 75114379Speter#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) 7638465Smsmith 77114379Speter#define MOD_VAR(t, a, s, c) { \ 78114379Speter COPY32(t, a, c); \ 79114379Speter COPY32(sizeof(s), a, c); \ 80114379Speter if (c) \ 81114379Speter i386_copyin(&s, a, sizeof(s)); \ 82114379Speter a += roundup(sizeof(s), sizeof(u_int64_t)); \ 8338465Smsmith} 8438465Smsmith 85114379Speter#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) 86114379Speter#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) 8738465Smsmith 88114379Speter#define MOD_METADATA(a, mm, c) { \ 89114379Speter COPY32(MODINFO_METADATA | mm->md_type, a, c); \ 90114379Speter COPY32(mm->md_size, a, c); \ 91114379Speter if (c) \ 92114379Speter i386_copyin(mm->md_data, a, mm->md_size); \ 93114379Speter a += roundup(mm->md_size, sizeof(u_int64_t));\ 9438465Smsmith} 9538465Smsmith 96114379Speter#define MOD_END(a, c) { \ 97114379Speter COPY32(MODINFO_END, a, c); \ 98114379Speter COPY32(0, a, c); \ 9939178Smsmith} 10039178Smsmith 101114379Speterstatic vm_offset_t 102114379Speterbi_copymodules64(vm_offset_t addr) 10338465Smsmith{ 10459854Sbp struct preloaded_file *fp; 10559854Sbp struct file_metadata *md; 106114379Speter int c; 107114379Speter u_int64_t v; 10838465Smsmith 109114379Speter c = addr != 0; 11038465Smsmith /* start with the first module on the list, should be the kernel */ 11159854Sbp for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { 11240146Speter 113114379Speter MOD_NAME(addr, fp->f_name, c); /* this field must come first */ 114114379Speter MOD_TYPE(addr, fp->f_type, c); 11559854Sbp if (fp->f_args) 116114379Speter MOD_ARGS(addr, fp->f_args, c); 117114379Speter v = fp->f_addr; 118114379Speter MOD_ADDR(addr, v, c); 119114379Speter v = fp->f_size; 120114379Speter MOD_SIZE(addr, v, c); 12159854Sbp for (md = fp->f_metadata; md != NULL; md = md->md_next) 12238764Smsmith if (!(md->md_type & MODINFOMD_NOCOPY)) 123114379Speter MOD_METADATA(addr, md, c); 12438465Smsmith } 125114379Speter MOD_END(addr, c); 12638465Smsmith return(addr); 12738465Smsmith} 12839902Smsmith 12939902Smsmith/* 130183667Sjhb * Check to see if this CPU supports long mode. 131183667Sjhb */ 132183667Sjhbstatic int 133183667Sjhbbi_checkcpu(void) 134183667Sjhb{ 135183667Sjhb char *cpu_vendor; 136183667Sjhb int vendor[3]; 137183667Sjhb int eflags, regs[4]; 138183667Sjhb 139183667Sjhb /* Check for presence of "cpuid". */ 140183667Sjhb eflags = read_eflags(); 141183667Sjhb write_eflags(eflags ^ PSL_ID); 142183667Sjhb if (!((eflags ^ read_eflags()) & PSL_ID)) 143183667Sjhb return (0); 144183667Sjhb 145183667Sjhb /* Fetch the vendor string. */ 146183667Sjhb do_cpuid(0, regs); 147183667Sjhb vendor[0] = regs[1]; 148183667Sjhb vendor[1] = regs[3]; 149183667Sjhb vendor[2] = regs[2]; 150183667Sjhb cpu_vendor = (char *)vendor; 151183667Sjhb 152183667Sjhb /* Check for vendors that support AMD features. */ 153187101Sjkim if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 && 154187101Sjkim strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 && 155187101Sjkim strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0) 156183667Sjhb return (0); 157183667Sjhb 158183667Sjhb /* Has to support AMD features. */ 159183667Sjhb do_cpuid(0x80000000, regs); 160183667Sjhb if (!(regs[0] >= 0x80000001)) 161183667Sjhb return (0); 162183667Sjhb 163183667Sjhb /* Check for long mode. */ 164183667Sjhb do_cpuid(0x80000001, regs); 165183667Sjhb return (regs[3] & AMDID_LM); 166183667Sjhb} 167183667Sjhb 168183667Sjhb/* 169183667Sjhb * Load the information expected by an amd64 kernel. 17039902Smsmith * 17139902Smsmith * - The 'boothowto' argument is constructed 17264187Sjhb * - The 'bootdev' argument is constructed 17339902Smsmith * - The 'bootinfo' struct is constructed, and copied into the kernel space. 17439902Smsmith * - The kernel environment is copied into kernel space. 17539902Smsmith * - Module metadata are formatted and placed in kernel space. 17639902Smsmith */ 17739902Smsmithint 178114379Speterbi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) 17939902Smsmith{ 180114379Speter struct preloaded_file *xp, *kfp; 18139902Smsmith struct i386_devdesc *rootdev; 182114379Speter struct file_metadata *md; 18364187Sjhb vm_offset_t addr; 184114379Speter u_int64_t kernend; 185114379Speter u_int64_t envp; 186114379Speter vm_offset_t size; 18739902Smsmith char *rootdevname; 188162814Sru int howto; 18939902Smsmith 190183667Sjhb if (!bi_checkcpu()) { 191183667Sjhb printf("CPU doesn't support long mode\n"); 192183667Sjhb return (EINVAL); 193183667Sjhb } 194183667Sjhb 195114379Speter howto = bi_getboothowto(args); 19639902Smsmith 19739902Smsmith /* 19839902Smsmith * Allow the environment variable 'rootdev' to override the supplied device 19939902Smsmith * This should perhaps go to MI code and/or have $rootdev tested/set by 20039902Smsmith * MI code before launching the kernel. 20139902Smsmith */ 20239902Smsmith rootdevname = getenv("rootdev"); 20339902Smsmith i386_getdev((void **)(&rootdev), rootdevname, NULL); 20439902Smsmith if (rootdev == NULL) { /* bad $rootdev/$currdev */ 20539902Smsmith printf("can't determine root device\n"); 20639902Smsmith return(EINVAL); 20739902Smsmith } 20841139Smsmith 20948952Smsmith /* Try reading the /etc/fstab file to select the root device */ 21048952Smsmith getrootmount(i386_fmtdev((void *)rootdev)); 21148952Smsmith 21239902Smsmith /* find the last module in the chain */ 21340393Speter addr = 0; 21459854Sbp for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { 21559854Sbp if (addr < (xp->f_addr + xp->f_size)) 21659854Sbp addr = xp->f_addr + xp->f_size; 21740393Speter } 21839902Smsmith /* pad to a page boundary */ 219114379Speter addr = roundup(addr, PAGE_SIZE); 22039902Smsmith 22139902Smsmith /* copy our environment */ 222114379Speter envp = addr; 22339902Smsmith addr = bi_copyenv(addr); 22439902Smsmith 22539902Smsmith /* pad to a page boundary */ 226114379Speter addr = roundup(addr, PAGE_SIZE); 22739902Smsmith 228114385Speter kfp = file_findfile(NULL, "elf kernel"); 229114379Speter if (kfp == NULL) 230114385Speter kfp = file_findfile(NULL, "elf64 kernel"); 231114379Speter if (kfp == NULL) 232114379Speter panic("can't find kernel file"); 233114379Speter kernend = 0; /* fill it in later */ 234114379Speter file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); 235114379Speter file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); 236114379Speter file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); 237114379Speter bios_addsmapdata(kfp); 23839902Smsmith 239114379Speter /* Figure out the size and location of the metadata */ 240114379Speter *modulep = addr; 241114379Speter size = bi_copymodules64(0); 242114379Speter kernend = roundup(addr + size, PAGE_SIZE); 243114379Speter *kernendp = kernend; 24439902Smsmith 245114379Speter /* patch MODINFOMD_KERNEND */ 246114379Speter md = file_findmetadata(kfp, MODINFOMD_KERNEND); 247114379Speter bcopy(&kernend, md->md_data, sizeof kernend); 24839902Smsmith 249114379Speter /* copy module list and metadata */ 250114379Speter (void)bi_copymodules64(addr); 251114379Speter 25239902Smsmith return(0); 25339902Smsmith} 254