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