dm: Reorganize ACRN DM directory.

The current dm, all non-pci and non-acpi related files are put into
hw/platform directory. This is actually disturbed the meaning of
*platform*. The platform devices are mean of board and SoC specific
non-PCI devices, like usb devices, etc.

This patch refines the ACRN dm directory architecture.

For some common device logic files, likes block_if.c/uart_core.c or
usb_core.c. They will move to hw/ directly.

For platform architecture depended files, create arch/ under root dir.
And create sub-dir arch/x86 for x86 architecture, will create more
architectures in future. The pm.c will move to this new dir.

The hw/acpi will be moved to hw/platform/acpi due to acpi also be
considered as part of platform.

Signed-off-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Yu Wang
2018-05-09 11:30:55 +08:00
committed by Jack Ren
parent eebf5fec6f
commit 8c06b69622
7 changed files with 9 additions and 6 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,387 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
* OWNER 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.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "vmm.h"
#include "vmmapi.h"
#include "dm.h"
#include "acpi.h"
static inline int get_vcpu_pm_info(struct vmctx *ctx, int vcpu_id,
uint64_t pm_type, uint64_t *pm_info)
{
*pm_info = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
| ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
| (pm_type & PMCMD_TYPE_MASK);
return vm_get_cpu_state(ctx, pm_info);
}
static inline uint8_t get_vcpu_px_cnt(struct vmctx *ctx, int vcpu_id)
{
uint64_t px_cnt;
if (get_vcpu_pm_info(ctx, vcpu_id, PMCMD_GET_PX_CNT, &px_cnt)) {
return 0;
}
return (uint8_t)px_cnt;
}
uint8_t get_vcpu_cx_cnt(struct vmctx *ctx, int vcpu_id)
{
uint64_t cx_cnt;
if (get_vcpu_pm_info(ctx, vcpu_id, PMCMD_GET_CX_CNT, &cx_cnt)) {
return 0;
}
return (uint8_t)cx_cnt;
}
static int get_vcpu_px_data(struct vmctx *ctx, int vcpu_id,
int px_num, struct cpu_px_data *vcpu_px_data)
{
uint64_t *pm_ioctl_buf;
enum pm_cmd_type cmd_type = PMCMD_GET_PX_DATA;
pm_ioctl_buf = malloc(sizeof(struct cpu_px_data));
if (!pm_ioctl_buf) {
return -1;
}
*pm_ioctl_buf = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
| ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
| ((px_num << PMCMD_STATE_NUM_SHIFT) & PMCMD_STATE_NUM_MASK)
| cmd_type;
/* get and validate px data */
if (vm_get_cpu_state(ctx, pm_ioctl_buf)) {
free(pm_ioctl_buf);
return -1;
}
memcpy(vcpu_px_data, pm_ioctl_buf,
sizeof(struct cpu_px_data));
free(pm_ioctl_buf);
return 0;
}
int get_vcpu_cx_data(struct vmctx *ctx, int vcpu_id,
int cx_num, struct cpu_cx_data *vcpu_cx_data)
{
uint64_t *pm_ioctl_buf;
enum pm_cmd_type cmd_type = PMCMD_GET_CX_DATA;
pm_ioctl_buf = malloc(sizeof(struct cpu_cx_data));
if (!pm_ioctl_buf) {
return -1;
}
*pm_ioctl_buf = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
| ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
| ((cx_num << PMCMD_STATE_NUM_SHIFT) & PMCMD_STATE_NUM_MASK)
| cmd_type;
/* get and validate cx data */
if (vm_get_cpu_state(ctx, pm_ioctl_buf)) {
free(pm_ioctl_buf);
return -1;
}
memcpy(vcpu_cx_data, pm_ioctl_buf,
sizeof(struct cpu_cx_data));
free(pm_ioctl_buf);
return 0;
}
char *_asi_table[7] = { "SystemMemory",
"SystemIO",
"PCI_Config",
"EmbeddedControl",
"SMBus",
"PCC",
"FFixedHW"};
static char *get_asi_string(uint8_t space_id)
{
switch (space_id) {
case SPACE_SYSTEM_MEMORY:
return _asi_table[0];
case SPACE_SYSTEM_IO:
return _asi_table[1];
case SPACE_PCI_CONFIG:
return _asi_table[2];
case SPACE_Embedded_Control:
return _asi_table[3];
case SPACE_SMBUS:
return _asi_table[4];
case SPACE_PLATFORM_COMM:
return _asi_table[5];
case SPACE_FFixedHW:
return _asi_table[6];
default:
return NULL;
}
}
/* _CST: C-States
*/
void dsdt_write_cst(struct vmctx *ctx, int vcpu_id)
{
int i;
uint8_t vcpu_cx_cnt;
char *cx_asi;
struct acpi_generic_address cx_reg;
struct cpu_cx_data *vcpu_cx_data;
vcpu_cx_cnt = get_vcpu_cx_cnt(ctx, vcpu_id);
if (!vcpu_cx_cnt) {
return;
}
/* vcpu_cx_data start from C1, cx_cnt is total Cx entry num. */
vcpu_cx_data = malloc(vcpu_cx_cnt * sizeof(struct cpu_cx_data));
if (!vcpu_cx_data) {
return;
}
/* copy and validate cx data first */
for (i = 1; i <= vcpu_cx_cnt; i++) {
if (get_vcpu_cx_data(ctx, vcpu_id, i, vcpu_cx_data + i - 1)) {
/* something must be wrong, so skip the write. */
free(vcpu_cx_data);
return;
}
}
dsdt_line("");
dsdt_line(" Method (_CST, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (Package (0x%02X)", vcpu_cx_cnt + 1);
dsdt_line(" {");
dsdt_line(" 0x%02X,", vcpu_cx_cnt);
for (i = 0; i < vcpu_cx_cnt; i++) {
dsdt_line(" Package (0x04)");
dsdt_line(" {");
cx_reg = (vcpu_cx_data + i)->cx_reg;
cx_asi = get_asi_string(cx_reg.space_id);
dsdt_line(" ResourceTemplate ()");
dsdt_line(" {");
dsdt_line(" Register (%s,", cx_asi);
dsdt_line(" 0x%02x,", cx_reg.bit_width);
dsdt_line(" 0x%02x,", cx_reg.bit_offset);
dsdt_line(" 0x%016lx,", cx_reg.address);
dsdt_line(" 0x%02x,", cx_reg.access_size);
dsdt_line(" )");
dsdt_line(" },");
dsdt_line(" 0x%04X,", (vcpu_cx_data + i)->type);
dsdt_line(" 0x%04X,", (vcpu_cx_data + i)->latency);
dsdt_line(" 0x%04X", (vcpu_cx_data + i)->power);
if (i == (vcpu_cx_cnt - 1)) {
dsdt_line(" }");
} else {
dsdt_line(" },");
}
}
dsdt_line(" })");
dsdt_line(" }");
free(vcpu_cx_data);
}
/* _PPC: Performance Present Capabilities
* hard code _PPC to 0, all states are available.
*/
static void dsdt_write_ppc(void)
{
dsdt_line(" Name (_PPC, Zero)");
}
/* _PCT: Performance Control
* Both Performance Control and Status Register are set to FFixedHW
*/
static void dsdt_write_pct(void)
{
dsdt_line(" Method (_PCT, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (Package (0x02)");
dsdt_line(" {");
dsdt_line(" ResourceTemplate ()");
dsdt_line(" {");
dsdt_line(" Register (FFixedHW,");
dsdt_line(" 0x00,");
dsdt_line(" 0x00,");
dsdt_line(" 0x0000000000000000,");
dsdt_line(" ,)");
dsdt_line(" },");
dsdt_line("");
dsdt_line(" ResourceTemplate ()");
dsdt_line(" {");
dsdt_line(" Register (FFixedHW,");
dsdt_line(" 0x00,");
dsdt_line(" 0x00,");
dsdt_line(" 0x0000000000000000,");
dsdt_line(" ,)");
dsdt_line(" }");
dsdt_line(" })");
dsdt_line(" }");
}
/* _PSS: Performance Supported States
*/
static void dsdt_write_pss(struct vmctx *ctx, int vcpu_id)
{
uint8_t vcpu_px_cnt;
int i;
struct cpu_px_data *vcpu_px_data;
vcpu_px_cnt = get_vcpu_px_cnt(ctx, vcpu_id);
if (!vcpu_px_cnt) {
return;
}
vcpu_px_data = malloc(vcpu_px_cnt * sizeof(struct cpu_px_data));
if (!vcpu_px_data) {
return;
}
/* copy and validate px data first */
for (i = 0; i < vcpu_px_cnt; i++) {
if (get_vcpu_px_data(ctx, vcpu_id, i, vcpu_px_data + i)) {
/* something must be wrong, so skip the write. */
free(vcpu_px_data);
return;
}
}
dsdt_line("");
dsdt_line(" Method (_PSS, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (Package (0x%02X)", vcpu_px_cnt);
dsdt_line(" {");
for (i = 0; i < vcpu_px_cnt; i++) {
dsdt_line(" Package (0x%02X)", 6);
dsdt_line(" {");
dsdt_line(" 0x%08X,",
(vcpu_px_data + i)->core_frequency);
dsdt_line(" 0x%08X,",
(vcpu_px_data + i)->power);
dsdt_line(" 0x%08X,",
(vcpu_px_data + i)->transition_latency);
dsdt_line(" 0x%08X,",
(vcpu_px_data + i)->bus_master_latency);
dsdt_line(" 0x%08X,",
(vcpu_px_data + i)->control);
dsdt_line(" 0x%08X",
(vcpu_px_data + i)->status);
if (i == (vcpu_px_cnt - 1)) {
dsdt_line(" }");
} else {
dsdt_line(" },");
}
}
dsdt_line(" })");
dsdt_line(" }");
free(vcpu_px_data);
}
void pm_write_dsdt(struct vmctx *ctx, int ncpu)
{
int i;
/* Scope (_PR) */
dsdt_line("");
dsdt_line(" Scope (_PR)");
dsdt_line(" {");
for (i = 0; i < ncpu; i++) {
dsdt_line(" Processor (CPU%d, 0x%02X, 0x00000000, 0x00) {}",
i, i);
}
dsdt_line(" }");
dsdt_line("");
/* Scope (_PR.CPU(N)) */
for (i = 0; i < ncpu; i++) {
dsdt_line(" Scope (_PR.CPU%d)", i);
dsdt_line(" {");
dsdt_line("");
dsdt_write_pss(ctx, i);
dsdt_write_cst(ctx, i);
/* hard code _PPC and _PCT for all vpu */
if (i == 0) {
dsdt_write_ppc();
dsdt_write_pct();
} else {
dsdt_line(" Method (_PPC, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (^^CPU0._PPC)");
dsdt_line(" }");
dsdt_line("");
dsdt_line(" Method (_PCT, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (^^CPU0._PCT)");
dsdt_line(" }");
dsdt_line("");
}
dsdt_line(" }");
}
}

