1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/param.h>
  28 #include <sys/controlregs.h>
  29 #include <sys/bootconf.h>
  30 #include <sys/bootvfs.h>
  31 #include <sys/bootregs.h>
  32 #include <sys/bootconf.h>
  33 #include <sys/conf.h>
  34 #include <sys/promif.h>
  35 #include <sys/ddi.h>
  36 #include <sys/sunddi.h>
  37 #include <sys/sunndi.h>
  38 #include <sys/biosdisk.h>
  39 #include <sys/psw.h>
  40 #if defined(__xpv)
  41 #include <sys/hypervisor.h>
  42 #endif
  43 
  44 extern int prom_debug;
  45 
  46 /* hard code realmode memory address for now */
  47 #define BIOS_RES_BUFFER_ADDR            0x7000
  48 
  49 #define BIOSDEV_NUM     8
  50 #define STARTING_DRVNUM 0x80
  51 #define FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF)
  52 #define FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
  53 
  54 #ifdef DEBUG
  55 int biosdebug = 0;
  56 #define dprintf(fmt) \
  57         if (biosdebug) \
  58                 prom_printf fmt
  59 #else
  60 #define dprintf(fmt)
  61 #endif
  62 
  63 biosdev_data_t biosdev_info[BIOSDEV_NUM]; /* from 0x80 to 0x87 */
  64 int dobiosdev = 1;
  65 
  66 
  67 static int bios_check_extension_present(uchar_t);
  68 static int get_dev_params(uchar_t);
  69 static int read_firstblock(uchar_t drivenum);
  70 static int drive_present(uchar_t drivenum);
  71 static void reset_disk(uchar_t drivenum);
  72 static int is_eltorito(uchar_t drivenum);
  73 
  74 #if !defined(__xpv)
  75 void
  76 startup_bios_disk()
  77 {
  78         uchar_t drivenum;
  79         int got_devparams = 0;
  80         int got_first_block = 0;
  81         uchar_t name[20];
  82         dev_info_t      *devi;
  83         int extensions;
  84 
  85         if (dobiosdev == 0)
  86                 return;
  87 
  88         for (drivenum = 0x80; drivenum < (0x80 + BIOSDEV_NUM); drivenum++) {
  89 
  90                 if (!drive_present(drivenum))
  91                         continue;
  92 
  93                 extensions = bios_check_extension_present(drivenum);
  94 
  95                 /*
  96                  * If we're booting from an Eltorito CD/DVD image, there's
  97                  * no need to get the device parameters or read the first block
  98                  * because we'll never install onto this device.
  99                  */
 100                 if (extensions && is_eltorito(drivenum))
 101                         continue;
 102 
 103                 if (extensions && get_dev_params(drivenum))
 104                         got_devparams = 1;
 105                 else
 106                         got_devparams = 0;
 107 
 108                 if ((got_first_block = read_firstblock(drivenum)) == 0) {
 109                         /* retry */
 110                         got_first_block = read_firstblock(drivenum);
 111                 }
 112 
 113                 if (got_devparams || got_first_block) {
 114                         (void) sprintf((char *)name, "biosdev-0x%x", drivenum);
 115                         devi = ddi_root_node();
 116                         (void) e_ddi_prop_update_byte_array(DDI_DEV_T_NONE,
 117                             devi, (char *)name,
 118                             (uchar_t *)&biosdev_info[drivenum - 0x80],
 119                             sizeof (biosdev_data_t));
 120                 }
 121         }
 122 }
 123 #endif
 124 
 125 static int
 126 bios_check_extension_present(uchar_t drivenum)
 127 {
 128         struct bop_regs rp = {{0}};
 129         extern struct bootops           *bootops;
 130 
 131         rp.eax.word.ax = 0x4100;
 132         rp.ebx.word.bx = 0x55AA;
 133         rp.edx.word.dx = drivenum;
 134 
 135         /* make sure we have extension support */
 136         BOP_DOINT(bootops, 0x13, &rp);
 137 
 138         if (((rp.eflags & PS_C) != 0) || (rp.ebx.word.bx != 0xAA55)) {
 139                 dprintf(("bios_check_extension_present int13 fn 41 "
 140                     "failed %d bx = %x\n", rp.eflags, rp.ebx.word.bx));
 141                 return (0);
 142         }
 143 
 144         if ((rp.ecx.word.cx & 0x7) == 0) {
 145                 dprintf(("bios_check_extension_present get device parameters "
 146                     "not supported cx = %x\n", rp.ecx.word.cx));
 147                 return (0);
 148         }
 149 
 150         return (1);
 151 }
 152 
 153 static int
 154 get_dev_params(uchar_t drivenum)
 155 {
 156         struct bop_regs rp = {{0}};
 157         fn48_t   *bufp;
 158         extern struct bootops           *bootops;
 159         int i;
 160         int index;
 161         uchar_t *tmp;
 162 
 163         dprintf(("In get_dev_params\n"));
 164 
 165         bufp = (fn48_t *)BIOS_RES_BUFFER_ADDR;
 166 
 167         /*
 168          * We cannot use bzero here as we're initializing data
 169          * at an address below kernel base.
 170          */
 171         for (i = 0; i < sizeof (*bufp); i++)
 172                 ((uchar_t *)bufp)[i] = 0;
 173 
 174         bufp->buflen = sizeof (*bufp);
 175         rp.eax.word.ax = 0x4800;
 176         rp.edx.byte.dl = drivenum;
 177 
 178         rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
 179         rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
 180 
 181         BOP_DOINT(bootops, 0x13, &rp);
 182 
 183         if ((rp.eflags & PS_C) != 0) {
 184                 dprintf(("EDD FAILED on drive eflag = %x ah= %x\n",
 185                     rp.eflags, rp.eax.byte.ah));
 186                 return (0);
 187         }
 188 
 189         index = drivenum - 0x80;
 190         biosdev_info[index].edd_valid = 1;
 191 
 192         /*
 193          * Some compilers turn a structure copy into a call
 194          * to memcpy.  Since we are copying data below kernel
 195          * base intentionally, and memcpy asserts that's not
 196          * the case, we do the copy manually here.
 197          */
 198         tmp = (uchar_t *)&biosdev_info[index].fn48_dev_params;
 199         for (i = 0; i < sizeof (*bufp); i++)
 200                 tmp[i] = ((uchar_t *)bufp)[i];
 201 
 202         return (1);
 203 }
 204 
 205 static int
 206 drive_present(uchar_t drivenum)
 207 {
 208         struct bop_regs rp = {{0}};
 209 
 210         rp.eax.byte.ah = 0x8;   /* get params */
 211         rp.edx.byte.dl = drivenum;
 212 
 213         BOP_DOINT(bootops, 0x13, &rp);
 214 
 215         if (((rp.eflags & PS_C) != 0) || rp.eax.byte.ah != 0) {
 216                 dprintf(("drive not present drivenum %x eflag %x ah %x\n",
 217                     drivenum, rp.eflags, rp.eax.byte.ah));
 218                 return (0);
 219         }
 220 
 221         dprintf(("drive-present %x\n", drivenum));
 222         return (1);
 223 }
 224 
 225 static void
 226 reset_disk(uchar_t drivenum)
 227 {
 228         struct bop_regs rp = {{0}};
 229         int status;
 230 
 231         rp.eax.byte.ah = 0x0;   /* reset disk */
 232         rp.edx.byte.dl = drivenum;
 233 
 234         BOP_DOINT(bootops, 0x13, &rp);
 235 
 236         status = rp.eax.byte.ah;
 237 
 238         if (((rp.eflags & PS_C) != 0) || status != 0)
 239                 dprintf(("Bad disk reset driv %x, status %x\n", drivenum,
 240                     status));
 241 }
 242 
 243 /* Get first block */
 244 static int
 245 read_firstblock(uchar_t drivenum)
 246 {
 247 
 248         struct bop_regs rp = {{0}};
 249         caddr_t  bufp;
 250         uchar_t status;
 251         int i, index;
 252 
 253 
 254         reset_disk(drivenum);
 255         bufp = (caddr_t)BIOS_RES_BUFFER_ADDR;
 256 
 257 
 258         rp.eax.byte.ah = 0x2;   /* Read disk */
 259         rp.eax.byte.al = 1;     /* nsect */
 260         rp.ecx.byte.ch = 0;     /* cyl & 0xff */
 261         rp.ecx.byte.cl = 1;     /* cyl >> 2 & 0xc0 (sector number) */
 262         rp.edx.byte.dh = 0;     /* head */
 263         rp.edx.byte.dl = drivenum;      /* drivenum */
 264 
 265         /* es:bx is buf address */
 266         rp.ebx.word.bx = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
 267         rp.es = FP_SEG((uint_t)(uintptr_t)bufp);
 268 
 269         BOP_DOINT(bootops, 0x13, &rp);
 270 
 271         status = rp.eax.byte.ah;
 272         if (((rp.eflags & PS_C) != 0) || status != 0) {
 273                 dprintf(("read_firstblock AH not clear %x \n", status));
 274                 return (0);
 275         }
 276 
 277         dprintf(("drivenum %x uid at 0x1b8 is %x\n", drivenum,
 278             *(uint32_t *)(bufp +0x1b8)));
 279 
 280         index = drivenum - 0x80;
 281 
 282         biosdev_info[index].first_block_valid = 1;
 283         for (i = 0; i < 512; i++)
 284                 biosdev_info[index].first_block[i] = *((uchar_t *)bufp + i);
 285 
 286         return (1);
 287 }
 288 
 289 static int
 290 is_eltorito(uchar_t drivenum)
 291 {
 292         struct bop_regs rp = {{0}};
 293         fn4b_t   *bufp;
 294         extern struct bootops           *bootops;
 295         int i;
 296 
 297         dprintf(("In is_eltorito\n"));
 298 
 299         bufp = (fn4b_t *)BIOS_RES_BUFFER_ADDR;
 300 
 301         /*
 302          * We cannot use bzero here as we're initializing data
 303          * at an address below kernel base.
 304          */
 305         for (i = 0; i < sizeof (*bufp); i++)
 306                 ((uchar_t *)bufp)[i] = 0;
 307 
 308         bufp->pkt_size = sizeof (*bufp);
 309         rp.eax.word.ax = 0x4b01;
 310         rp.edx.byte.dl = drivenum;
 311 
 312         rp.esi.word.si = (uint16_t)FP_OFF((uint_t)(uintptr_t)bufp);
 313         rp.ds = FP_SEG((uint_t)(uintptr_t)bufp);
 314 
 315         BOP_DOINT(bootops, 0x13, &rp);
 316 
 317         if ((rp.eflags & PS_C) != 0 || bufp->drivenum != drivenum) {
 318                 dprintf(("fn 0x4b01 FAILED on drive "
 319                     "eflags=%x ah=%x drivenum=%x\n",
 320                     rp.eflags, rp.eax.byte.ah, bufp->drivenum));
 321                 return (0);
 322         }
 323 
 324         if (prom_debug)
 325                 prom_printf("INT13 FN4B01 mtype => %x", bufp->boot_mtype);
 326 
 327         return (1);
 328 }