1998-08-21 03:17:42 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
|
2012-08-05 14:37:48 +00:00
|
|
|
* Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
|
1998-08-21 03:17:42 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2003-08-25 23:28:32 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
1998-08-21 03:17:42 +00:00
|
|
|
/*
|
|
|
|
* BIOS disk device handling.
|
2018-08-05 18:19:40 +00:00
|
|
|
*
|
1998-08-21 03:17:42 +00:00
|
|
|
* Ideas and algorithms from:
|
|
|
|
*
|
|
|
|
* - NetBSD libi386/biosdisk.c
|
|
|
|
* - FreeBSD biosboot/disk.c
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
#include <sys/disk.h>
|
loader: biosdisk fix for 2+TB disks
This fix is implementing partition based boundary check for
disk IO and updates disk mediasize (if needed), based on information
from partition table.
As it appeared, the signed int based approach still has corner cases,
and the wrapover based behavior is non-standard.
The idea for this fix is based on two assumptions:
The bug about media size is hitting large (2+TB) disks, lesser disks
hopefully, are not affected.
Large disks are using GPT (which does include information about disk size).
Since our concern is about boot support and boot disks are partitioned,
implementing partition boundaries based IO verification should make the
media size issues mostly disappear.
However, for large disk case, we do have the disk size available from GPT table.
If non-GPT cases will appear, we still can make approximate calculation about
disk size based on defined partition(s), however, this is not the objective
of this patch, and can be added later if there is any need.
This patch does implement disk media size adjustment (if needed) in bd_open(),
and boundary check in bd_realstrategy().
Reviewed by: allanjude
Approved by: allanjude (mentor)
Differential Revision: https://reviews.freebsd.org/D8595
2017-02-06 18:29:43 +00:00
|
|
|
#include <sys/limits.h>
|
2018-11-30 08:01:11 +00:00
|
|
|
#include <sys/queue.h>
|
1998-08-21 03:17:42 +00:00
|
|
|
#include <stand.h>
|
2003-04-04 16:35:16 +00:00
|
|
|
#include <machine/bootinfo.h>
|
1998-10-11 10:29:49 +00:00
|
|
|
#include <stdarg.h>
|
2018-11-30 08:01:11 +00:00
|
|
|
#include <stdbool.h>
|
1998-10-11 10:29:49 +00:00
|
|
|
|
1998-09-17 23:52:16 +00:00
|
|
|
#include <bootstrap.h>
|
|
|
|
#include <btxv86.h>
|
2011-10-25 19:54:06 +00:00
|
|
|
#include <edd.h>
|
2012-08-05 14:37:48 +00:00
|
|
|
#include "disk.h"
|
1998-08-21 03:17:42 +00:00
|
|
|
#include "libi386.h"
|
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
#define BIOS_NUMDRIVES 0x475
|
|
|
|
#define BIOSDISK_SECSIZE 512
|
|
|
|
#define BUFSIZE (1 * BIOSDISK_SECSIZE)
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
|
|
|
|
#define WDMAJOR 0 /* major numbers for devices we frontend for */
|
|
|
|
#define WFDMAJOR 1
|
|
|
|
#define FDMAJOR 2
|
|
|
|
#define DAMAJOR 4
|
2018-11-30 08:01:11 +00:00
|
|
|
#define ACDMAJOR 117
|
|
|
|
#define CDMAJOR 15
|
1998-10-02 16:32:45 +00:00
|
|
|
|
2019-12-16 21:52:12 +00:00
|
|
|
/*
|
|
|
|
* INT13 commands
|
|
|
|
*/
|
|
|
|
#define CMD_RESET 0x0000
|
|
|
|
#define CMD_READ_CHS 0x0200
|
|
|
|
#define CMD_WRITE_CHS 0x0300
|
|
|
|
#define CMD_READ_PARAM 0x0800
|
|
|
|
#define CMD_DRIVE_TYPE 0x1500
|
|
|
|
#define CMD_CHECK_EDD 0x4100
|
|
|
|
#define CMD_READ_LBA 0x4200
|
|
|
|
#define CMD_WRITE_LBA 0x4300
|
|
|
|
#define CMD_EXT_PARAM 0x4800
|
|
|
|
#define CMD_CD_GET_STATUS 0x4b01
|
|
|
|
|
|
|
|
#define DISK_BIOS 0x13
|
|
|
|
|
1998-08-21 03:17:42 +00:00
|
|
|
#ifdef DISK_DEBUG
|
2019-05-09 13:12:43 +00:00
|
|
|
#define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
|
1998-08-21 03:17:42 +00:00
|
|
|
#else
|
2019-05-09 13:12:43 +00:00
|
|
|
#define DPRINTF(fmt, args...) ((void)0)
|
1998-08-21 03:17:42 +00:00
|
|
|
#endif
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
struct specification_packet {
|
|
|
|
uint8_t sp_size;
|
|
|
|
uint8_t sp_bootmedia;
|
|
|
|
uint8_t sp_drive;
|
|
|
|
uint8_t sp_controller;
|
|
|
|
uint32_t sp_lba;
|
|
|
|
uint16_t sp_devicespec;
|
|
|
|
uint16_t sp_buffersegment;
|
|
|
|
uint16_t sp_loadsegment;
|
|
|
|
uint16_t sp_sectorcount;
|
|
|
|
uint16_t sp_cylsec;
|
|
|
|
uint8_t sp_head;
|
2020-02-03 11:33:33 +00:00
|
|
|
uint8_t sp_dummy[16]; /* Avoid memory corruption */
|
2018-11-30 08:01:11 +00:00
|
|
|
};
|
|
|
|
|
1998-10-02 16:32:45 +00:00
|
|
|
/*
|
|
|
|
* List of BIOS devices, translation from disk unit number to
|
|
|
|
* BIOS unit number.
|
|
|
|
*/
|
2018-11-30 08:01:11 +00:00
|
|
|
typedef struct bdinfo
|
1998-10-02 16:32:45 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
STAILQ_ENTRY(bdinfo) bd_link; /* link in device list */
|
2012-08-05 14:37:48 +00:00
|
|
|
int bd_unit; /* BIOS unit number */
|
|
|
|
int bd_cyl; /* BIOS geometry */
|
|
|
|
int bd_hds;
|
|
|
|
int bd_sec;
|
|
|
|
int bd_flags;
|
|
|
|
#define BD_MODEINT13 0x0000
|
|
|
|
#define BD_MODEEDD1 0x0001
|
|
|
|
#define BD_MODEEDD3 0x0002
|
2018-10-31 16:42:40 +00:00
|
|
|
#define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3)
|
2012-08-05 14:37:48 +00:00
|
|
|
#define BD_MODEMASK 0x0003
|
|
|
|
#define BD_FLOPPY 0x0004
|
2018-11-30 08:01:11 +00:00
|
|
|
#define BD_CDROM 0x0008
|
|
|
|
#define BD_NO_MEDIA 0x0010
|
2012-08-05 14:37:48 +00:00
|
|
|
int bd_type; /* BIOS 'drive type' (floppy only) */
|
|
|
|
uint16_t bd_sectorsize; /* Sector size */
|
|
|
|
uint64_t bd_sectors; /* Disk size */
|
A new implementation of the loader block cache
The block cache implementation in loader has proven to be almost useless, and in worst case even slowing down the disk reads due to insufficient cache size and extra memory copy.
Also the current cache implementation does not cache reads from CDs, or work with zfs built on top of multiple disks.
Instead of an LRU, this code uses a simple hash (O(1) read from cache), and instead of a single global cache, a separate cache per block device.
The cache also implements limited read-ahead to increase performance.
To simplify read ahead management, the read ahead will not wrap over bcache end, so in worst case, single block physical read will be performed to fill the last block in bcache.
Booting from a virtual CD over IPMI:
0ms latency, before: 27 second, after: 7 seconds
60ms latency, before: over 12 minutes, after: under 5 minutes.
Submitted by: Toomas Soome <tsoome@me.com>
Reviewed by: delphij (previous version), emaste (previous version)
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D4713
2016-04-18 23:09:22 +00:00
|
|
|
int bd_open; /* reference counter */
|
|
|
|
void *bd_bcache; /* buffer cache data */
|
2018-11-30 08:01:11 +00:00
|
|
|
} bdinfo_t;
|
1998-10-02 16:32:45 +00:00
|
|
|
|
2018-08-15 22:40:09 +00:00
|
|
|
#define BD_RD 0
|
|
|
|
#define BD_WR 1
|
1998-09-17 23:52:16 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
typedef STAILQ_HEAD(bdinfo_list, bdinfo) bdinfo_list_t;
|
|
|
|
static bdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
|
|
|
|
static bdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
|
|
|
|
static bdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
|
2018-03-29 00:55:11 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
static void bd_io_workaround(bdinfo_t *);
|
|
|
|
static int bd_io(struct disk_devdesc *, bdinfo_t *, daddr_t, int, caddr_t, int);
|
|
|
|
static bool bd_int13probe(bdinfo_t *);
|
1998-09-17 23:52:16 +00:00
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
static int bd_init(void);
|
2018-11-30 08:01:11 +00:00
|
|
|
static int cd_init(void);
|
|
|
|
static int fd_init(void);
|
2016-12-30 19:06:29 +00:00
|
|
|
static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
|
|
|
|
char *buf, size_t *rsize);
|
|
|
|
static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size,
|
|
|
|
char *buf, size_t *rsize);
|
2012-08-05 14:37:48 +00:00
|
|
|
static int bd_open(struct open_file *f, ...);
|
|
|
|
static int bd_close(struct open_file *f);
|
|
|
|
static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
|
2016-11-08 06:50:18 +00:00
|
|
|
static int bd_print(int verbose);
|
2018-11-30 08:01:11 +00:00
|
|
|
static int cd_print(int verbose);
|
|
|
|
static int fd_print(int verbose);
|
2019-01-05 07:20:00 +00:00
|
|
|
static void bd_reset_disk(int);
|
|
|
|
static int bd_get_diskinfo_std(struct bdinfo *);
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
struct devsw biosfd = {
|
|
|
|
.dv_name = "fd",
|
|
|
|
.dv_type = DEVT_FD,
|
|
|
|
.dv_init = fd_init,
|
|
|
|
.dv_strategy = bd_strategy,
|
|
|
|
.dv_open = bd_open,
|
|
|
|
.dv_close = bd_close,
|
|
|
|
.dv_ioctl = bd_ioctl,
|
|
|
|
.dv_print = fd_print,
|
2022-07-27 14:46:21 +00:00
|
|
|
.dv_cleanup = nullsys,
|
2018-11-30 08:01:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct devsw bioscd = {
|
|
|
|
.dv_name = "cd",
|
|
|
|
.dv_type = DEVT_CD,
|
|
|
|
.dv_init = cd_init,
|
|
|
|
.dv_strategy = bd_strategy,
|
|
|
|
.dv_open = bd_open,
|
|
|
|
.dv_close = bd_close,
|
|
|
|
.dv_ioctl = bd_ioctl,
|
|
|
|
.dv_print = cd_print,
|
2022-07-27 14:46:21 +00:00
|
|
|
.dv_cleanup = nullsys,
|
2018-11-30 08:01:11 +00:00
|
|
|
};
|
1998-09-17 23:52:16 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
struct devsw bioshd = {
|
|
|
|
.dv_name = "disk",
|
|
|
|
.dv_type = DEVT_DISK,
|
|
|
|
.dv_init = bd_init,
|
|
|
|
.dv_strategy = bd_strategy,
|
|
|
|
.dv_open = bd_open,
|
|
|
|
.dv_close = bd_close,
|
|
|
|
.dv_ioctl = bd_ioctl,
|
|
|
|
.dv_print = bd_print,
|
2022-07-27 14:46:21 +00:00
|
|
|
.dv_cleanup = nullsys,
|
2022-08-11 15:06:28 +00:00
|
|
|
.dv_fmtdev = disk_fmtdev,
|
2022-11-30 22:08:51 +00:00
|
|
|
.dv_parsedev = disk_parsedev,
|
1998-08-21 03:17:42 +00:00
|
|
|
};
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
static bdinfo_list_t *
|
|
|
|
bd_get_bdinfo_list(struct devsw *dev)
|
|
|
|
{
|
|
|
|
if (dev->dv_type == DEVT_DISK)
|
|
|
|
return (&hdinfo);
|
|
|
|
if (dev->dv_type == DEVT_CD)
|
|
|
|
return (&cdinfo);
|
|
|
|
if (dev->dv_type == DEVT_FD)
|
|
|
|
return (&fdinfo);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX this gets called way way too often, investigate */
|
|
|
|
static bdinfo_t *
|
|
|
|
bd_get_bdinfo(struct devdesc *dev)
|
|
|
|
{
|
|
|
|
bdinfo_list_t *bdi;
|
|
|
|
bdinfo_t *bd = NULL;
|
|
|
|
int unit;
|
|
|
|
|
|
|
|
bdi = bd_get_bdinfo_list(dev->d_dev);
|
|
|
|
if (bdi == NULL)
|
|
|
|
return (bd);
|
|
|
|
|
|
|
|
unit = 0;
|
|
|
|
STAILQ_FOREACH(bd, bdi, bd_link) {
|
|
|
|
if (unit == dev->d_unit)
|
|
|
|
return (bd);
|
|
|
|
unit++;
|
|
|
|
}
|
|
|
|
return (bd);
|
|
|
|
}
|
|
|
|
|
1998-08-21 03:17:42 +00:00
|
|
|
/*
|
1998-10-02 16:32:45 +00:00
|
|
|
* Translate between BIOS device numbers and our private unit numbers.
|
1998-08-21 03:17:42 +00:00
|
|
|
*/
|
1998-10-02 16:32:45 +00:00
|
|
|
int
|
|
|
|
bd_bios2unit(int biosdev)
|
1998-08-21 03:17:42 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_list_t *bdi[] = { &fdinfo, &cdinfo, &hdinfo, NULL };
|
|
|
|
bdinfo_t *bd;
|
|
|
|
int i, unit;
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("looking for bios device 0x%x", biosdev);
|
2018-11-30 08:01:11 +00:00
|
|
|
for (i = 0; bdi[i] != NULL; i++) {
|
|
|
|
unit = 0;
|
|
|
|
STAILQ_FOREACH(bd, bdi[i], bd_link) {
|
|
|
|
if (bd->bd_unit == biosdev) {
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("bd unit %d is BIOS device 0x%x", unit,
|
2018-11-30 08:01:11 +00:00
|
|
|
bd->bd_unit);
|
|
|
|
return (unit);
|
|
|
|
}
|
|
|
|
unit++;
|
|
|
|
}
|
2012-08-05 14:37:48 +00:00
|
|
|
}
|
|
|
|
return (-1);
|
1998-10-02 16:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_unit2bios(struct i386_devdesc *dev)
|
1998-10-02 16:32:45 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_list_t *bdi;
|
|
|
|
bdinfo_t *bd;
|
|
|
|
int unit;
|
|
|
|
|
|
|
|
bdi = bd_get_bdinfo_list(dev->dd.d_dev);
|
|
|
|
if (bdi == NULL)
|
|
|
|
return (-1);
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
unit = 0;
|
|
|
|
STAILQ_FOREACH(bd, bdi, bd_link) {
|
|
|
|
if (unit == dev->dd.d_unit)
|
|
|
|
return (bd->bd_unit);
|
|
|
|
unit++;
|
|
|
|
}
|
2012-08-05 14:37:48 +00:00
|
|
|
return (-1);
|
1998-10-02 16:32:45 +00:00
|
|
|
}
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2019-01-05 07:20:00 +00:00
|
|
|
/*
|
|
|
|
* Use INT13 AH=15 - Read Drive Type.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fd_count(void)
|
|
|
|
{
|
|
|
|
int drive;
|
|
|
|
|
|
|
|
for (drive = 0; drive < MAXBDDEV; drive++) {
|
|
|
|
bd_reset_disk(drive);
|
|
|
|
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_DRIVE_TYPE;
|
2019-01-05 07:20:00 +00:00
|
|
|
v86.edx = drive;
|
|
|
|
v86int();
|
|
|
|
|
|
|
|
if (V86_CY(v86.efl))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if ((v86.eax & 0x300) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (drive);
|
|
|
|
}
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
/*
|
1998-08-21 03:17:42 +00:00
|
|
|
* Quiz the BIOS for disk devices, save a little info about them.
|
|
|
|
*/
|
|
|
|
static int
|
2018-11-30 08:01:11 +00:00
|
|
|
fd_init(void)
|
1998-08-21 03:17:42 +00:00
|
|
|
{
|
2019-01-05 07:20:00 +00:00
|
|
|
int unit, numfd;
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2019-01-05 07:20:00 +00:00
|
|
|
numfd = fd_count();
|
|
|
|
for (unit = 0; unit < numfd; unit++) {
|
2018-11-30 08:01:11 +00:00
|
|
|
if ((bd = calloc(1, sizeof(*bd))) == NULL)
|
|
|
|
break;
|
2019-01-05 07:20:00 +00:00
|
|
|
|
|
|
|
bd->bd_sectorsize = BIOSDISK_SECSIZE;
|
2018-11-30 08:01:11 +00:00
|
|
|
bd->bd_flags = BD_FLOPPY;
|
|
|
|
bd->bd_unit = unit;
|
2019-01-05 07:20:00 +00:00
|
|
|
|
|
|
|
/* Use std diskinfo for floppy drive */
|
|
|
|
if (bd_get_diskinfo_std(bd) != 0) {
|
2018-11-30 08:01:11 +00:00
|
|
|
free(bd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (bd->bd_sectors == 0)
|
|
|
|
bd->bd_flags |= BD_NO_MEDIA;
|
|
|
|
|
|
|
|
printf("BIOS drive %c: is %s%d\n", ('A' + unit),
|
|
|
|
biosfd.dv_name, unit);
|
|
|
|
|
|
|
|
STAILQ_INSERT_TAIL(&fdinfo, bd, bd_link);
|
|
|
|
}
|
|
|
|
|
|
|
|
bcache_add_dev(unit);
|
|
|
|
return (0);
|
|
|
|
}
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
static int
|
|
|
|
bd_init(void)
|
|
|
|
{
|
|
|
|
int base, unit;
|
|
|
|
bdinfo_t *bd;
|
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSENTER();
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
base = 0x80;
|
|
|
|
for (unit = 0; unit < *(unsigned char *)PTOV(BIOS_NUMDRIVES); unit++) {
|
|
|
|
/*
|
|
|
|
* Check the BIOS equipment list for number of fixed disks.
|
|
|
|
*/
|
|
|
|
if ((bd = calloc(1, sizeof(*bd))) == NULL)
|
|
|
|
break;
|
|
|
|
bd->bd_unit = base + unit;
|
|
|
|
if (!bd_int13probe(bd)) {
|
|
|
|
free(bd);
|
|
|
|
break;
|
2012-08-05 14:37:48 +00:00
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
printf("BIOS drive %c: is %s%d\n", ('C' + unit),
|
|
|
|
bioshd.dv_name, unit);
|
|
|
|
|
|
|
|
STAILQ_INSERT_TAIL(&hdinfo, bd, bd_link);
|
1998-08-21 03:17:42 +00:00
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
bcache_add_dev(unit);
|
2021-05-31 00:01:25 +00:00
|
|
|
TSEXIT();
|
2018-08-05 18:19:40 +00:00
|
|
|
return (0);
|
1998-08-21 03:17:42 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
/*
|
|
|
|
* We can't quiz, we have to be told what device to use, so this function
|
|
|
|
* doesn't do anything. Instead, the loader calls bc_add() with the BIOS
|
|
|
|
* device number to add.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
cd_init(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
/*
|
|
|
|
* Information from bootable CD-ROM.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
bd_get_diskinfo_cd(struct bdinfo *bd)
|
2018-11-30 08:01:11 +00:00
|
|
|
{
|
|
|
|
struct specification_packet bc_sp;
|
2020-02-03 11:33:33 +00:00
|
|
|
int ret = -1;
|
2018-11-30 08:01:11 +00:00
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
(void) memset(&bc_sp, 0, sizeof (bc_sp));
|
|
|
|
/* Set sp_size as per specification. */
|
|
|
|
bc_sp.sp_size = sizeof (bc_sp) - sizeof (bc_sp.sp_dummy);
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_CD_GET_STATUS;
|
2020-02-03 11:33:33 +00:00
|
|
|
v86.edx = bd->bd_unit;
|
2018-11-30 08:01:11 +00:00
|
|
|
v86.ds = VTOPSEG(&bc_sp);
|
|
|
|
v86.esi = VTOPOFF(&bc_sp);
|
|
|
|
v86int();
|
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
if ((v86.eax & 0xff00) == 0 &&
|
|
|
|
bc_sp.sp_drive == bd->bd_unit) {
|
|
|
|
bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) +
|
|
|
|
((bc_sp.sp_cylsec & 0xff00) >> 8) + 1;
|
|
|
|
bd->bd_sec = bc_sp.sp_cylsec & 0x3f;
|
|
|
|
bd->bd_hds = bc_sp.sp_head + 1;
|
|
|
|
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
|
2018-11-30 08:01:11 +00:00
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
if (bc_sp.sp_bootmedia & 0x0F) {
|
|
|
|
/* Floppy or hard-disk emulation */
|
|
|
|
bd->bd_sectorsize = BIOSDISK_SECSIZE;
|
|
|
|
return (-1);
|
|
|
|
} else {
|
|
|
|
bd->bd_sectorsize = 2048;
|
|
|
|
bd->bd_flags = BD_MODEEDD | BD_CDROM;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
/*
|
2020-02-03 11:33:33 +00:00
|
|
|
* If this is the boot_drive, default to non-emulation bootable CD-ROM.
|
2018-11-30 08:01:11 +00:00
|
|
|
*/
|
2020-02-03 11:33:33 +00:00
|
|
|
if (ret != 0 && bd->bd_unit >= 0x88) {
|
|
|
|
bd->bd_cyl = 0;
|
|
|
|
bd->bd_hds = 1;
|
|
|
|
bd->bd_sec = 15;
|
|
|
|
bd->bd_sectorsize = 2048;
|
|
|
|
bd->bd_flags = BD_MODEEDD | BD_CDROM;
|
|
|
|
bd->bd_sectors = 0;
|
|
|
|
ret = 0;
|
2018-11-30 08:01:11 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
/*
|
|
|
|
* Note we can not use bd_get_diskinfo_ext() nor bd_get_diskinfo_std()
|
|
|
|
* here - some systems do get hung with those.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Still no size? use 7.961GB. The size does not really matter
|
|
|
|
* as long as it is reasonably large to make our reads to pass
|
|
|
|
* the sector count check.
|
|
|
|
*/
|
2018-11-30 08:01:11 +00:00
|
|
|
if (bd->bd_sectors == 0)
|
|
|
|
bd->bd_sectors = 4173824;
|
2020-02-03 11:33:33 +00:00
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bc_add(int biosdev)
|
|
|
|
{
|
|
|
|
bdinfo_t *bd;
|
|
|
|
int nbcinfo = 0;
|
|
|
|
|
|
|
|
if (!STAILQ_EMPTY(&cdinfo))
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if ((bd = calloc(1, sizeof(*bd))) == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
bd->bd_unit = biosdev;
|
|
|
|
if (bd_get_diskinfo_cd(bd) < 0) {
|
|
|
|
free(bd);
|
|
|
|
return (-1);
|
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link);
|
|
|
|
printf("BIOS CD is cd%d\n", nbcinfo);
|
|
|
|
nbcinfo++;
|
|
|
|
bcache_add_dev(nbcinfo); /* register cd device in bcache */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
1998-08-21 03:17:42 +00:00
|
|
|
/*
|
2018-10-31 16:42:40 +00:00
|
|
|
* Return EDD version or 0 if EDD is not supported on this drive.
|
1998-08-21 03:17:42 +00:00
|
|
|
*/
|
|
|
|
static int
|
2018-10-31 16:42:40 +00:00
|
|
|
bd_check_extensions(int unit)
|
1998-08-21 03:17:42 +00:00
|
|
|
{
|
2019-01-05 07:20:00 +00:00
|
|
|
/* do not use ext calls for floppy devices */
|
|
|
|
if (unit < 0x80)
|
|
|
|
return (0);
|
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
/* Determine if we can use EDD with this device. */
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_CHECK_EDD;
|
2018-10-31 16:42:40 +00:00
|
|
|
v86.edx = unit;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.ebx = EDD_QUERY_MAGIC;
|
2018-10-31 16:42:40 +00:00
|
|
|
v86int();
|
|
|
|
|
|
|
|
if (V86_CY(v86.efl) || /* carry set */
|
2019-12-16 21:52:12 +00:00
|
|
|
(v86.ebx & 0xffff) != EDD_INSTALLED) /* signature */
|
2018-10-31 16:42:40 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* extended disk access functions (AH=42h-44h,47h,48h) supported */
|
|
|
|
if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
|
|
|
|
return (0);
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
return ((v86.eax >> 8) & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bd_reset_disk(int unit)
|
|
|
|
{
|
|
|
|
/* reset disk */
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_RESET;
|
2018-10-31 16:42:40 +00:00
|
|
|
v86.edx = unit;
|
|
|
|
v86int();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read CHS info. Return 0 on success, error otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
bd_get_diskinfo_std(struct bdinfo *bd)
|
|
|
|
{
|
|
|
|
bzero(&v86, sizeof(v86));
|
2012-08-05 14:37:48 +00:00
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_READ_PARAM;
|
2012-08-05 14:37:48 +00:00
|
|
|
v86.edx = bd->bd_unit;
|
|
|
|
v86int();
|
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
|
|
|
|
return ((v86.eax & 0xff00) >> 8);
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
/* return custom error on absurd sector number */
|
|
|
|
if ((v86.ecx & 0x3f) == 0)
|
|
|
|
return (0x60);
|
2016-09-01 06:35:13 +00:00
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
|
|
|
|
/* Convert max head # -> # of heads */
|
|
|
|
bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
|
|
|
|
bd->bd_sec = v86.ecx & 0x3f;
|
2018-10-31 16:42:40 +00:00
|
|
|
bd->bd_type = v86.ebx;
|
|
|
|
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read EDD info. Return 0 on success, error otherwise.
|
2020-02-03 11:33:33 +00:00
|
|
|
*
|
|
|
|
* Avoid stack corruption on some systems by adding extra bytes to
|
|
|
|
* params block.
|
2018-10-31 16:42:40 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
bd_get_diskinfo_ext(struct bdinfo *bd)
|
|
|
|
{
|
2020-02-03 11:33:33 +00:00
|
|
|
struct disk_params {
|
|
|
|
struct edd_params head;
|
|
|
|
struct edd_device_path_v3 device_path;
|
|
|
|
uint8_t dummy[16];
|
|
|
|
} __packed dparams;
|
|
|
|
struct edd_params *params;
|
2018-10-31 16:42:40 +00:00
|
|
|
uint64_t total;
|
2000-06-19 17:44:40 +00:00
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
params = &dparams.head;
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
/* Get disk params */
|
2020-02-03 11:33:33 +00:00
|
|
|
bzero(&dparams, sizeof(dparams));
|
|
|
|
params->len = sizeof(struct edd_params_v3);
|
2012-08-05 14:37:48 +00:00
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_EXT_PARAM;
|
2012-08-05 14:37:48 +00:00
|
|
|
v86.edx = bd->bd_unit;
|
2020-02-03 11:33:33 +00:00
|
|
|
v86.ds = VTOPSEG(&dparams);
|
|
|
|
v86.esi = VTOPOFF(&dparams);
|
2012-08-05 14:37:48 +00:00
|
|
|
v86int();
|
2016-09-01 06:35:13 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0))
|
|
|
|
return ((v86.eax & 0xff00) >> 8);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sector size must be a multiple of 512 bytes.
|
|
|
|
* An alternate test would be to check power of 2,
|
|
|
|
* powerof2(params.sector_size).
|
2018-12-30 09:35:47 +00:00
|
|
|
* 16K is largest read buffer we can use at this time.
|
2018-10-31 16:42:40 +00:00
|
|
|
*/
|
2020-02-03 11:33:33 +00:00
|
|
|
if (params->sector_size >= 512 &&
|
|
|
|
params->sector_size <= 16384 &&
|
|
|
|
(params->sector_size % BIOSDISK_SECSIZE) == 0)
|
|
|
|
bd->bd_sectorsize = params->sector_size;
|
2016-09-01 06:35:13 +00:00
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
bd->bd_cyl = params->cylinders;
|
|
|
|
bd->bd_hds = params->heads;
|
|
|
|
bd->bd_sec = params->sectors_per_track;
|
2018-10-31 16:42:40 +00:00
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
if (params->sectors != 0) {
|
|
|
|
total = params->sectors;
|
2018-10-31 16:42:40 +00:00
|
|
|
} else {
|
2020-02-03 11:33:33 +00:00
|
|
|
total = (uint64_t)params->cylinders *
|
|
|
|
params->heads * params->sectors_per_track;
|
2018-10-31 16:42:40 +00:00
|
|
|
}
|
|
|
|
bd->bd_sectors = total;
|
2016-09-01 06:35:13 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to detect a device supported by the legacy int13 BIOS
|
|
|
|
*/
|
2018-11-30 08:01:11 +00:00
|
|
|
static bool
|
|
|
|
bd_int13probe(bdinfo_t *bd)
|
2018-10-31 16:42:40 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
int edd, ret;
|
2018-10-31 16:42:40 +00:00
|
|
|
|
|
|
|
bd->bd_flags &= ~BD_NO_MEDIA;
|
|
|
|
|
2020-02-03 11:33:33 +00:00
|
|
|
if ((bd->bd_flags & BD_CDROM) != 0) {
|
|
|
|
return (bd_get_diskinfo_cd(bd) == 0);
|
|
|
|
}
|
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
edd = bd_check_extensions(bd->bd_unit);
|
|
|
|
if (edd == 0)
|
|
|
|
bd->bd_flags |= BD_MODEINT13;
|
|
|
|
else if (edd < 0x30)
|
|
|
|
bd->bd_flags |= BD_MODEEDD1;
|
|
|
|
else
|
|
|
|
bd->bd_flags |= BD_MODEEDD3;
|
|
|
|
|
|
|
|
/* Default sector size */
|
2020-01-09 21:21:08 +00:00
|
|
|
if (bd->bd_sectorsize == 0)
|
|
|
|
bd->bd_sectorsize = BIOSDISK_SECSIZE;
|
2018-10-31 16:42:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Test if the floppy device is present, so we can avoid receiving
|
|
|
|
* bogus information from bd_get_diskinfo_std().
|
|
|
|
*/
|
|
|
|
if (bd->bd_unit < 0x80) {
|
|
|
|
/* reset disk */
|
|
|
|
bd_reset_disk(bd->bd_unit);
|
|
|
|
|
|
|
|
/* Get disk type */
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
|
|
|
v86.eax = CMD_DRIVE_TYPE;
|
2018-10-31 16:42:40 +00:00
|
|
|
v86.edx = bd->bd_unit;
|
|
|
|
v86int();
|
|
|
|
if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0)
|
2018-11-30 08:01:11 +00:00
|
|
|
return (false);
|
2000-06-19 17:44:40 +00:00
|
|
|
}
|
2018-10-31 16:42:40 +00:00
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
if (edd != 0)
|
|
|
|
ret = bd_get_diskinfo_ext(bd);
|
|
|
|
if (ret != 0 || bd->bd_sectors == 0)
|
|
|
|
ret = bd_get_diskinfo_std(bd);
|
|
|
|
|
|
|
|
if (ret != 0 && bd->bd_unit < 0x80) {
|
|
|
|
/* Set defaults for 1.44 floppy */
|
|
|
|
bd->bd_cyl = 80;
|
|
|
|
bd->bd_hds = 2;
|
|
|
|
bd->bd_sec = 18;
|
|
|
|
bd->bd_sectors = 2880;
|
|
|
|
/* Since we are there, there most likely is no media */
|
|
|
|
bd->bd_flags |= BD_NO_MEDIA;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
if (bd->bd_sectors != 0 && edd != 0) {
|
|
|
|
bd->bd_sec = 63;
|
|
|
|
bd->bd_hds = 255;
|
|
|
|
bd->bd_cyl =
|
|
|
|
(bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) /
|
|
|
|
bd->bd_sec * bd->bd_hds;
|
|
|
|
} else {
|
2018-11-30 08:01:11 +00:00
|
|
|
const char *dv_name;
|
|
|
|
|
|
|
|
if ((bd->bd_flags & BD_FLOPPY) != 0)
|
|
|
|
dv_name = biosfd.dv_name;
|
|
|
|
else
|
|
|
|
dv_name = bioshd.dv_name;
|
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
printf("Can not get information about %s unit %#x\n",
|
2018-11-30 08:01:11 +00:00
|
|
|
dv_name, bd->bd_unit);
|
|
|
|
return (false);
|
2018-10-31 16:42:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bd->bd_sec == 0)
|
|
|
|
bd->bd_sec = 63;
|
|
|
|
if (bd->bd_hds == 0)
|
|
|
|
bd->bd_hds = 255;
|
|
|
|
|
|
|
|
if (bd->bd_sectors == 0)
|
|
|
|
bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec;
|
|
|
|
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("unit 0x%x geometry %d/%d/%d\n", bd->bd_unit, bd->bd_cyl,
|
2018-10-31 16:42:40 +00:00
|
|
|
bd->bd_hds, bd->bd_sec);
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bd_count(bdinfo_list_t *bdi)
|
|
|
|
{
|
|
|
|
bdinfo_t *bd;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
STAILQ_FOREACH(bd, bdi, bd_link)
|
|
|
|
i++;
|
|
|
|
return (i);
|
1998-08-21 03:17:42 +00:00
|
|
|
}
|
|
|
|
|
1998-10-31 02:53:12 +00:00
|
|
|
/*
|
|
|
|
* Print information about disks
|
|
|
|
*/
|
2016-11-08 06:50:18 +00:00
|
|
|
static int
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_print_common(struct devsw *dev, bdinfo_list_t *bdi, int verbose)
|
1998-10-31 02:53:12 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
char line[80];
|
|
|
|
struct disk_devdesc devd;
|
|
|
|
bdinfo_t *bd;
|
2016-11-08 06:50:18 +00:00
|
|
|
int i, ret = 0;
|
2018-11-30 08:01:11 +00:00
|
|
|
char drive;
|
2012-08-05 14:37:48 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if (STAILQ_EMPTY(bdi))
|
2016-11-19 08:54:21 +00:00
|
|
|
return (0);
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
printf("%s devices:", dev->dv_name);
|
2016-11-19 08:54:21 +00:00
|
|
|
if ((ret = pager_output("\n")) != 0)
|
|
|
|
return (ret);
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
i = -1;
|
|
|
|
STAILQ_FOREACH(bd, bdi, bd_link) {
|
|
|
|
i++;
|
|
|
|
|
|
|
|
switch (dev->dv_type) {
|
|
|
|
case DEVT_FD:
|
|
|
|
drive = 'A';
|
|
|
|
break;
|
|
|
|
case DEVT_CD:
|
|
|
|
drive = 'C' + bd_count(&hdinfo);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
drive = 'C';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-11-08 06:50:18 +00:00
|
|
|
snprintf(line, sizeof(line),
|
2018-11-30 08:01:11 +00:00
|
|
|
" %s%d: BIOS drive %c (%s%ju X %u):\n",
|
|
|
|
dev->dv_name, i, drive + i,
|
|
|
|
(bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ?
|
2018-10-31 16:42:40 +00:00
|
|
|
"no media, " : "",
|
2018-11-30 08:01:11 +00:00
|
|
|
(uintmax_t)bd->bd_sectors,
|
|
|
|
bd->bd_sectorsize);
|
2016-11-08 06:50:18 +00:00
|
|
|
if ((ret = pager_output(line)) != 0)
|
2016-05-18 05:59:05 +00:00
|
|
|
break;
|
2018-08-16 06:50:53 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
|
2018-11-02 11:41:58 +00:00
|
|
|
continue;
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dev->dv_type != DEVT_DISK)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
devd.dd.d_dev = dev;
|
|
|
|
devd.dd.d_unit = i;
|
2019-03-24 18:51:52 +00:00
|
|
|
devd.d_slice = D_SLICENONE;
|
|
|
|
devd.d_partition = D_PARTNONE;
|
2018-11-30 08:01:11 +00:00
|
|
|
if (disk_open(&devd,
|
|
|
|
bd->bd_sectorsize * bd->bd_sectors,
|
|
|
|
bd->bd_sectorsize) == 0) {
|
|
|
|
snprintf(line, sizeof(line), " %s%d",
|
|
|
|
dev->dv_name, i);
|
|
|
|
ret = disk_print(&devd, line, verbose);
|
|
|
|
disk_close(&devd);
|
2016-11-08 06:50:18 +00:00
|
|
|
if (ret != 0)
|
2018-08-16 06:50:53 +00:00
|
|
|
break;
|
2007-10-24 21:33:00 +00:00
|
|
|
}
|
1999-01-09 02:36:19 +00:00
|
|
|
}
|
2016-11-08 06:50:18 +00:00
|
|
|
return (ret);
|
1998-10-31 02:53:12 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
static int
|
|
|
|
fd_print(int verbose)
|
|
|
|
{
|
|
|
|
return (bd_print_common(&biosfd, &fdinfo, verbose));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bd_print(int verbose)
|
|
|
|
{
|
|
|
|
return (bd_print_common(&bioshd, &hdinfo, verbose));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cd_print(int verbose)
|
|
|
|
{
|
|
|
|
return (bd_print_common(&bioscd, &cdinfo, verbose));
|
|
|
|
}
|
|
|
|
|
2018-11-07 11:14:22 +00:00
|
|
|
/*
|
|
|
|
* Read disk size from partition.
|
|
|
|
* This is needed to work around buggy BIOS systems returning
|
|
|
|
* wrong (truncated) disk media size.
|
|
|
|
* During bd_probe() we tested if the multiplication of bd_sectors
|
|
|
|
* would overflow so it should be safe to perform here.
|
|
|
|
*/
|
|
|
|
static uint64_t
|
|
|
|
bd_disk_get_sectors(struct disk_devdesc *dev)
|
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
2018-11-07 11:14:22 +00:00
|
|
|
struct disk_devdesc disk;
|
|
|
|
uint64_t size;
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
|
|
|
return (0);
|
|
|
|
|
2018-11-07 11:14:22 +00:00
|
|
|
disk.dd.d_dev = dev->dd.d_dev;
|
|
|
|
disk.dd.d_unit = dev->dd.d_unit;
|
2019-03-24 18:51:52 +00:00
|
|
|
disk.d_slice = D_SLICENONE;
|
|
|
|
disk.d_partition = D_PARTNONE;
|
2018-11-07 11:14:22 +00:00
|
|
|
disk.d_offset = 0;
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
size = bd->bd_sectors * bd->bd_sectorsize;
|
|
|
|
if (disk_open(&disk, size, bd->bd_sectorsize) == 0) {
|
2018-11-07 11:14:22 +00:00
|
|
|
(void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size);
|
|
|
|
disk_close(&disk);
|
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
return (size / bd->bd_sectorsize);
|
2018-11-07 11:14:22 +00:00
|
|
|
}
|
|
|
|
|
1998-08-21 03:17:42 +00:00
|
|
|
/*
|
|
|
|
* Attempt to open the disk described by (dev) for use by (f).
|
|
|
|
*
|
|
|
|
* Note that the philosophy here is "give them exactly what
|
|
|
|
* they ask for". This is necessary because being too "smart"
|
|
|
|
* about what the user might want leads to complications.
|
|
|
|
* (eg. given no slice or partition value, with a disk that is
|
|
|
|
* sliced - are they after the first BSD slice, or the DOS
|
|
|
|
* slice before it?)
|
|
|
|
*/
|
1998-10-02 16:32:45 +00:00
|
|
|
static int
|
2012-08-05 14:37:48 +00:00
|
|
|
bd_open(struct open_file *f, ...)
|
1998-10-02 16:32:45 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
2018-08-05 19:17:07 +00:00
|
|
|
struct disk_devdesc *dev;
|
2012-08-05 14:37:48 +00:00
|
|
|
va_list ap;
|
2018-08-05 19:17:07 +00:00
|
|
|
int rc;
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSENTER();
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
va_start(ap, f);
|
|
|
|
dev = va_arg(ap, struct disk_devdesc *);
|
|
|
|
va_end(ap);
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
2012-08-05 14:37:48 +00:00
|
|
|
return (EIO);
|
2018-10-31 16:42:40 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) {
|
|
|
|
if (!bd_int13probe(bd))
|
2018-10-31 16:42:40 +00:00
|
|
|
return (EIO);
|
2018-11-30 08:01:11 +00:00
|
|
|
if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
|
2018-11-02 11:41:58 +00:00
|
|
|
return (EIO);
|
2018-10-31 16:42:40 +00:00
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
if (bd->bd_bcache == NULL)
|
|
|
|
bd->bd_bcache = bcache_allocate();
|
|
|
|
|
|
|
|
if (bd->bd_open == 0)
|
|
|
|
bd->bd_sectors = bd_disk_get_sectors(dev);
|
|
|
|
bd->bd_open++;
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
|
|
rc = disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
|
|
|
|
bd->bd_sectorsize);
|
|
|
|
if (rc != 0) {
|
|
|
|
bd->bd_open--;
|
|
|
|
if (bd->bd_open == 0) {
|
|
|
|
bcache_free(bd->bd_bcache);
|
|
|
|
bd->bd_bcache = NULL;
|
|
|
|
}
|
2018-08-05 19:17:07 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-31 00:01:25 +00:00
|
|
|
TSEXIT();
|
2018-08-05 19:17:07 +00:00
|
|
|
return (rc);
|
2007-10-24 21:33:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-08-05 14:37:48 +00:00
|
|
|
bd_close(struct open_file *f)
|
2000-04-29 20:49:33 +00:00
|
|
|
{
|
2012-08-05 14:37:48 +00:00
|
|
|
struct disk_devdesc *dev;
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
|
|
|
int rc = 0;
|
1998-10-09 07:11:19 +00:00
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
dev = (struct disk_devdesc *)f->f_devdata;
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
|
|
|
return (EIO);
|
|
|
|
|
|
|
|
bd->bd_open--;
|
|
|
|
if (bd->bd_open == 0) {
|
|
|
|
bcache_free(bd->bd_bcache);
|
|
|
|
bd->bd_bcache = NULL;
|
A new implementation of the loader block cache
The block cache implementation in loader has proven to be almost useless, and in worst case even slowing down the disk reads due to insufficient cache size and extra memory copy.
Also the current cache implementation does not cache reads from CDs, or work with zfs built on top of multiple disks.
Instead of an LRU, this code uses a simple hash (O(1) read from cache), and instead of a single global cache, a separate cache per block device.
The cache also implements limited read-ahead to increase performance.
To simplify read ahead management, the read ahead will not wrap over bcache end, so in worst case, single block physical read will be performed to fill the last block in bcache.
Booting from a virtual CD over IPMI:
0ms latency, before: 27 second, after: 7 seconds
60ms latency, before: over 12 minutes, after: under 5 minutes.
Submitted by: Toomas Soome <tsoome@me.com>
Reviewed by: delphij (previous version), emaste (previous version)
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D4713
2016-04-18 23:09:22 +00:00
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK)
|
|
|
|
rc = disk_close(dev);
|
|
|
|
return (rc);
|
1998-10-09 07:11:19 +00:00
|
|
|
}
|
2007-10-24 21:33:00 +00:00
|
|
|
|
|
|
|
static int
|
2012-08-05 14:37:48 +00:00
|
|
|
bd_ioctl(struct open_file *f, u_long cmd, void *data)
|
2007-10-24 21:33:00 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
2012-08-05 14:37:48 +00:00
|
|
|
struct disk_devdesc *dev;
|
loader: biosdisk fix for 2+TB disks
This fix is implementing partition based boundary check for
disk IO and updates disk mediasize (if needed), based on information
from partition table.
As it appeared, the signed int based approach still has corner cases,
and the wrapover based behavior is non-standard.
The idea for this fix is based on two assumptions:
The bug about media size is hitting large (2+TB) disks, lesser disks
hopefully, are not affected.
Large disks are using GPT (which does include information about disk size).
Since our concern is about boot support and boot disks are partitioned,
implementing partition boundaries based IO verification should make the
media size issues mostly disappear.
However, for large disk case, we do have the disk size available from GPT table.
If non-GPT cases will appear, we still can make approximate calculation about
disk size based on defined partition(s), however, this is not the objective
of this patch, and can be added later if there is any need.
This patch does implement disk media size adjustment (if needed) in bd_open(),
and boundary check in bd_realstrategy().
Reviewed by: allanjude
Approved by: allanjude (mentor)
Differential Revision: https://reviews.freebsd.org/D8595
2017-02-06 18:29:43 +00:00
|
|
|
int rc;
|
2007-10-24 21:33:00 +00:00
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
dev = (struct disk_devdesc *)f->f_devdata;
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
|
|
|
return (EIO);
|
loader: biosdisk fix for 2+TB disks
This fix is implementing partition based boundary check for
disk IO and updates disk mediasize (if needed), based on information
from partition table.
As it appeared, the signed int based approach still has corner cases,
and the wrapover based behavior is non-standard.
The idea for this fix is based on two assumptions:
The bug about media size is hitting large (2+TB) disks, lesser disks
hopefully, are not affected.
Large disks are using GPT (which does include information about disk size).
Since our concern is about boot support and boot disks are partitioned,
implementing partition boundaries based IO verification should make the
media size issues mostly disappear.
However, for large disk case, we do have the disk size available from GPT table.
If non-GPT cases will appear, we still can make approximate calculation about
disk size based on defined partition(s), however, this is not the objective
of this patch, and can be added later if there is any need.
This patch does implement disk media size adjustment (if needed) in bd_open(),
and boundary check in bd_realstrategy().
Reviewed by: allanjude
Approved by: allanjude (mentor)
Differential Revision: https://reviews.freebsd.org/D8595
2017-02-06 18:29:43 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
|
|
rc = disk_ioctl(dev, cmd, data);
|
|
|
|
if (rc != ENOTTY)
|
|
|
|
return (rc);
|
|
|
|
}
|
loader: biosdisk fix for 2+TB disks
This fix is implementing partition based boundary check for
disk IO and updates disk mediasize (if needed), based on information
from partition table.
As it appeared, the signed int based approach still has corner cases,
and the wrapover based behavior is non-standard.
The idea for this fix is based on two assumptions:
The bug about media size is hitting large (2+TB) disks, lesser disks
hopefully, are not affected.
Large disks are using GPT (which does include information about disk size).
Since our concern is about boot support and boot disks are partitioned,
implementing partition boundaries based IO verification should make the
media size issues mostly disappear.
However, for large disk case, we do have the disk size available from GPT table.
If non-GPT cases will appear, we still can make approximate calculation about
disk size based on defined partition(s), however, this is not the objective
of this patch, and can be added later if there is any need.
This patch does implement disk media size adjustment (if needed) in bd_open(),
and boundary check in bd_realstrategy().
Reviewed by: allanjude
Approved by: allanjude (mentor)
Differential Revision: https://reviews.freebsd.org/D8595
2017-02-06 18:29:43 +00:00
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case DIOCGSECTORSIZE:
|
2018-11-30 08:01:11 +00:00
|
|
|
*(uint32_t *)data = bd->bd_sectorsize;
|
2007-10-24 21:33:00 +00:00
|
|
|
break;
|
2012-08-05 14:37:48 +00:00
|
|
|
case DIOCGMEDIASIZE:
|
2018-11-30 08:01:11 +00:00
|
|
|
*(uint64_t *)data = bd->bd_sectors * bd->bd_sectorsize;
|
2012-08-05 14:37:48 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (ENOTTY);
|
2007-10-24 21:33:00 +00:00
|
|
|
}
|
2012-08-05 14:37:48 +00:00
|
|
|
return (0);
|
1998-08-21 03:17:42 +00:00
|
|
|
}
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
static int
|
2016-12-30 19:06:29 +00:00
|
|
|
bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
|
A new implementation of the loader block cache
The block cache implementation in loader has proven to be almost useless, and in worst case even slowing down the disk reads due to insufficient cache size and extra memory copy.
Also the current cache implementation does not cache reads from CDs, or work with zfs built on top of multiple disks.
Instead of an LRU, this code uses a simple hash (O(1) read from cache), and instead of a single global cache, a separate cache per block device.
The cache also implements limited read-ahead to increase performance.
To simplify read ahead management, the read ahead will not wrap over bcache end, so in worst case, single block physical read will be performed to fill the last block in bcache.
Booting from a virtual CD over IPMI:
0ms latency, before: 27 second, after: 7 seconds
60ms latency, before: over 12 minutes, after: under 5 minutes.
Submitted by: Toomas Soome <tsoome@me.com>
Reviewed by: delphij (previous version), emaste (previous version)
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D4713
2016-04-18 23:09:22 +00:00
|
|
|
char *buf, size_t *rsize)
|
1998-11-02 23:28:11 +00:00
|
|
|
{
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
2012-08-05 14:37:48 +00:00
|
|
|
struct bcache_devdata bcd;
|
|
|
|
struct disk_devdesc *dev;
|
2018-11-30 08:01:11 +00:00
|
|
|
daddr_t offset;
|
2012-08-05 14:37:48 +00:00
|
|
|
|
|
|
|
dev = (struct disk_devdesc *)devdata;
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
|
|
|
return (EINVAL);
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
bcd.dv_strategy = bd_realstrategy;
|
|
|
|
bcd.dv_devdata = devdata;
|
2018-11-30 08:01:11 +00:00
|
|
|
bcd.dv_cache = bd->bd_bcache;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
|
|
|
|
|
|
offset = dev->d_offset * bd->bd_sectorsize;
|
|
|
|
offset /= BIOSDISK_SECSIZE;
|
|
|
|
}
|
|
|
|
return (bcache_strategy(&bcd, rw, dblk + offset, size,
|
2018-08-05 18:19:40 +00:00
|
|
|
buf, rsize));
|
1998-11-02 23:28:11 +00:00
|
|
|
}
|
|
|
|
|
2012-08-05 14:37:48 +00:00
|
|
|
static int
|
2016-12-30 19:06:29 +00:00
|
|
|
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
|
A new implementation of the loader block cache
The block cache implementation in loader has proven to be almost useless, and in worst case even slowing down the disk reads due to insufficient cache size and extra memory copy.
Also the current cache implementation does not cache reads from CDs, or work with zfs built on top of multiple disks.
Instead of an LRU, this code uses a simple hash (O(1) read from cache), and instead of a single global cache, a separate cache per block device.
The cache also implements limited read-ahead to increase performance.
To simplify read ahead management, the read ahead will not wrap over bcache end, so in worst case, single block physical read will be performed to fill the last block in bcache.
Booting from a virtual CD over IPMI:
0ms latency, before: 27 second, after: 7 seconds
60ms latency, before: over 12 minutes, after: under 5 minutes.
Submitted by: Toomas Soome <tsoome@me.com>
Reviewed by: delphij (previous version), emaste (previous version)
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D4713
2016-04-18 23:09:22 +00:00
|
|
|
char *buf, size_t *rsize)
|
1998-08-21 03:17:42 +00:00
|
|
|
{
|
2018-08-05 18:19:40 +00:00
|
|
|
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
|
|
|
uint64_t disk_blocks, offset, d_offset;
|
2018-12-30 09:35:47 +00:00
|
|
|
size_t blks, blkoff, bsize, bio_size, rest;
|
|
|
|
caddr_t bbuf = NULL;
|
2018-10-23 14:44:32 +00:00
|
|
|
int rc;
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL || (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA)
|
2018-11-02 11:41:58 +00:00
|
|
|
return (EIO);
|
|
|
|
|
2018-10-23 14:44:32 +00:00
|
|
|
/*
|
|
|
|
* First make sure the IO size is a multiple of 512 bytes. While we do
|
|
|
|
* process partial reads below, the strategy mechanism is built
|
|
|
|
* assuming IO is a multiple of 512B blocks. If the request is not
|
|
|
|
* a multiple of 512B blocks, it has to be some sort of bug.
|
|
|
|
*/
|
|
|
|
if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) {
|
|
|
|
printf("bd_strategy: %d bytes I/O not multiple of %d\n",
|
|
|
|
size, BIOSDISK_SECSIZE);
|
|
|
|
return (EIO);
|
2018-08-05 18:19:40 +00:00
|
|
|
}
|
1998-08-21 03:17:42 +00:00
|
|
|
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("open_disk %p", dev);
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2018-10-23 14:44:32 +00:00
|
|
|
offset = dblk * BIOSDISK_SECSIZE;
|
2018-11-30 08:01:11 +00:00
|
|
|
dblk = offset / bd->bd_sectorsize;
|
|
|
|
blkoff = offset % bd->bd_sectorsize;
|
2018-10-23 14:44:32 +00:00
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
/*
|
|
|
|
* Check the value of the size argument. We do have quite small
|
|
|
|
* heap (64MB), but we do not know good upper limit, so we check against
|
|
|
|
* INT_MAX here. This will also protect us against possible overflows
|
|
|
|
* while translating block count to bytes.
|
|
|
|
*/
|
|
|
|
if (size > INT_MAX) {
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("too large I/O: %zu bytes", size);
|
2018-08-05 18:19:40 +00:00
|
|
|
return (EIO);
|
2007-10-24 04:13:35 +00:00
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
blks = size / bd->bd_sectorsize;
|
|
|
|
if (blks == 0 || (size % bd->bd_sectorsize) != 0)
|
2018-10-23 14:44:32 +00:00
|
|
|
blks++;
|
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
if (dblk > dblk + blks)
|
|
|
|
return (EIO);
|
|
|
|
|
|
|
|
if (rsize)
|
|
|
|
*rsize = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get disk blocks, this value is either for whole disk or for
|
|
|
|
* partition.
|
|
|
|
*/
|
2018-11-30 08:01:11 +00:00
|
|
|
d_offset = 0;
|
|
|
|
disk_blocks = 0;
|
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
|
|
if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
|
|
|
|
/* DIOCGMEDIASIZE does return bytes. */
|
|
|
|
disk_blocks /= bd->bd_sectorsize;
|
|
|
|
}
|
|
|
|
d_offset = dev->d_offset;
|
2007-10-24 04:13:35 +00:00
|
|
|
}
|
2018-11-30 08:01:11 +00:00
|
|
|
if (disk_blocks == 0)
|
2022-09-08 00:35:06 +00:00
|
|
|
disk_blocks = bd->bd_sectors * (bd->bd_sectorsize /
|
|
|
|
BIOSDISK_SECSIZE) - d_offset;
|
2001-12-11 00:08:10 +00:00
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
/* Validate source block address. */
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dblk < d_offset || dblk >= d_offset + disk_blocks)
|
2018-08-05 18:19:40 +00:00
|
|
|
return (EIO);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Truncate if we are crossing disk or partition end.
|
|
|
|
*/
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dblk + blks >= d_offset + disk_blocks) {
|
|
|
|
blks = d_offset + disk_blocks - dblk;
|
|
|
|
size = blks * bd->bd_sectorsize;
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("short I/O %d", blks);
|
2007-10-24 04:13:35 +00:00
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2018-12-30 09:35:47 +00:00
|
|
|
bio_size = min(BIO_BUFFER_SIZE, size);
|
|
|
|
while (bio_size > bd->bd_sectorsize) {
|
|
|
|
bbuf = bio_alloc(bio_size);
|
|
|
|
if (bbuf != NULL)
|
|
|
|
break;
|
|
|
|
bio_size -= bd->bd_sectorsize;
|
|
|
|
}
|
|
|
|
if (bbuf == NULL) {
|
|
|
|
bio_size = V86_IO_BUFFER_SIZE;
|
|
|
|
if (bio_size / bd->bd_sectorsize == 0)
|
|
|
|
panic("BUG: Real mode buffer is too small");
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2018-12-30 09:35:47 +00:00
|
|
|
/* Use alternate 4k buffer */
|
|
|
|
bbuf = PTOV(V86_IO_BUFFER);
|
|
|
|
}
|
2018-10-23 14:44:32 +00:00
|
|
|
rest = size;
|
2018-12-30 09:35:47 +00:00
|
|
|
rc = 0;
|
2018-10-23 14:44:32 +00:00
|
|
|
while (blks > 0) {
|
2018-12-30 09:35:47 +00:00
|
|
|
int x = min(blks, bio_size / bd->bd_sectorsize);
|
2018-10-23 14:44:32 +00:00
|
|
|
|
|
|
|
switch (rw & F_MASK) {
|
|
|
|
case F_READ:
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("read %d from %lld to %p", x, dblk, buf);
|
2018-11-30 08:01:11 +00:00
|
|
|
bsize = bd->bd_sectorsize * x - blkoff;
|
2018-10-23 14:44:32 +00:00
|
|
|
if (rest < bsize)
|
|
|
|
bsize = rest;
|
|
|
|
|
2018-12-30 09:35:47 +00:00
|
|
|
if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD)) != 0) {
|
|
|
|
rc = EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
2018-10-23 14:44:32 +00:00
|
|
|
|
|
|
|
bcopy(bbuf + blkoff, buf, bsize);
|
|
|
|
break;
|
|
|
|
case F_WRITE :
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("write %d from %lld to %p", x, dblk, buf);
|
2018-10-23 14:44:32 +00:00
|
|
|
if (blkoff != 0) {
|
|
|
|
/*
|
|
|
|
* We got offset to sector, read 1 sector to
|
|
|
|
* bbuf.
|
|
|
|
*/
|
|
|
|
x = 1;
|
2018-11-30 08:01:11 +00:00
|
|
|
bsize = bd->bd_sectorsize - blkoff;
|
2018-10-23 14:44:32 +00:00
|
|
|
bsize = min(bsize, rest);
|
2018-11-30 08:01:11 +00:00
|
|
|
rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
|
|
|
|
} else if (rest < bd->bd_sectorsize) {
|
2018-10-23 14:44:32 +00:00
|
|
|
/*
|
|
|
|
* The remaining block is not full
|
|
|
|
* sector. Read 1 sector to bbuf.
|
|
|
|
*/
|
|
|
|
x = 1;
|
|
|
|
bsize = rest;
|
2018-11-30 08:01:11 +00:00
|
|
|
rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD);
|
2018-10-23 14:44:32 +00:00
|
|
|
} else {
|
|
|
|
/* We can write full sector(s). */
|
2018-11-30 08:01:11 +00:00
|
|
|
bsize = bd->bd_sectorsize * x;
|
2018-08-05 18:19:40 +00:00
|
|
|
}
|
2018-10-23 14:44:32 +00:00
|
|
|
/*
|
|
|
|
* Put your Data In, Put your Data out,
|
|
|
|
* Put your Data In, and shake it all about
|
|
|
|
*/
|
|
|
|
bcopy(buf, bbuf + blkoff, bsize);
|
2018-12-30 09:35:47 +00:00
|
|
|
if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_WR)) != 0) {
|
|
|
|
rc = EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2018-10-23 14:44:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* DO NOTHING */
|
2018-12-30 09:35:47 +00:00
|
|
|
rc = EROFS;
|
|
|
|
goto error;
|
2018-08-05 18:19:40 +00:00
|
|
|
}
|
2018-10-23 14:44:32 +00:00
|
|
|
|
|
|
|
blkoff = 0;
|
|
|
|
buf += bsize;
|
|
|
|
rest -= bsize;
|
|
|
|
blks -= x;
|
|
|
|
dblk += x;
|
2018-08-05 18:19:40 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 14:44:32 +00:00
|
|
|
if (rsize != NULL)
|
2018-08-05 18:19:40 +00:00
|
|
|
*rsize = size;
|
2018-12-30 09:35:47 +00:00
|
|
|
error:
|
|
|
|
if (bbuf != PTOV(V86_IO_BUFFER))
|
|
|
|
bio_free(bbuf, bio_size);
|
|
|
|
return (rc);
|
1998-08-21 03:17:42 +00:00
|
|
|
}
|
1998-09-17 23:52:16 +00:00
|
|
|
|
|
|
|
static int
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_edd_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
|
2018-08-05 18:19:40 +00:00
|
|
|
int dowrite)
|
1998-09-17 23:52:16 +00:00
|
|
|
{
|
2018-08-05 18:19:40 +00:00
|
|
|
static struct edd_packet packet;
|
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSENTER();
|
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
packet.len = sizeof(struct edd_packet);
|
|
|
|
packet.count = blks;
|
|
|
|
packet.off = VTOPOFF(dest);
|
|
|
|
packet.seg = VTOPSEG(dest);
|
|
|
|
packet.lba = dblk;
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
2018-08-15 22:40:09 +00:00
|
|
|
if (dowrite == BD_WR)
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.eax = CMD_WRITE_LBA; /* maybe Write with verify 0x4302? */
|
2018-08-05 18:19:40 +00:00
|
|
|
else
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.eax = CMD_READ_LBA;
|
2018-11-30 08:01:11 +00:00
|
|
|
v86.edx = bd->bd_unit;
|
2018-08-05 18:19:40 +00:00
|
|
|
v86.ds = VTOPSEG(&packet);
|
|
|
|
v86.esi = VTOPOFF(&packet);
|
|
|
|
v86int();
|
|
|
|
if (V86_CY(v86.efl))
|
|
|
|
return (v86.eax >> 8);
|
2021-05-31 00:01:25 +00:00
|
|
|
|
|
|
|
TSEXIT();
|
2018-08-05 18:19:40 +00:00
|
|
|
return (0);
|
2007-10-24 12:49:55 +00:00
|
|
|
}
|
1998-09-26 01:30:20 +00:00
|
|
|
|
2007-10-24 12:49:55 +00:00
|
|
|
static int
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_chs_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest,
|
2018-08-05 18:19:40 +00:00
|
|
|
int dowrite)
|
2007-10-24 12:49:55 +00:00
|
|
|
{
|
2018-08-05 18:19:40 +00:00
|
|
|
uint32_t x, bpc, cyl, hd, sec;
|
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSENTER();
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bpc = bd->bd_sec * bd->bd_hds; /* blocks per cylinder */
|
2018-08-05 18:19:40 +00:00
|
|
|
x = dblk;
|
|
|
|
cyl = x / bpc; /* block # / blocks per cylinder */
|
|
|
|
x %= bpc; /* block offset into cylinder */
|
2018-11-30 08:01:11 +00:00
|
|
|
hd = x / bd->bd_sec; /* offset / blocks per track */
|
|
|
|
sec = x % bd->bd_sec; /* offset into track */
|
2018-08-05 18:19:40 +00:00
|
|
|
|
|
|
|
/* correct sector number for 1-based BIOS numbering */
|
|
|
|
sec++;
|
|
|
|
|
|
|
|
if (cyl > 1023) {
|
|
|
|
/* CHS doesn't support cylinders > 1023. */
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
v86.ctl = V86_FLAGS;
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.addr = DISK_BIOS;
|
2018-08-15 22:40:09 +00:00
|
|
|
if (dowrite == BD_WR)
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.eax = CMD_WRITE_CHS | blks;
|
2018-08-05 18:19:40 +00:00
|
|
|
else
|
2019-12-16 21:52:12 +00:00
|
|
|
v86.eax = CMD_READ_CHS | blks;
|
2018-08-05 18:19:40 +00:00
|
|
|
v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
|
2018-11-30 08:01:11 +00:00
|
|
|
v86.edx = (hd << 8) | bd->bd_unit;
|
2018-08-05 18:19:40 +00:00
|
|
|
v86.es = VTOPSEG(dest);
|
|
|
|
v86.ebx = VTOPOFF(dest);
|
|
|
|
v86int();
|
|
|
|
if (V86_CY(v86.efl))
|
|
|
|
return (v86.eax >> 8);
|
2021-05-31 00:01:25 +00:00
|
|
|
TSEXIT();
|
2018-08-05 18:19:40 +00:00
|
|
|
return (0);
|
1998-09-17 23:52:16 +00:00
|
|
|
}
|
|
|
|
|
2018-03-29 00:55:11 +00:00
|
|
|
static void
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_io_workaround(bdinfo_t *bd)
|
2018-03-29 00:55:11 +00:00
|
|
|
{
|
|
|
|
uint8_t buf[8 * 1024];
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_edd_io(bd, 0xffffffff, 1, (caddr_t)buf, BD_RD);
|
2018-03-29 00:55:11 +00:00
|
|
|
}
|
|
|
|
|
2001-12-11 00:08:10 +00:00
|
|
|
static int
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_io(struct disk_devdesc *dev, bdinfo_t *bd, daddr_t dblk, int blks,
|
|
|
|
caddr_t dest, int dowrite)
|
2001-12-11 00:08:10 +00:00
|
|
|
{
|
2018-10-23 14:44:32 +00:00
|
|
|
int result, retry;
|
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSENTER();
|
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
/* Just in case some idiot actually tries to read/write -1 blocks... */
|
|
|
|
if (blks < 0)
|
|
|
|
return (-1);
|
|
|
|
|
2007-10-24 12:49:55 +00:00
|
|
|
/*
|
2018-08-05 18:19:40 +00:00
|
|
|
* Workaround for a problem with some HP ProLiant BIOS failing to work
|
|
|
|
* out the boot disk after installation. hrs and kuriyama discovered
|
|
|
|
* this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and
|
|
|
|
* discovered that an int13h call seems to cause a buffer overrun in
|
|
|
|
* the bios. The problem is alleviated by doing an extra read before
|
|
|
|
* the buggy read. It is not immediately known whether other models
|
|
|
|
* are similarly affected.
|
2018-10-23 14:44:32 +00:00
|
|
|
* Loop retrying the operation a couple of times. The BIOS
|
|
|
|
* may also retry.
|
2007-10-24 12:49:55 +00:00
|
|
|
*/
|
2018-08-16 07:13:36 +00:00
|
|
|
if (dowrite == BD_RD && dblk >= 0x100000000)
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_io_workaround(bd);
|
2018-10-23 14:44:32 +00:00
|
|
|
for (retry = 0; retry < 3; retry++) {
|
2018-11-30 08:01:11 +00:00
|
|
|
if (bd->bd_flags & BD_MODEEDD)
|
|
|
|
result = bd_edd_io(bd, dblk, blks, dest, dowrite);
|
2018-08-05 18:19:40 +00:00
|
|
|
else
|
2018-11-30 08:01:11 +00:00
|
|
|
result = bd_chs_io(bd, dblk, blks, dest, dowrite);
|
2018-10-23 14:44:32 +00:00
|
|
|
|
2018-10-31 16:42:40 +00:00
|
|
|
if (result == 0) {
|
2018-11-30 08:01:11 +00:00
|
|
|
if (bd->bd_flags & BD_NO_MEDIA)
|
|
|
|
bd->bd_flags &= ~BD_NO_MEDIA;
|
2018-10-31 16:42:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
bd_reset_disk(bd->bd_unit);
|
2018-10-31 16:42:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Error codes:
|
|
|
|
* 20h controller failure
|
|
|
|
* 31h no media in drive (IBM/MS INT 13 extensions)
|
|
|
|
* 80h no media in drive, VMWare (Fusion)
|
|
|
|
* There is no reason to repeat the IO with errors above.
|
|
|
|
*/
|
|
|
|
if (result == 0x20 || result == 0x31 || result == 0x80) {
|
2018-11-30 08:01:11 +00:00
|
|
|
bd->bd_flags |= BD_NO_MEDIA;
|
2018-10-23 14:44:32 +00:00
|
|
|
break;
|
2018-10-31 16:42:40 +00:00
|
|
|
}
|
2018-10-23 14:44:32 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if (result != 0 && (bd->bd_flags & BD_NO_MEDIA) == 0) {
|
2018-10-23 14:44:32 +00:00
|
|
|
if (dowrite == BD_WR) {
|
|
|
|
printf("%s%d: Write %d sector(s) from %p (0x%x) "
|
|
|
|
"to %lld: 0x%x\n", dev->dd.d_dev->dv_name,
|
|
|
|
dev->dd.d_unit, blks, dest, VTOP(dest), dblk,
|
|
|
|
result);
|
|
|
|
} else {
|
|
|
|
printf("%s%d: Read %d sector(s) from %lld to %p "
|
|
|
|
"(0x%x): 0x%x\n", dev->dd.d_dev->dv_name,
|
|
|
|
dev->dd.d_unit, blks, dblk, dest, VTOP(dest),
|
|
|
|
result);
|
2018-08-05 18:19:40 +00:00
|
|
|
}
|
2001-12-11 00:08:10 +00:00
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
|
2021-05-31 00:01:25 +00:00
|
|
|
TSEXIT();
|
|
|
|
|
2018-10-23 14:44:32 +00:00
|
|
|
return (result);
|
2001-12-11 00:08:10 +00:00
|
|
|
}
|
2007-10-24 12:49:55 +00:00
|
|
|
|
1998-10-02 16:32:45 +00:00
|
|
|
/*
|
2004-06-16 18:21:22 +00:00
|
|
|
* Return a suitable dev_t value for (dev).
|
1998-11-13 23:40:02 +00:00
|
|
|
*
|
|
|
|
* In the case where it looks like (dev) is a SCSI disk, we allow the number of
|
|
|
|
* IDE disks to be specified in $num_ide_disks. There should be a Better Way.
|
1998-10-02 16:32:45 +00:00
|
|
|
*/
|
|
|
|
int
|
2012-08-05 14:37:48 +00:00
|
|
|
bd_getdev(struct i386_devdesc *d)
|
1998-10-02 16:32:45 +00:00
|
|
|
{
|
2018-08-05 18:19:40 +00:00
|
|
|
struct disk_devdesc *dev;
|
2018-11-30 08:01:11 +00:00
|
|
|
bdinfo_t *bd;
|
2018-08-05 18:19:40 +00:00
|
|
|
int biosdev;
|
|
|
|
int major;
|
|
|
|
int rootdev;
|
|
|
|
char *nip, *cp;
|
2018-11-30 08:01:11 +00:00
|
|
|
int i, unit, slice, partition;
|
|
|
|
|
|
|
|
/* XXX: Assume partition 'a'. */
|
|
|
|
slice = 0;
|
|
|
|
partition = 0;
|
2018-08-05 18:19:40 +00:00
|
|
|
|
|
|
|
dev = (struct disk_devdesc *)d;
|
2018-11-30 08:01:11 +00:00
|
|
|
bd = bd_get_bdinfo(&dev->dd);
|
|
|
|
if (bd == NULL)
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
biosdev = bd_unit2bios(d);
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("unit %d BIOS device %d", dev->dd.d_unit, biosdev);
|
2018-08-05 18:19:40 +00:00
|
|
|
if (biosdev == -1) /* not a BIOS device */
|
|
|
|
return (-1);
|
2018-11-30 08:01:11 +00:00
|
|
|
|
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
|
|
if (disk_open(dev, bd->bd_sectors * bd->bd_sectorsize,
|
|
|
|
bd->bd_sectorsize) != 0) /* oops, not a viable device */
|
|
|
|
return (-1);
|
|
|
|
else
|
|
|
|
disk_close(dev);
|
|
|
|
slice = dev->d_slice + 1;
|
|
|
|
partition = dev->d_partition;
|
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
|
|
|
|
if (biosdev < 0x80) {
|
|
|
|
/* floppy (or emulated floppy) or ATAPI device */
|
2018-11-30 08:01:11 +00:00
|
|
|
if (bd->bd_type == DT_ATAPI) {
|
2018-08-05 18:19:40 +00:00
|
|
|
/* is an ATAPI disk */
|
|
|
|
major = WFDMAJOR;
|
|
|
|
} else {
|
|
|
|
/* is a floppy disk */
|
|
|
|
major = FDMAJOR;
|
|
|
|
}
|
1998-10-02 16:32:45 +00:00
|
|
|
} else {
|
2018-08-05 18:19:40 +00:00
|
|
|
/* assume an IDE disk */
|
|
|
|
major = WDMAJOR;
|
1998-10-02 16:32:45 +00:00
|
|
|
}
|
2018-08-05 18:19:40 +00:00
|
|
|
/* default root disk unit number */
|
|
|
|
unit = biosdev & 0x7f;
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
if (dev->dd.d_dev->dv_type == DEVT_CD) {
|
|
|
|
/*
|
|
|
|
* XXX: Need to examine device spec here to figure out if
|
|
|
|
* SCSI or ATAPI. No idea on how to figure out device number.
|
|
|
|
* All we can really pass to the kernel is what bus and device
|
|
|
|
* on which bus we were booted from, which dev_t isn't well
|
|
|
|
* suited to since those number don't match to unit numbers
|
|
|
|
* very well. We may just need to engage in a hack where
|
|
|
|
* we pass -C to the boot args if we are the boot device.
|
|
|
|
*/
|
|
|
|
major = ACDMAJOR;
|
|
|
|
unit = 0; /* XXX */
|
|
|
|
}
|
|
|
|
|
2018-08-05 18:19:40 +00:00
|
|
|
/* XXX a better kludge to set the root disk unit number */
|
|
|
|
if ((nip = getenv("root_disk_unit")) != NULL) {
|
|
|
|
i = strtol(nip, &cp, 0);
|
|
|
|
/* check for parse error */
|
|
|
|
if ((cp != nip) && (*cp == 0))
|
|
|
|
unit = i;
|
|
|
|
}
|
|
|
|
|
2018-11-30 08:01:11 +00:00
|
|
|
rootdev = MAKEBOOTDEV(major, slice, unit, partition);
|
2019-05-09 13:12:43 +00:00
|
|
|
DPRINTF("dev is 0x%x\n", rootdev);
|
2018-08-05 18:19:40 +00:00
|
|
|
return (rootdev);
|
1998-10-09 07:11:19 +00:00
|
|
|
}
|