View File

@@ -1,936 +0,0 @@
/*-
* Copyright (c) 2013 Peter Grehan <grehan@freebsd.org>
* 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 ``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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <errno.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sysexits.h>
#include <unistd.h>
#include "dm.h"
#include "mevent.h"
#include "block_if.h"
#include "ahci.h"
/*
* Notes:
* The F_OFD_SETLK support is introduced in glibc 2.20.
* The glibc version on target board is above 2.20.
* The following code temporarily fixes up building issues on Ubuntu 14.04,
* where the glibc version is 2.19 by default.
* Theoretically we should use cross-compiling tool to compile applications.
*/
#ifndef F_OFD_SETLK
#define F_OFD_SETLK 37
#endif
#define BLOCKIF_SIG 0xb109b109
#define BLOCKIF_NUMTHR 8
#define BLOCKIF_MAXREQ (64 + BLOCKIF_NUMTHR)
/*
* Debug printf
*/
static int block_if_debug;
#define DPRINTF(params) do { if (block_if_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
enum blockop {
BOP_READ,
BOP_WRITE,
BOP_FLUSH,
BOP_DELETE
};
enum blockstat {
BST_FREE,
BST_BLOCK,
BST_PEND,
BST_BUSY,
BST_DONE
};
struct blockif_elem {
TAILQ_ENTRY(blockif_elem) link;
struct blockif_req *req;
enum blockop op;
enum blockstat status;
pthread_t tid;
off_t block;
};
struct blockif_ctxt {
int magic;
int fd;
int isblk;
int isgeom;
int candelete;
int rdonly;
off_t size;
int sub_file_assign;
off_t sub_file_start_lba;
struct flock fl;
int sectsz;
int psectsz;
int psectoff;
int closing;
pthread_t btid[BLOCKIF_NUMTHR];
pthread_mutex_t mtx;
pthread_cond_t cond;
/* Request elements and free/pending/busy queues */
TAILQ_HEAD(, blockif_elem) freeq;
TAILQ_HEAD(, blockif_elem) pendq;
TAILQ_HEAD(, blockif_elem) busyq;
struct blockif_elem reqs[BLOCKIF_MAXREQ];
};
static pthread_once_t blockif_once = PTHREAD_ONCE_INIT;
struct blockif_sig_elem {
pthread_mutex_t mtx;
pthread_cond_t cond;
int pending;
struct blockif_sig_elem *next;
};
static struct blockif_sig_elem *blockif_bse_head;
static int
blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op)
{
struct blockif_elem *be, *tbe;
off_t off;
int i;
be = TAILQ_FIRST(&bc->freeq);
assert(be != NULL);
assert(be->status == BST_FREE);
TAILQ_REMOVE(&bc->freeq, be, link);
be->req = breq;
be->op = op;
switch (op) {
case BOP_READ:
case BOP_WRITE:
case BOP_DELETE:
off = breq->offset;
for (i = 0; i < breq->iovcnt; i++)
off += breq->iov[i].iov_len;
break;
default:
/* off = OFF_MAX; */
off = 1 << (sizeof(off_t) - 1);
}
be->block = off;
TAILQ_FOREACH(tbe, &bc->pendq, link) {
if (tbe->block == breq->offset)
break;
}
if (tbe == NULL) {
TAILQ_FOREACH(tbe, &bc->busyq, link) {
if (tbe->block == breq->offset)
break;
}
}
if (tbe == NULL)
be->status = BST_PEND;
else
be->status = BST_BLOCK;
TAILQ_INSERT_TAIL(&bc->pendq, be, link);
return (be->status == BST_PEND);
}
static int
blockif_dequeue(struct blockif_ctxt *bc, pthread_t t, struct blockif_elem **bep)
{
struct blockif_elem *be;
TAILQ_FOREACH(be, &bc->pendq, link) {
if (be->status == BST_PEND)
break;
assert(be->status == BST_BLOCK);
}
if (be == NULL)
return 0;
TAILQ_REMOVE(&bc->pendq, be, link);
be->status = BST_BUSY;
be->tid = t;
TAILQ_INSERT_TAIL(&bc->busyq, be, link);
*bep = be;
return 1;
}
static void
blockif_complete(struct blockif_ctxt *bc, struct blockif_elem *be)
{
struct blockif_elem *tbe;
if (be->status == BST_DONE || be->status == BST_BUSY)
TAILQ_REMOVE(&bc->busyq, be, link);
else
TAILQ_REMOVE(&bc->pendq, be, link);
TAILQ_FOREACH(tbe, &bc->pendq, link) {
if (tbe->req->offset == be->block)
tbe->status = BST_PEND;
}
be->tid = 0;
be->status = BST_FREE;
be->req = NULL;
TAILQ_INSERT_TAIL(&bc->freeq, be, link);
}
static void
blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be, uint8_t *buf)
{
struct blockif_req *br;
off_t arg[2];
ssize_t clen, len, off, boff, voff;
int i, err;
br = be->req;
if (br->iovcnt <= 1)
buf = NULL;
err = 0;
switch (be->op) {
case BOP_READ:
if (buf == NULL) {
len = preadv(bc->fd, br->iov, br->iovcnt,
br->offset + bc->sub_file_start_lba);
if (len < 0)
err = errno;
else
br->resid -= len;
break;
}
i = 0;
off = voff = 0;
while (br->resid > 0) {
len = MIN(br->resid, MAXPHYS);
if (pread(bc->fd, buf, len, br->offset +
off + bc->sub_file_start_lba) < 0) {
err = errno;
break;
}
boff = 0;
do {
clen = MIN(len - boff, br->iov[i].iov_len -
voff);
memcpy(br->iov[i].iov_base + voff,
buf + boff, clen);
if (clen < br->iov[i].iov_len - voff)
voff += clen;
else {
i++;
voff = 0;
}
boff += clen;
} while (boff < len);
off += len;
br->resid -= len;
}
break;
case BOP_WRITE:
if (bc->rdonly) {
err = EROFS;
break;
}
if (buf == NULL) {
len = pwritev(bc->fd, br->iov, br->iovcnt,
br->offset + bc->sub_file_start_lba);
if (len < 0)
err = errno;
else
br->resid -= len;
break;
}
i = 0;
off = voff = 0;
while (br->resid > 0) {
len = MIN(br->resid, MAXPHYS);
boff = 0;
do {
clen = MIN(len - boff, br->iov[i].iov_len -
voff);
memcpy(buf + boff,
br->iov[i].iov_base + voff, clen);
if (clen < br->iov[i].iov_len - voff)
voff += clen;
else {
i++;
voff = 0;
}
boff += clen;
} while (boff < len);
if (pwrite(bc->fd, buf, len, br->offset +
off + bc->sub_file_start_lba) < 0) {
err = errno;
break;
}
off += len;
br->resid -= len;
}
break;
case BOP_FLUSH:
if (fsync(bc->fd))
err = errno;
break;
case BOP_DELETE:
/* only used by AHCI */
if (!bc->candelete)
err = EOPNOTSUPP;
else if (bc->rdonly)
err = EROFS;
else if (bc->isblk) {
arg[0] = br->offset;
arg[1] = br->resid;
if (ioctl(bc->fd, BLKDISCARD, arg))
err = errno;
else
br->resid = 0;
}
else
err = EOPNOTSUPP;
break;
default:
err = EINVAL;
break;
}
be->status = BST_DONE;
(*br->callback)(br, err);
}
static void *
blockif_thr(void *arg)
{
struct blockif_ctxt *bc;
struct blockif_elem *be;
pthread_t t;
uint8_t *buf;
bc = arg;
if (bc->isgeom)
buf = malloc(MAXPHYS);
else
buf = NULL;
t = pthread_self();
pthread_mutex_lock(&bc->mtx);
for (;;) {
while (blockif_dequeue(bc, t, &be)) {
pthread_mutex_unlock(&bc->mtx);
blockif_proc(bc, be, buf);
pthread_mutex_lock(&bc->mtx);
blockif_complete(bc, be);
}
/* Check ctxt status here to see if exit requested */
if (bc->closing)
break;
pthread_cond_wait(&bc->cond, &bc->mtx);
}
pthread_mutex_unlock(&bc->mtx);
if (buf)
free(buf);
pthread_exit(NULL);
return NULL;
}
static void
blockif_sigcont_handler(int signal)
{
struct blockif_sig_elem *bse;
WPRINTF(("block_if sigcont handler!\n"));
for (;;) {
/*
* Process the entire list even if not intended for
* this thread.
*/
do {
bse = blockif_bse_head;
if (bse == NULL)
return;
} while (!__sync_bool_compare_and_swap(
(uintptr_t *)&blockif_bse_head,
(uintptr_t)bse,
(uintptr_t)bse->next));
pthread_mutex_lock(&bse->mtx);
bse->pending = 0;
pthread_cond_signal(&bse->cond);
pthread_mutex_unlock(&bse->mtx);
}
}
static void
blockif_init(void)
{
signal(SIGCONT, blockif_sigcont_handler);
}
/*
* This function checks if the sub file range, specified by sub_start and
* sub_size, has any overlap with other sub file ranges with write access.
*/
static int
sub_file_validate(struct blockif_ctxt *bc, int fd, int read_only,
off_t sub_start, off_t sub_size)
{
struct flock *fl = &bc->fl;
memset(fl, 0, sizeof(struct flock));
fl->l_whence = SEEK_SET; /* offset base is start of file */
if (read_only)
fl->l_type = F_RDLCK;
else
fl->l_type = F_WRLCK;
fl->l_start = sub_start;
fl->l_len = sub_size;
/* use "open file description locks" to validate */
if (fcntl(fd, F_OFD_SETLK, fl) == -1) {
DPRINTF(("failed to lock subfile!\n"));
return -1;
}
/* Keep file lock on to prevent other sub files, until DM exits */
return 0;
}
void
sub_file_unlock(struct blockif_ctxt *bc)
{
struct flock *fl;
if (bc->sub_file_assign) {
fl = &bc->fl;
DPRINTF(("blockif: release file lock...\n"));
fl->l_type = F_UNLCK;
if (fcntl(bc->fd, F_OFD_SETLK, fl) == -1) {
fprintf(stderr, "blockif: failed to unlock subfile!\n");
exit(1);
}
DPRINTF(("blockif: release done\n"));
}
}
struct blockif_ctxt *
blockif_open(const char *optstr, const char *ident)
{
char tname[MAXCOMLEN + 1];
/* char name[MAXPATHLEN]; */
char *nopt, *xopts, *cp;
struct blockif_ctxt *bc;
struct stat sbuf;
/* struct diocgattr_arg arg; */
off_t size, psectsz, psectoff;
int extra, fd, i, sectsz;
int nocache, sync, ro, candelete, geom, ssopt, pssopt;
long sz;
long long b;
int err_code = -1;
off_t sub_file_start_lba, sub_file_size;
int sub_file_assign;
pthread_once(&blockif_once, blockif_init);
fd = -1;
ssopt = 0;
nocache = 0;
sync = 0;
ro = 0;
sub_file_assign = 0;
/*
* The first element in the optstring is always a pathname.
* Optional elements follow
*/
nopt = xopts = strdup(optstr);
if (!nopt) {
WPRINTF(("block_if.c: strdup retruns NULL\n"));
return NULL;
}
while (xopts != NULL) {
cp = strsep(&xopts, ",");
if (cp == nopt) /* file or device pathname */
continue;
else if (!strcmp(cp, "nocache"))
nocache = 1;
else if (!strcmp(cp, "sync") || !strcmp(cp, "direct"))
sync = 1;
else if (!strcmp(cp, "ro"))
ro = 1;
else if (sscanf(cp, "sectorsize=%d/%d", &ssopt, &pssopt) == 2)
;
else if (sscanf(cp, "sectorsize=%d", &ssopt) == 1)
pssopt = ssopt;
else if (sscanf(cp, "range=%ld/%ld", &sub_file_start_lba,
&sub_file_size) == 2)
sub_file_assign = 1;
else {
fprintf(stderr, "Invalid device option \"%s\"\n", cp);
goto err;
}
}
/* enforce a write-through policy by default */
nocache = 1;
sync = 1;
extra = 0;
if (nocache)
extra |= O_DIRECT;
if (sync)
extra |= O_SYNC;
fd = open(nopt, (ro ? O_RDONLY : O_RDWR) | extra);
if (fd < 0 && !ro) {
/* Attempt a r/w fail with a r/o open */
fd = open(nopt, O_RDONLY | extra);
ro = 1;
}
if (fd < 0) {
warn("Could not open backing file: %s", nopt);
goto err;
}
if (fstat(fd, &sbuf) < 0) {
warn("Could not stat backing file %s", nopt);
goto err;
}
/*
* Deal with raw devices
*/
size = sbuf.st_size;
sectsz = DEV_BSIZE;
psectsz = psectoff = 0;
candelete = geom = 0;
if (S_ISBLK(sbuf.st_mode)) {
/* get size */
err_code = ioctl(fd, BLKGETSIZE, &sz);
if (err_code) {
fprintf(stderr, "error %d getting block size!\n",
err_code);
size = sbuf.st_size; /* set default value */
} else {
size = sz * DEV_BSIZE; /* DEV_BSIZE is 512 on Linux */
}
if (!err_code || err_code == EFBIG) {
err_code = ioctl(fd, BLKGETSIZE64, &b);
if (err_code || b == 0 || b == sz)
size = b * DEV_BSIZE;
else
size = b;
}
DPRINTF(("block partition size is 0x%lx\n", size));
/* get sector size, 512 on Linux */
sectsz = DEV_BSIZE;
DPRINTF(("block partition sector size is 0x%x\n", sectsz));
/* get physical sector size */
err_code = ioctl(fd, BLKPBSZGET, &psectsz);
if (err_code) {
fprintf(stderr, "error %d getting physical sectsz!\n",
err_code);
psectsz = DEV_BSIZE; /* set default physical size */
}
DPRINTF(("block partition physical sector size is 0x%lx\n",
psectsz));
} else
psectsz = sbuf.st_blksize;
if (ssopt != 0) {
if (!powerof2(ssopt) || !powerof2(pssopt) || ssopt < 512 ||
ssopt > pssopt) {
fprintf(stderr, "Invalid sector size %d/%d\n",
ssopt, pssopt);
goto err;
}
/*
* Some backend drivers (e.g. cd0, ada0) require that the I/O
* size be a multiple of the device's sector size.
*
* Validate that the emulated sector size complies with this
* requirement.
*/
if (S_ISCHR(sbuf.st_mode)) {
if (ssopt < sectsz || (ssopt % sectsz) != 0) {
fprintf(stderr,
"Sector size %d incompatible with underlying device sector size %d\n",
ssopt, sectsz);
goto err;
}
}
sectsz = ssopt;
psectsz = pssopt;
psectoff = 0;
}
bc = calloc(1, sizeof(struct blockif_ctxt));
if (bc == NULL) {
perror("calloc");
goto err;
}
if (sub_file_assign) {
DPRINTF(("sector size is %d\n", sectsz));
bc->sub_file_assign = 1;
bc->sub_file_start_lba = sub_file_start_lba * sectsz;
size = sub_file_size * sectsz;
DPRINTF(("Validating sub file...\n"));
err_code = sub_file_validate(bc, fd, ro, bc->sub_file_start_lba,
size);
if (err_code < 0) {
fprintf(stderr, "subfile range specified not valid!\n");
exit(1);
}
DPRINTF(("Validated done!\n"));
} else {
/* normal case */
bc->sub_file_assign = 0;
bc->sub_file_start_lba = 0;
}
bc->magic = BLOCKIF_SIG;
bc->fd = fd;
bc->isblk = S_ISBLK(sbuf.st_mode);
bc->isgeom = geom;
bc->candelete = candelete;
bc->rdonly = ro;
bc->size = size;
bc->sectsz = sectsz;
bc->psectsz = psectsz;
bc->psectoff = psectoff;
pthread_mutex_init(&bc->mtx, NULL);
pthread_cond_init(&bc->cond, NULL);
TAILQ_INIT(&bc->freeq);
TAILQ_INIT(&bc->pendq);
TAILQ_INIT(&bc->busyq);
for (i = 0; i < BLOCKIF_MAXREQ; i++) {
bc->reqs[i].status = BST_FREE;
TAILQ_INSERT_HEAD(&bc->freeq, &bc->reqs[i], link);
}
for (i = 0; i < BLOCKIF_NUMTHR; i++) {
pthread_create(&bc->btid[i], NULL, blockif_thr, bc);
snprintf(tname, sizeof(tname), "blk-%s-%d", ident, i);
pthread_setname_np(bc->btid[i], tname);
}
return bc;
err:
if (fd >= 0)
close(fd);
return NULL;
}
static int
blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op)
{
int err;
err = 0;
pthread_mutex_lock(&bc->mtx);
if (!TAILQ_EMPTY(&bc->freeq)) {
/*
* Enqueue and inform the block i/o thread
* that there is work available
*/
if (blockif_enqueue(bc, breq, op))
pthread_cond_signal(&bc->cond);
} else {
/*
* Callers are not allowed to enqueue more than
* the specified blockif queue limit. Return an
* error to indicate that the queue length has been
* exceeded.
*/
err = E2BIG;
}
pthread_mutex_unlock(&bc->mtx);
return err;
}
int
blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_READ);
}
int
blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_WRITE);
}
int
blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_FLUSH);
}
int
blockif_delete(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_DELETE);
}
int
blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
{
struct blockif_elem *be;
assert(bc->magic == BLOCKIF_SIG);
pthread_mutex_lock(&bc->mtx);
/*
* Check pending requests.
*/
TAILQ_FOREACH(be, &bc->pendq, link) {
if (be->req == breq)
break;
}
if (be != NULL) {
/*
* Found it.
*/
blockif_complete(bc, be);
pthread_mutex_unlock(&bc->mtx);
return 0;
}
/*
* Check in-flight requests.
*/
TAILQ_FOREACH(be, &bc->busyq, link) {
if (be->req == breq)
break;
}
if (be == NULL) {
/*
* Didn't find it.
*/
pthread_mutex_unlock(&bc->mtx);
return -1;
}
/*
* Interrupt the processing thread to force it return
* prematurely via it's normal callback path.
*/
while (be->status == BST_BUSY) {
struct blockif_sig_elem bse, *old_head;
pthread_mutex_init(&bse.mtx, NULL);
pthread_cond_init(&bse.cond, NULL);
bse.pending = 1;
do {
old_head = blockif_bse_head;
bse.next = old_head;
} while (!__sync_bool_compare_and_swap((uintptr_t *)&
blockif_bse_head,
(uintptr_t)old_head,
(uintptr_t)&bse));
pthread_kill(be->tid, SIGCONT);
pthread_mutex_lock(&bse.mtx);
while (bse.pending)
pthread_cond_wait(&bse.cond, &bse.mtx);
pthread_mutex_unlock(&bse.mtx);
}
pthread_mutex_unlock(&bc->mtx);
/*
* The processing thread has been interrupted. Since it's not
* clear if the callback has been invoked yet, return EBUSY.
*/
return -EBUSY;
}
int
blockif_close(struct blockif_ctxt *bc)
{
void *jval;
int i;
assert(bc->magic == BLOCKIF_SIG);
sub_file_unlock(bc);
/*
* Stop the block i/o thread
*/
pthread_mutex_lock(&bc->mtx);
bc->closing = 1;
pthread_mutex_unlock(&bc->mtx);
pthread_cond_broadcast(&bc->cond);
for (i = 0; i < BLOCKIF_NUMTHR; i++)
pthread_join(bc->btid[i], &jval);
/* XXX Cancel queued i/o's ??? */
/*
* Release resources
*/
bc->magic = 0;
close(bc->fd);
free(bc);
return 0;
}
/*
* Return virtual C/H/S values for a given block. Use the algorithm
* outlined in the VHD specification to calculate values.
*/
void
blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h, uint8_t *s)
{
off_t sectors; /* total sectors of the block dev */
off_t hcyl; /* cylinders times heads */
uint16_t secpt; /* sectors per track */
uint8_t heads;
assert(bc->magic == BLOCKIF_SIG);
sectors = bc->size / bc->sectsz;
/* Clamp the size to the largest possible with CHS */
if (sectors > 65535UL*16*255)
sectors = 65535UL*16*255;
if (sectors >= 65536UL*16*63) {
secpt = 255;
heads = 16;
hcyl = sectors / secpt;
} else {
secpt = 17;
hcyl = sectors / secpt;
heads = (hcyl + 1023) / 1024;
if (heads < 4)
heads = 4;
if (hcyl >= (heads * 1024) || heads > 16) {
secpt = 31;
heads = 16;
hcyl = sectors / secpt;
}
if (hcyl >= (heads * 1024)) {
secpt = 63;
heads = 16;
hcyl = sectors / secpt;
}
}
*c = hcyl / heads;
*h = heads;
*s = secpt;
}
/*
* Accessors
*/
off_t
blockif_size(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->size;
}
int
blockif_sectsz(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->sectsz;
}
void
blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off)
{
assert(bc->magic == BLOCKIF_SIG);
*size = bc->psectsz;
*off = bc->psectoff;
}
int
blockif_queuesz(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return (BLOCKIF_MAXREQ - 1);
}
int
blockif_is_ro(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->rdonly;
}
int
blockif_candelete(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->candelete;
}

