Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_ahci.c
Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <pthread_np.h> | #include <pthread_np.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <md5.h> | #include <md5.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "config.h" | |||||
#include "debug.h" | |||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "ahci.h" | #include "ahci.h" | ||||
#include "block_if.h" | #include "block_if.h" | ||||
#define DEF_PORTS 6 /* Intel ICH8 AHCI supports 6 ports */ | #define DEF_PORTS 6 /* Intel ICH8 AHCI supports 6 ports */ | ||||
#define MAX_PORTS 32 /* AHCI supports 32 ports */ | #define MAX_PORTS 32 /* AHCI supports 32 ports */ | ||||
#define PxSIG_ATA 0x00000101 /* ATA drive */ | #define PxSIG_ATA 0x00000101 /* ATA drive */ | ||||
▲ Show 20 Lines • Show All 2,239 Lines • ▼ Show 20 Lines | pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
} | } | ||||
value >>= 8 * (regoff & 0x3); | value >>= 8 * (regoff & 0x3); | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
return (value); | return (value); | ||||
} | } | ||||
/* | |||||
* Each AHCI controller has a "port" node which contains nodes for | |||||
* each port named after the decimal number of the port (no leading | |||||
* zeroes). Port nodes contain a "type" ("hd" or "cd"), as well as | |||||
* options for blockif. For example: | |||||
* | |||||
* pci.0.1.0 | |||||
* .device="ahci" | |||||
* .port | |||||
* .0 | |||||
* .type="hd" | |||||
* .path="/path/to/image" | |||||
*/ | |||||
static int | static int | ||||
pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi) | pci_ahci_legacy_config_port(nvlist_t *nvl, int port, const char *type, | ||||
const char *opts) | |||||
{ | { | ||||
char node_name[sizeof("XX")]; | |||||
nvlist_t *port_nvl; | |||||
snprintf(node_name, sizeof(node_name), "%d", port); | |||||
port_nvl = create_relative_config_node(nvl, node_name); | |||||
set_config_value_node(port_nvl, "type", type); | |||||
return (blockif_legacy_config(port_nvl, opts)); | |||||
} | |||||
static int | |||||
pci_ahci_legacy_config(nvlist_t *nvl, const char *opts) | |||||
{ | |||||
nvlist_t *ports_nvl; | |||||
const char *type; | |||||
char *next, *next2, *str, *tofree; | |||||
int p, ret; | |||||
if (opts == NULL) | |||||
return (0); | |||||
ports_nvl = create_relative_config_node(nvl, "port"); | |||||
ret = 1; | |||||
tofree = str = strdup(opts); | |||||
for (p = 0; p < MAX_PORTS && str != NULL; p++, str = next) { | |||||
/* Identify and cut off type of present port. */ | |||||
if (strncmp(str, "hd:", 3) == 0) { | |||||
type = "hd"; | |||||
str += 3; | |||||
} else if (strncmp(str, "cd:", 3) == 0) { | |||||
type = "cd"; | |||||
str += 3; | |||||
} else | |||||
type = NULL; | |||||
/* Find and cut off the next port options. */ | |||||
next = strstr(str, ",hd:"); | |||||
next2 = strstr(str, ",cd:"); | |||||
if (next == NULL || (next2 != NULL && next2 < next)) | |||||
next = next2; | |||||
if (next != NULL) { | |||||
next[0] = 0; | |||||
next++; | |||||
} | |||||
if (str[0] == 0) | |||||
continue; | |||||
if (type == NULL) { | |||||
EPRINTLN("Missing or invalid type for port %d: \"%s\"", | |||||
p, str); | |||||
goto out; | |||||
} | |||||
if (pci_ahci_legacy_config_port(ports_nvl, p, type, str) != 0) | |||||
goto out; | |||||
} | |||||
ret = 0; | |||||
out: | |||||
free(tofree); | |||||
return (ret); | |||||
} | |||||
static int | |||||
pci_ahci_cd_legacy_config(nvlist_t *nvl, const char *opts) | |||||
{ | |||||
nvlist_t *ports_nvl; | |||||
ports_nvl = create_relative_config_node(nvl, "port"); | |||||
return (pci_ahci_legacy_config_port(ports_nvl, 0, "cd", opts)); | |||||
} | |||||
static int | |||||
pci_ahci_hd_legacy_config(nvlist_t *nvl, const char *opts) | |||||
{ | |||||
nvlist_t *ports_nvl; | |||||
ports_nvl = create_relative_config_node(nvl, "port"); | |||||
return (pci_ahci_legacy_config_port(ports_nvl, 0, "hd", opts)); | |||||
} | |||||
static int | |||||
pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | |||||
{ | |||||
char bident[sizeof("XX:XX:XX")]; | char bident[sizeof("XX:XX:XX")]; | ||||
char node_name[sizeof("XX")]; | |||||
struct blockif_ctxt *bctxt; | struct blockif_ctxt *bctxt; | ||||
struct pci_ahci_softc *sc; | struct pci_ahci_softc *sc; | ||||
int ret, slots, p; | int atapi, ret, slots, p; | ||||
MD5_CTX mdctx; | MD5_CTX mdctx; | ||||
u_char digest[16]; | u_char digest[16]; | ||||
char *next, *next2; | const char *path, *type, *value; | ||||
char *bopt, *uopt, *xopts, *config; | nvlist_t *ports_nvl, *port_nvl; | ||||
FILE* fp; | |||||
size_t block_len; | |||||
int comma, optpos; | |||||
ret = 0; | ret = 0; | ||||
#ifdef AHCI_DEBUG | #ifdef AHCI_DEBUG | ||||
dbg = fopen("/tmp/log", "w+"); | dbg = fopen("/tmp/log", "w+"); | ||||
#endif | #endif | ||||
sc = calloc(1, sizeof(struct pci_ahci_softc)); | sc = calloc(1, sizeof(struct pci_ahci_softc)); | ||||
pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
sc->asc_pi = pi; | sc->asc_pi = pi; | ||||
pthread_mutex_init(&sc->mtx, NULL); | pthread_mutex_init(&sc->mtx, NULL); | ||||
sc->ports = 0; | sc->ports = 0; | ||||
sc->pi = 0; | sc->pi = 0; | ||||
slots = 32; | slots = 32; | ||||
for (p = 0; p < MAX_PORTS && opts != NULL; p++, opts = next) { | ports_nvl = find_relative_config_node(nvl, "port"); | ||||
for (p = 0; p < MAX_PORTS; p++) { | |||||
struct ata_params *ata_ident = &sc->port[p].ata_ident; | struct ata_params *ata_ident = &sc->port[p].ata_ident; | ||||
memset(ata_ident, 0, sizeof(struct ata_params)); | char ident[AHCI_PORT_IDENT]; | ||||
/* Identify and cut off type of present port. */ | snprintf(node_name, sizeof(node_name), "%d", p); | ||||
if (strncmp(opts, "hd:", 3) == 0) { | port_nvl = find_relative_config_node(ports_nvl, node_name); | ||||
atapi = 0; | if (port_nvl == NULL) | ||||
opts += 3; | continue; | ||||
} else if (strncmp(opts, "cd:", 3) == 0) { | |||||
atapi = 1; | |||||
opts += 3; | |||||
} | |||||
/* Find and cut off the next port options. */ | type = get_config_value_node(port_nvl, "type"); | ||||
next = strstr(opts, ",hd:"); | if (type == NULL) | ||||
next2 = strstr(opts, ",cd:"); | |||||
if (next == NULL || (next2 != NULL && next2 < next)) | |||||
next = next2; | |||||
if (next != NULL) { | |||||
next[0] = 0; | |||||
next++; | |||||
} | |||||
if (opts[0] == 0) | |||||
continue; | continue; | ||||
uopt = strdup(opts); | if (strcmp(type, "hd") == 0) | ||||
bopt = NULL; | atapi = 0; | ||||
fp = open_memstream(&bopt, &block_len); | else | ||||
comma = 0; | atapi = 1; | ||||
optpos = 0; | |||||
for (xopts = strtok(uopt, ","); | |||||
xopts != NULL; | |||||
xopts = strtok(NULL, ",")) { | |||||
/* First option assume as block filename. */ | |||||
if (optpos == 0) { | |||||
/* | /* | ||||
* Create an identifier for the backing file. | |||||
* Use parts of the md5 sum of the filename | |||||
*/ | |||||
char ident[AHCI_PORT_IDENT]; | |||||
MD5Init(&mdctx); | |||||
MD5Update(&mdctx, opts, strlen(opts)); | |||||
MD5Final(digest, &mdctx); | |||||
snprintf(ident, AHCI_PORT_IDENT, | |||||
"BHYVE-%02X%02X-%02X%02X-%02X%02X", | |||||
digest[0], digest[1], digest[2], digest[3], digest[4], | |||||
digest[5]); | |||||
ata_string((uint8_t*)&ata_ident->serial, ident, 20); | |||||
ata_string((uint8_t*)&ata_ident->revision, "001", 8); | |||||
if (atapi) { | |||||
ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DVD ROM", 40); | |||||
} | |||||
else { | |||||
ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DISK", 40); | |||||
} | |||||
} | |||||
if ((config = strchr(xopts, '=')) != NULL) { | |||||
*config++ = '\0'; | |||||
if (!strcmp("nmrr", xopts)) { | |||||
ata_ident->media_rotation_rate = atoi(config); | |||||
} | |||||
else if (!strcmp("ser", xopts)) { | |||||
ata_string((uint8_t*)(&ata_ident->serial), config, 20); | |||||
} | |||||
else if (!strcmp("rev", xopts)) { | |||||
ata_string((uint8_t*)(&ata_ident->revision), config, 8); | |||||
} | |||||
else if (!strcmp("model", xopts)) { | |||||
ata_string((uint8_t*)(&ata_ident->model), config, 40); | |||||
} | |||||
else { | |||||
/* Pass all other options to blockif_open. */ | |||||
*--config = '='; | |||||
fprintf(fp, "%s%s", comma ? "," : "", xopts); | |||||
comma = 1; | |||||
} | |||||
} | |||||
else { | |||||
/* Pass all other options to blockif_open. */ | |||||
fprintf(fp, "%s%s", comma ? "," : "", xopts); | |||||
comma = 1; | |||||
} | |||||
optpos++; | |||||
} | |||||
free(uopt); | |||||
fclose(fp); | |||||
DPRINTF("%s\n", bopt); | |||||
/* | |||||
* Attempt to open the backing image. Use the PCI slot/func | * Attempt to open the backing image. Use the PCI slot/func | ||||
* and the port number for the identifier string. | * and the port number for the identifier string. | ||||
*/ | */ | ||||
snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot, | snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot, | ||||
pi->pi_func, p); | pi->pi_func, p); | ||||
bctxt = blockif_open(bopt, bident); | |||||
free(bopt); | |||||
bctxt = blockif_open(port_nvl, bident); | |||||
if (bctxt == NULL) { | if (bctxt == NULL) { | ||||
sc->ports = p; | sc->ports = p; | ||||
ret = 1; | ret = 1; | ||||
goto open_fail; | goto open_fail; | ||||
} | } | ||||
sc->port[p].bctx = bctxt; | sc->port[p].bctx = bctxt; | ||||
sc->port[p].pr_sc = sc; | sc->port[p].pr_sc = sc; | ||||
sc->port[p].port = p; | sc->port[p].port = p; | ||||
sc->port[p].atapi = atapi; | sc->port[p].atapi = atapi; | ||||
/* | |||||
* Create an identifier for the backing file. | |||||
* Use parts of the md5 sum of the filename | |||||
*/ | |||||
path = get_config_value_node(port_nvl, "path"); | |||||
MD5Init(&mdctx); | |||||
MD5Update(&mdctx, path, strlen(path)); | |||||
MD5Final(digest, &mdctx); | |||||
snprintf(ident, AHCI_PORT_IDENT, | |||||
"BHYVE-%02X%02X-%02X%02X-%02X%02X", | |||||
digest[0], digest[1], digest[2], digest[3], digest[4], | |||||
digest[5]); | |||||
memset(ata_ident, 0, sizeof(struct ata_params)); | |||||
ata_string((uint8_t*)&ata_ident->serial, ident, 20); | |||||
ata_string((uint8_t*)&ata_ident->revision, "001", 8); | |||||
if (atapi) | |||||
ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DVD ROM", 40); | |||||
else | |||||
ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DISK", 40); | |||||
value = get_config_value_node(port_nvl, "nmrr"); | |||||
if (value != NULL) | |||||
ata_ident->media_rotation_rate = atoi(value); | |||||
value = get_config_value_node(port_nvl, "ser"); | |||||
if (value != NULL) | |||||
ata_string((uint8_t*)(&ata_ident->serial), value, 20); | |||||
value = get_config_value_node(port_nvl, "rev"); | |||||
if (value != NULL) | |||||
ata_string((uint8_t*)(&ata_ident->revision), value, 8); | |||||
value = get_config_value_node(port_nvl, "model"); | |||||
if (value != NULL) | |||||
ata_string((uint8_t*)(&ata_ident->model), value, 40); | |||||
ata_identify_init(&sc->port[p], atapi); | ata_identify_init(&sc->port[p], atapi); | ||||
/* | /* | ||||
* Allocate blockif request structures and add them | * Allocate blockif request structures and add them | ||||
* to the free list | * to the free list | ||||
*/ | */ | ||||
pci_ahci_ioreq_init(&sc->port[p]); | pci_ahci_ioreq_init(&sc->port[p]); | ||||
Show All 37 Lines | for (p = 0; p < sc->ports; p++) { | ||||
blockif_close(sc->port[p].bctx); | blockif_close(sc->port[p].bctx); | ||||
} | } | ||||
free(sc); | free(sc); | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | |||||
pci_ahci_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | |||||
{ | |||||
return (pci_ahci_init(ctx, pi, opts, 0)); | |||||
} | |||||
static int | |||||
pci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | |||||
{ | |||||
return (pci_ahci_init(ctx, pi, opts, 1)); | |||||
} | |||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
static int | static int | ||||
pci_ahci_snapshot_save_queues(struct ahci_port *port, | pci_ahci_snapshot_save_queues(struct ahci_port *port, | ||||
struct vm_snapshot_meta *meta) | struct vm_snapshot_meta *meta) | ||||
{ | { | ||||
int ret; | int ret; | ||||
int idx; | int idx; | ||||
struct ahci_ioreq *ioreq; | struct ahci_ioreq *ioreq; | ||||
▲ Show 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* Use separate emulation names to distinguish drive and atapi devices | * Use separate emulation names to distinguish drive and atapi devices | ||||
*/ | */ | ||||
struct pci_devemu pci_de_ahci = { | struct pci_devemu pci_de_ahci = { | ||||
.pe_emu = "ahci", | .pe_emu = "ahci", | ||||
.pe_init = pci_ahci_hd_init, | .pe_init = pci_ahci_init, | ||||
.pe_legacy_config = pci_ahci_legacy_config, | |||||
.pe_barwrite = pci_ahci_write, | .pe_barwrite = pci_ahci_write, | ||||
.pe_barread = pci_ahci_read, | .pe_barread = pci_ahci_read, | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
.pe_snapshot = pci_ahci_snapshot, | .pe_snapshot = pci_ahci_snapshot, | ||||
.pe_pause = pci_ahci_pause, | .pe_pause = pci_ahci_pause, | ||||
.pe_resume = pci_ahci_resume, | .pe_resume = pci_ahci_resume, | ||||
#endif | #endif | ||||
}; | }; | ||||
PCI_EMUL_SET(pci_de_ahci); | PCI_EMUL_SET(pci_de_ahci); | ||||
struct pci_devemu pci_de_ahci_hd = { | struct pci_devemu pci_de_ahci_hd = { | ||||
.pe_emu = "ahci-hd", | .pe_emu = "ahci-hd", | ||||
.pe_init = pci_ahci_hd_init, | .pe_legacy_config = pci_ahci_hd_legacy_config, | ||||
.pe_barwrite = pci_ahci_write, | .pe_alias = "ahci", | ||||
.pe_barread = pci_ahci_read, | |||||
#ifdef BHYVE_SNAPSHOT | |||||
.pe_snapshot = pci_ahci_snapshot, | |||||
.pe_pause = pci_ahci_pause, | |||||
.pe_resume = pci_ahci_resume, | |||||
#endif | |||||
}; | }; | ||||
PCI_EMUL_SET(pci_de_ahci_hd); | PCI_EMUL_SET(pci_de_ahci_hd); | ||||
struct pci_devemu pci_de_ahci_cd = { | struct pci_devemu pci_de_ahci_cd = { | ||||
.pe_emu = "ahci-cd", | .pe_emu = "ahci-cd", | ||||
grehan: Perhaps a flag could be used to indicate a legacy-only device name and retire the -hd and -cd… | |||||
Done Inline ActionsNot quite sure what you mean? Do you want '-s 0,ahci,<path>' to work without an hd: or cd: prefix? Or rather, did I break an existing feature that let you do '-s 0,ahci-cd,hd:<path>'? The current code assumes that ahci-cd and ahci-hd are only used with exactly one port (pci_ahci_hd_legacy_config only creates a single port and doesn't parse a string of drives the way pci_ahci_legacy_config does). jhb: Not quite sure what you mean? Do you want '-s 0,ahci,<path>' to work without an hd: or cd… | |||||
.pe_init = pci_ahci_atapi_init, | .pe_legacy_config = pci_ahci_cd_legacy_config, | ||||
.pe_barwrite = pci_ahci_write, | .pe_alias = "ahci", | ||||
.pe_barread = pci_ahci_read, | |||||
#ifdef BHYVE_SNAPSHOT | |||||
.pe_snapshot = pci_ahci_snapshot, | |||||
.pe_pause = pci_ahci_pause, | |||||
.pe_resume = pci_ahci_resume, | |||||
#endif | |||||
}; | }; | ||||
PCI_EMUL_SET(pci_de_ahci_cd); | PCI_EMUL_SET(pci_de_ahci_cd); |
Perhaps a flag could be used to indicate a legacy-only device name and retire the -hd and -cd names. It's now possible to create an ahci-cd node with "type=hd".