View File

@@ -1,310 +0,0 @@
/*-
* Copyright (c) 2013 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* 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.
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include "vmmapi.h"
#include "vmm.h"
#include "acpi.h"
#include "inout.h"
#include "mevent.h"
#include "irq.h"
#include "lpc.h"
static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
static struct mevent *power_button;
static sig_t old_power_handler;
/*
* Reset Control register at I/O port 0xcf9. Bit 2 forces a system
* reset when it transitions from 0 to 1. Bit 1 selects the type of
* reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
* reset.
*/
static int
reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
int error;
static uint8_t reset_control;
if (bytes != 1)
return -1;
if (in)
*eax = reset_control;
else {
reset_control = *eax;
/* Treat hard and soft resets the same. */
if (reset_control & 0x4) {
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
}
/* cold reset should clear the value in 0xcf9 */
if (reset_control & 0x8) {
reset_control = 0;
}
}
return 0;
}
INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
/*
* ACPI's SCI is a level-triggered interrupt.
*/
static int sci_active;
static void
sci_assert(struct vmctx *ctx)
{
if (sci_active)
return;
vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
sci_active = 1;
}
static void
sci_deassert(struct vmctx *ctx)
{
if (!sci_active)
return;
vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
sci_active = 0;
}
/*
* Power Management 1 Event Registers
*
* The only power management event supported is a power button upon
* receiving SIGTERM.
*/
static uint16_t pm1_enable, pm1_status;
#define PM1_TMR_STS 0x0001
#define PM1_BM_STS 0x0010
#define PM1_GBL_STS 0x0020
#define PM1_PWRBTN_STS 0x0100
#define PM1_SLPBTN_STS 0x0200
#define PM1_RTC_STS 0x0400
#define PM1_WAK_STS 0x8000
#define PM1_TMR_EN 0x0001
#define PM1_GBL_EN 0x0020
#define PM1_PWRBTN_EN 0x0100
#define PM1_SLPBTN_EN 0x0200
#define PM1_RTC_EN 0x0400
static void
sci_update(struct vmctx *ctx)
{
int need_sci;
/* See if the SCI should be active or not. */
need_sci = 0;
if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
need_sci = 1;
if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
need_sci = 1;
if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
need_sci = 1;
if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
need_sci = 1;
if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
need_sci = 1;
if (need_sci)
sci_assert(ctx);
else
sci_deassert(ctx);
}
static int
pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
if (bytes != 2)
return -1;
pthread_mutex_lock(&pm_lock);
if (in)
*eax = pm1_status;
else {
/*
* Writes are only permitted to clear certain bits by
* writing 1 to those flags.
*/
pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
static int
pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
if (bytes != 2)
return -1;
pthread_mutex_lock(&pm_lock);
if (in)
*eax = pm1_enable;
else {
/*
* Only permit certain bits to be set. We never use
* the global lock, but ACPI-CA whines profusely if it
* can't set GBL_EN.
*/
pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
static void
power_button_handler(int signal, enum ev_type type, void *arg)
{
struct vmctx *ctx;
ctx = arg;
pthread_mutex_lock(&pm_lock);
if (!(pm1_status & PM1_PWRBTN_STS)) {
pm1_status |= PM1_PWRBTN_STS;
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
}
/*
* Power Management 1 Control Register
*
* This is mostly unimplemented except that we wish to handle writes that
* set SPL_EN to handle S5 (soft power off).
*/
static uint16_t pm1_control;
#define PM1_SCI_EN 0x0001
#define PM1_SLP_TYP 0x1c00
#define PM1_SLP_EN 0x2000
#define PM1_ALWAYS_ZERO 0xc003
static int
pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
int error;
if (bytes != 2)
return -1;
if (in)
*eax = pm1_control;
else {
/*
* Various bits are write-only or reserved, so force them
* to zero in pm1_control. Always preserve SCI_EN as OSPM
* can never change it.
*/
pm1_control = (pm1_control & PM1_SCI_EN) |
(*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
/*
* If SLP_EN is set, check for S5. ACRN-DM's _S5_ method
* says that '5' should be stored in SLP_TYP for S5.
*/
if (*eax & PM1_SLP_EN) {
if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) {
error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
assert(error == 0 || errno == EALREADY);
}
}
}
return 0;
}
INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
SYSRES_IO(PM1A_EVT_ADDR, 8);
/*
* ACPI SMI Command Register
*
* This write-only register is used to enable and disable ACPI.
*/
static int
smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
assert(!in);
if (bytes != 1)
return -1;
pthread_mutex_lock(&pm_lock);
switch (*eax) {
case ACPI_ENABLE:
pm1_control |= PM1_SCI_EN;
if (power_button == NULL) {
power_button = mevent_add(SIGTERM, EVF_SIGNAL,
power_button_handler, ctx);
old_power_handler = signal(SIGTERM, SIG_IGN);
}
break;
case ACPI_DISABLE:
pm1_control &= ~PM1_SCI_EN;
if (power_button != NULL) {
mevent_delete(power_button);
power_button = NULL;
signal(SIGTERM, old_power_handler);
}
break;
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
SYSRES_IO(SMI_CMD, 1);
void
sci_init(struct vmctx *ctx)
{
/*
* Mark ACPI's SCI as level trigger and bump its use count
* in the PIRQ router.
*/
pci_irq_use(SCI_INT);
}

View File

@@ -1,722 +0,0 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* 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 NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <sysexits.h>
#include "types.h"
#include "mevent.h"
#include "uart_core.h"
#include "ns16550.h"
#include "dm.h"
#define COM1_BASE 0x3F8
#define COM1_IRQ 4
#define COM2_BASE 0x2F8
#define COM2_IRQ 3
#define DEFAULT_RCLK 1843200
#define DEFAULT_BAUD 9600
#define FCR_RX_MASK 0xC0
#define MCR_OUT1 0x04
#define MCR_OUT2 0x08
#define MSR_DELTA_MASK 0x0f
#ifndef REG_SCR
#define REG_SCR com_scr
#endif
#define FIFOSZ 256
static struct termios tio_stdio_orig;
static struct {
int baseaddr;
int irq;
bool inuse;
} uart_lres[] = {
{ COM1_BASE, COM1_IRQ, false},
{ COM2_BASE, COM2_IRQ, false},
};
#define UART_NLDEVS (ARRAY_SIZE(uart_lres))
struct fifo {
uint8_t buf[FIFOSZ];
int rindex; /* index to read from */
int windex; /* index to write to */
int num; /* number of characters in the fifo */
int size; /* size of the fifo */
};
struct ttyfd {
bool opened;
int fd; /* tty device file descriptor */
struct termios tio_orig, tio_new; /* I/O Terminals */
};
struct uart_vdev {
pthread_mutex_t mtx; /* protects all elements */
uint8_t data; /* Data register (R/W) */
uint8_t ier; /* Interrupt enable register (R/W) */
uint8_t lcr; /* Line control register (R/W) */
uint8_t mcr; /* Modem control register (R/W) */
uint8_t lsr; /* Line status register (R/W) */
uint8_t msr; /* Modem status register (R/W) */
uint8_t fcr; /* FIFO control register (W) */
uint8_t scr; /* Scratch register (R/W) */
uint8_t dll; /* Baudrate divisor latch LSB */
uint8_t dlh; /* Baudrate divisor latch MSB */
struct fifo rxfifo;
struct mevent *mev;
struct ttyfd tty;
bool thre_int_pending; /* THRE interrupt pending */
void *arg;
uart_intr_func_t intr_assert;
uart_intr_func_t intr_deassert;
};
static void uart_drain(int fd, enum ev_type ev, void *arg);
static void
ttyclose(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
}
static void
ttyopen(struct ttyfd *tf)
{
tcgetattr(tf->fd, &tf->tio_orig);
tf->tio_new = tf->tio_orig;
cfmakeraw(&tf->tio_new);
tf->tio_new.c_cflag |= CLOCAL;
tcsetattr(tf->fd, TCSANOW, &tf->tio_new);
if (tf->fd == STDIN_FILENO) {
tio_stdio_orig = tf->tio_orig;
atexit(ttyclose);
}
}
static int
ttyread(struct ttyfd *tf)
{
unsigned char rb;
if (read(tf->fd, &rb, 1) > 0)
return rb;
return -1;
}
static int
ttywrite(struct ttyfd *tf, unsigned char wb)
{
if (write(tf->fd, &wb, 1) > 0)
return 1;
return -1;
}
static void
rxfifo_reset(struct uart_vdev *uart, int size)
{
char flushbuf[32];
struct fifo *fifo;
ssize_t nread;
int error;
fifo = &uart->rxfifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = size;
if (uart->tty.opened) {
/*
* Flush any unread input from the tty buffer.
*/
while (1) {
nread = read(uart->tty.fd, flushbuf, sizeof(flushbuf));
if (nread != sizeof(flushbuf))
break;
}
/*
* Enable mevent to trigger when new characters are available
* on the tty fd.
*/
error = mevent_enable(uart->mev);
assert(error == 0);
}
}
static int
rxfifo_available(struct uart_vdev *uart)
{
struct fifo *fifo;
fifo = &uart->rxfifo;
return (fifo->num < fifo->size);
}
static int
rxfifo_putchar(struct uart_vdev *uart, uint8_t ch)
{
struct fifo *fifo;
int error;
fifo = &uart->rxfifo;
if (fifo->num < fifo->size) {
fifo->buf[fifo->windex] = ch;
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
if (!rxfifo_available(uart)) {
if (uart->tty.opened) {
/*
* Disable mevent callback if the FIFO is full.
*/
error = mevent_disable(uart->mev);
assert(error == 0);
}
}
return 0;
} else
return -1;
}
static int
rxfifo_getchar(struct uart_vdev *uart)
{
struct fifo *fifo;
int c, error, wasfull;
wasfull = 0;
fifo = &uart->rxfifo;
if (fifo->num > 0) {
if (!rxfifo_available(uart))
wasfull = 1;
c = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
if (wasfull) {
if (uart->tty.opened) {
error = mevent_enable(uart->mev);
assert(error == 0);
}
}
return c;
} else
return -1;
}
static int
rxfifo_numchars(struct uart_vdev *uart)
{
struct fifo *fifo = &uart->rxfifo;
return fifo->num;
}
static void
uart_opentty(struct uart_vdev *uart)
{
ttyopen(&uart->tty);
if (isatty(uart->tty.fd)) {
uart->mev = mevent_add(uart->tty.fd, EVF_READ,
uart_drain, uart);
assert(uart->mev != NULL);
}
}
static void
uart_closetty(struct uart_vdev *uart)
{
if (uart->tty.fd != STDIN_FILENO)
mevent_delete_close(uart->mev);
else
mevent_delete(uart->mev);
uart->mev = 0;
ttyclose();
}
static uint8_t
modem_status(uint8_t mcr)
{
uint8_t msr;
if (mcr & MCR_LOOPBACK) {
/*
* In the loopback mode certain bits from the MCR are
* reflected back into MSR.
*/
msr = 0;
if (mcr & MCR_RTS)
msr |= MSR_CTS;
if (mcr & MCR_DTR)
msr |= MSR_DSR;
if (mcr & MCR_OUT1)
msr |= MSR_RI;
if (mcr & MCR_OUT2)
msr |= MSR_DCD;
} else {
/*
* Always assert DCD and DSR so tty open doesn't block
* even if CLOCAL is turned off.
*/
msr = MSR_DCD | MSR_DSR;
}
assert((msr & MSR_DELTA_MASK) == 0);
return msr;
}
/*
* The IIR returns a prioritized interrupt reason:
* - receive data available
* - transmit holding register empty
* - modem status change
*
* Return an interrupt reason if one is available.
*/
static int
uart_intr_reason(struct uart_vdev *uart)
{
if ((uart->lsr & LSR_OE) != 0 && (uart->ier & IER_ERLS) != 0)
return IIR_RLS;
else if (rxfifo_numchars(uart) > 0 && (uart->ier & IER_ERXRDY) != 0)
return IIR_RXTOUT;
else if (uart->thre_int_pending && (uart->ier & IER_ETXRDY) != 0)
return IIR_TXRDY;
else if ((uart->msr & MSR_DELTA_MASK) != 0 &&
(uart->ier & IER_EMSC) != 0)
return IIR_MLSC;
else
return IIR_NOPEND;
}
static void
uart_reset(struct uart_vdev *uart)
{
uint16_t divisor;
divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
uart->dll = divisor;
uart->dlh = divisor >> 16;
uart->msr = modem_status(uart->mcr);
rxfifo_reset(uart, 1); /* no fifo until enabled by software */
}
/*
* Toggle the COM port's intr pin depending on whether or not we have an
* interrupt condition to report to the processor.
*/
static void
uart_toggle_intr(struct uart_vdev *uart)
{
uint8_t intr_reason;
intr_reason = uart_intr_reason(uart);
if (intr_reason == IIR_NOPEND)
(*uart->intr_deassert)(uart->arg);
else
(*uart->intr_assert)(uart->arg);
}
static void
uart_drain(int fd, enum ev_type ev, void *arg)
{
struct uart_vdev *uart;
int ch;
uart = arg;
assert(fd == uart->tty.fd);
assert(ev == EVF_READ);
/*
* This routine is called in the context of the mevent thread
* to take out the uart lock to protect against concurrent
* access from a vCPU i/o exit
*/
pthread_mutex_lock(&uart->mtx);
if ((uart->mcr & MCR_LOOPBACK) != 0) {
(void) ttyread(&uart->tty);
} else {
while ((ch = ttyread(&uart->tty)) != -1)
rxfifo_putchar(uart, ch);
uart_toggle_intr(uart);
}
pthread_mutex_unlock(&uart->mtx);
}
void
uart_write(struct uart_vdev *uart, int offset, uint8_t value)
{
int fifosz;
uint8_t msr;
pthread_mutex_lock(&uart->mtx);
/*
* Take care of the special case DLAB accesses first
*/
if ((uart->lcr & LCR_DLAB) != 0) {
if (offset == REG_DLL) {
uart->dll = value;
goto done;
}
if (offset == REG_DLH) {
uart->dlh = value;
goto done;
}
}
switch (offset) {
case REG_DATA:
if (uart->mcr & MCR_LOOPBACK) {
if (rxfifo_putchar(uart, value) != 0)
uart->lsr |= LSR_OE;
} else if (uart->tty.opened) {
ttywrite(&uart->tty, value);
} /* else drop on floor */
uart->thre_int_pending = true;
break;
case REG_IER:
/*
* Apply mask so that bits 4-7 are 0
* Also enables bits 0-3 only if they're 1
*/
uart->ier = value & 0x0F;
break;
case REG_FCR:
/*
* When moving from FIFO and 16450 mode and vice versa,
* the FIFO contents are reset.
*/
if ((uart->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
rxfifo_reset(uart, fifosz);
}
/*
* The FCR_ENABLE bit must be '1' for the programming
* of other FCR bits to be effective.
*/
if ((value & FCR_ENABLE) == 0) {
uart->fcr = 0;
} else {
if ((value & FCR_RCV_RST) != 0)
rxfifo_reset(uart, FIFOSZ);
uart->fcr = value &
(FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
}
break;
case REG_LCR:
uart->lcr = value;
break;
case REG_MCR:
/* Apply mask so that bits 5-7 are 0 */
uart->mcr = value & 0x1F;
msr = modem_status(uart->mcr);
/*
* Detect if there has been any change between the
* previous and the new value of MSR. If there is
* then assert the appropriate MSR delta bit.
*/
if ((msr & MSR_CTS) ^ (uart->msr & MSR_CTS))
uart->msr |= MSR_DCTS;
if ((msr & MSR_DSR) ^ (uart->msr & MSR_DSR))
uart->msr |= MSR_DDSR;
if ((msr & MSR_DCD) ^ (uart->msr & MSR_DCD))
uart->msr |= MSR_DDCD;
if ((uart->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
uart->msr |= MSR_TERI;
/*
* Update the value of MSR while retaining the delta
* bits.
*/
uart->msr &= MSR_DELTA_MASK;
uart->msr |= msr;
break;
case REG_LSR:
/*
* Line status register is not meant to be written to
* during normal operation.
*/
break;
case REG_MSR:
/*
* As far as I can tell MSR is a read-only register.
*/
break;
case REG_SCR:
uart->scr = value;
break;
default:
break;
}
done:
uart_toggle_intr(uart);
pthread_mutex_unlock(&uart->mtx);
}
uint8_t
uart_read(struct uart_vdev *uart, int offset)
{
uint8_t iir, intr_reason, reg;
pthread_mutex_lock(&uart->mtx);
/*
* Take care of the special case DLAB accesses first
*/
if ((uart->lcr & LCR_DLAB) != 0) {
if (offset == REG_DLL) {
reg = uart->dll;
goto done;
}
if (offset == REG_DLH) {
reg = uart->dlh;
goto done;
}
}
switch (offset) {
case REG_DATA:
reg = rxfifo_getchar(uart);
break;
case REG_IER:
reg = uart->ier;
break;
case REG_IIR:
iir = (uart->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
intr_reason = uart_intr_reason(uart);
/*
* Deal with side effects of reading the IIR register
*/
if (intr_reason == IIR_TXRDY)
uart->thre_int_pending = false;
iir |= intr_reason;
reg = iir;
break;
case REG_LCR:
reg = uart->lcr;
break;
case REG_MCR:
reg = uart->mcr;
break;
case REG_LSR:
/* Transmitter is always ready for more data */
uart->lsr |= LSR_TEMT | LSR_THRE;
/* Check for new receive data */
if (rxfifo_numchars(uart) > 0)
uart->lsr |= LSR_RXRDY;
else
uart->lsr &= ~LSR_RXRDY;
reg = uart->lsr;
/* The LSR_OE bit is cleared on LSR read */
uart->lsr &= ~LSR_OE;
break;
case REG_MSR:
/*
* MSR delta bits are cleared on read
*/
reg = uart->msr;
uart->msr &= ~MSR_DELTA_MASK;
break;
case REG_SCR:
reg = uart->scr;
break;
default:
reg = 0xFF;
break;
}
done:
uart_toggle_intr(uart);
pthread_mutex_unlock(&uart->mtx);
return reg;
}
int
uart_legacy_alloc(int which, int *baseaddr, int *irq)
{
if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse)
return -1;
uart_lres[which].inuse = true;
*baseaddr = uart_lres[which].baseaddr;
*irq = uart_lres[which].irq;
return 0;
}
void
uart_legacy_dealloc(int which)
{
uart_lres[which].inuse = false;
}
struct uart_vdev *
uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
void *arg)
{
struct uart_vdev *uart;
uart = calloc(1, sizeof(struct uart_vdev));
assert(uart != NULL);
uart->arg = arg;
uart->intr_assert = intr_assert;
uart->intr_deassert = intr_deassert;
pthread_mutex_init(&uart->mtx, NULL);
uart_reset(uart);
return uart;
}
void
uart_deinit(struct uart_vdev *uart)
{
if (uart) {
if (uart->tty.opened && uart->tty.fd == STDIN_FILENO) {
ttyclose();
stdio_in_use = false;
}
free(uart);
}
}
static int
uart_tty_backend(struct uart_vdev *uart, const char *opts)
{
int fd;
int retval;
retval = -1;
fd = open(opts, O_RDWR | O_NONBLOCK);
if (fd > 0 && isatty(fd)) {
uart->tty.fd = fd;
uart->tty.opened = true;
retval = 0;
}
return retval;
}
int
uart_set_backend(struct uart_vdev *uart, const char *opts)
{
int retval;
retval = -1;
if (opts == NULL)
return 0;
if (strcmp("stdio", opts) == 0) {
if (!stdio_in_use) {
uart->tty.fd = STDIN_FILENO;
uart->tty.opened = true;
stdio_in_use = true;
retval = 0;
}
} else if (uart_tty_backend(uart, opts) == 0) {
retval = 0;
}
/* Make the backend file descriptor non-blocking */
if (retval == 0)
retval = fcntl(uart->tty.fd, F_SETFL, O_NONBLOCK);
if (retval == 0)
uart_opentty(uart);
return retval;
}
void
uart_release_backend(struct uart_vdev *uart, const char *opts)
{
if (opts == NULL)
return;
uart_closetty(uart);
if (strcmp("stdio", opts) == 0) {
stdio_in_use = false;
} else
close(uart->tty.fd);
uart->tty.fd = 0;
uart->tty.opened = false;
}

View File

@@ -1,73 +0,0 @@
/*-
* Copyright (c) 2014 Nahanni Systems Inc.
* 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.
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "usb_core.h"
SET_DECLARE(usb_emu_set, struct usb_devemu);
struct usb_devemu *
usb_emu_finddev(char *name)
{
struct usb_devemu **udpp, *udp;
SET_FOREACH(udpp, usb_emu_set) {
udp = *udpp;
if (!strcmp(udp->ue_emu, name))
return udp;
}
return NULL;
}
struct usb_data_xfer_block *
usb_data_xfer_append(struct usb_data_xfer *xfer, void *buf, int blen,
void *hci_data, int ccs)
{
struct usb_data_xfer_block *xb;
if (xfer->ndata >= USB_MAX_XFER_BLOCKS)
return NULL;
xb = &xfer->data[xfer->tail];
xb->buf = buf;
xb->blen = blen;
xb->hci_data = hci_data;
xb->ccs = ccs;
xb->processed = 0;
xb->bdone = 0;
xfer->ndata++;
xfer->tail = (xfer->tail + 1) % USB_MAX_XFER_BLOCKS;
return xb;
}