summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McDonald <danmcd@mnx.io>2022-11-16 11:19:58 -0500
committerDan McDonald <danmcd@mnx.io>2022-11-16 11:19:58 -0500
commit499546739d4558c1c4e8fd25d572b92ddf77883c (patch)
tree12f4d01c377962369d0882fe765a16009003ccac
parentcd75df2bda342d38890513e04267a306c9666ad3 (diff)
parent3cfbf5be38df79575cc7d2705bb059b2feca1332 (diff)
downloadillumos-joyent-release-20221117.tar.gz
[illumos-gate merge]release-20221117
commit 3cfbf5be38df79575cc7d2705bb059b2feca1332 15155 WPTS BVT_SMB2Basic_Query_FileNormalizedNameInformation commit bf4554a2fffed30a37fb5ff4467267a2eaeea07f 15154 WPTS FSA BVT_AlternateDataStream_ListStreams_File commit 2266458a6b75760fe455b5b5b6ab3421ea102c97 15153 WPTS FileInfo_Query_FileIdInformation_Dir/File fails commit b219643fc2667abe7d09cda95ef286f8b16dedc6 15150 WPTS FileInfo_Query_FileAccessInformation_DataSuffix commit 9621f52ed119bdb5188286f4d81d713cf7ce8a08 15127 libnsl: '&' within '^' commit 4ac713da4ff2c45287699af975f8c98142bbd9d3 14251 operationalize bhyve dirty page tracking
-rw-r--r--usr/src/lib/libnsl/rpc/clnt_vc.c60
-rw-r--r--usr/src/test/bhyve-tests/runfiles/default.run3
-rw-r--r--usr/src/test/bhyve-tests/tests/common/common.c (renamed from usr/src/test/bhyve-tests/tests/vmm/common.c)0
-rw-r--r--usr/src/test/bhyve-tests/tests/common/common.h (renamed from usr/src/test/bhyve-tests/tests/vmm/common.h)0
-rw-r--r--usr/src/test/bhyve-tests/tests/common/in_guest.c11
-rw-r--r--usr/src/test/bhyve-tests/tests/common/in_guest.h2
-rw-r--r--usr/src/test/bhyve-tests/tests/inst_emul/Makefile2
-rw-r--r--usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c79
-rw-r--r--usr/src/test/bhyve-tests/tests/vmm/Makefile7
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c99
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_pathname.c95
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c207
-rw-r--r--usr/src/uts/common/smbsrv/ntifs.h6
-rw-r--r--usr/src/uts/intel/io/vmm/sys/vmm_gpt.h8
-rw-r--r--usr/src/uts/intel/io/vmm/sys/vmm_kernel.h2
-rw-r--r--usr/src/uts/intel/io/vmm/sys/vmm_vm.h4
-rw-r--r--usr/src/uts/intel/io/vmm/vmm.c16
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_gpt.c5
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_sol_dev.c5
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_sol_ept.c18
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_sol_rvi.c12
-rw-r--r--usr/src/uts/intel/io/vmm/vmm_vm.c9
-rw-r--r--usr/src/uts/intel/sys/vmm.h5
-rw-r--r--usr/src/uts/intel/sys/vmm_dev.h2
24 files changed, 431 insertions, 226 deletions
diff --git a/usr/src/lib/libnsl/rpc/clnt_vc.c b/usr/src/lib/libnsl/rpc/clnt_vc.c
index 77c6d0b290..19112b52e6 100644
--- a/usr/src/lib/libnsl/rpc/clnt_vc.c
+++ b/usr/src/lib/libnsl/rpc/clnt_vc.c
@@ -133,9 +133,9 @@ struct ct_data {
ushort_t ct_blocking_mode;
uint_t ct_bufferSize; /* Total size of the buffer. */
uint_t ct_bufferPendingSize; /* Size of unsent data. */
- char *ct_buffer; /* Pointer to the buffer. */
- char *ct_bufferWritePtr; /* Ptr to the first free byte. */
- char *ct_bufferReadPtr; /* Ptr to the first byte of data. */
+ char *ct_buffer; /* Pointer to the buffer. */
+ char *ct_bufferWritePtr; /* Ptr to the first free byte. */
+ char *ct_bufferReadPtr; /* Ptr to the first byte of data. */
};
struct nb_reg_node {
@@ -216,7 +216,7 @@ set_blocking_connection(struct ct_data *ct, bool_t blocking)
*/
CLIENT *
clnt_vc_create(const int fd, struct netbuf *svcaddr, const rpcprog_t prog,
- const rpcvers_t vers, const uint_t sendsz, const uint_t recvsz)
+ const rpcvers_t vers, const uint_t sendsz, const uint_t recvsz)
{
return (_clnt_vc_create_timed(fd, svcaddr, prog, vers, sendsz,
recvsz, NULL));
@@ -235,7 +235,7 @@ clnt_vc_create(const int fd, struct netbuf *svcaddr, const rpcprog_t prog,
*/
CLIENT *
_clnt_vc_create_timed(int fd, struct netbuf *svcaddr, rpcprog_t prog,
- rpcvers_t vers, uint_t sendsz, uint_t recvsz, const struct timeval *tp)
+ rpcvers_t vers, uint_t sendsz, uint_t recvsz, const struct timeval *tp)
{
CLIENT *cl; /* client handle */
struct ct_data *ct; /* private data */
@@ -395,7 +395,6 @@ _set_tcp_conntime(int fd, int optval)
int *ip;
char buf[TCPOPT_BUFSIZE];
- /* LINTED pointer cast */
opt = (struct opthdr *)buf;
opt->level = IPPROTO_TCP;
opt->name = TCP_CONN_ABORT_THRESHOLD;
@@ -404,7 +403,6 @@ _set_tcp_conntime(int fd, int optval)
req.flags = T_NEGOTIATE;
req.opt.len = sizeof (struct opthdr) + opt->len;
req.opt.buf = (char *)opt;
- /* LINTED pointer cast */
ip = (int *)((char *)buf + sizeof (struct opthdr));
*ip = optval;
@@ -429,7 +427,6 @@ _get_tcp_conntime(int fd)
int *ip, retval;
char buf[TCPOPT_BUFSIZE];
- /* LINTED pointer cast */
opt = (struct opthdr *)buf;
opt->level = IPPROTO_TCP;
opt->name = TCP_CONN_ABORT_THRESHOLD;
@@ -438,7 +435,6 @@ _get_tcp_conntime(int fd)
req.flags = T_CURRENT;
req.opt.len = sizeof (struct opthdr) + opt->len;
req.opt.buf = (char *)opt;
- /* LINTED pointer cast */
ip = (int *)((char *)buf + sizeof (struct opthdr));
*ip = 0;
@@ -449,7 +445,6 @@ _get_tcp_conntime(int fd)
return (-1);
}
- /* LINTED pointer cast */
ip = (int *)((char *)buf + sizeof (struct opthdr));
retval = *ip;
return (retval);
@@ -485,7 +480,6 @@ set_up_connection(int fd, struct netbuf *svcaddr, struct ct_data *ct,
/*
* Connect only if state is IDLE and svcaddr known
*/
-/* LINTED pointer alignment */
rcvcall = (struct t_call *)t_alloc(fd, T_CALL, T_OPT|T_ADDR);
if (rcvcall == NULL) {
rpc_createerr.cf_stat = RPC_TLIERROR;
@@ -642,14 +636,12 @@ set_up_connection(int fd, struct netbuf *svcaddr, struct ct_data *ct,
static enum clnt_stat
clnt_vc_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xdr_args, caddr_t args_ptr,
- xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout)
+ xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout)
{
-/* LINTED pointer alignment */
struct ct_data *ct = (struct ct_data *)cl->cl_private;
XDR *xdrs = &(ct->ct_xdrs);
struct rpc_msg reply_msg;
uint32_t x_id;
-/* LINTED pointer alignment */
uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */
bool_t shipnow;
int refreshes = 2;
@@ -702,7 +694,6 @@ call_again:
return (rpc_callerr.re_status);
}
} else {
-/* LINTED pointer alignment */
uint32_t *u = (uint32_t *)&ct->ct_mcall[ct->ct_mpos];
IXDR_PUT_U_INT32(u, proc);
if (!__rpc_gss_wrap(cl->cl_auth, ct->ct_mcall,
@@ -810,11 +801,9 @@ call_again:
static enum clnt_stat
clnt_vc_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xdr_args, caddr_t args_ptr)
{
-/* LINTED pointer alignment */
struct ct_data *ct = (struct ct_data *)cl->cl_private;
XDR *xdrs = &(ct->ct_xdrs);
uint32_t x_id;
-/* LINTED pointer alignment */
uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */
if (rpc_fd_lock(vctbl, ct->ct_fd)) {
@@ -847,7 +836,6 @@ clnt_vc_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xdr_args, caddr_t args_ptr)
return (rpc_callerr.re_status);
}
} else {
-/* LINTED pointer alignment */
uint32_t *u = (uint32_t *)&ct->ct_mcall[ct->ct_mpos];
IXDR_PUT_U_INT32(u, proc);
if (!__rpc_gss_wrap(cl->cl_auth, ct->ct_mcall,
@@ -882,7 +870,6 @@ clnt_vc_geterr(CLIENT *cl, struct rpc_err *errp)
static bool_t
clnt_vc_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
{
-/* LINTED pointer alignment */
struct ct_data *ct = (struct ct_data *)cl->cl_private;
XDR *xdrs = &(ct->ct_xdrs);
bool_t stat;
@@ -899,12 +886,10 @@ clnt_vc_abort(void)
{
}
-/*ARGSUSED*/
static bool_t
clnt_vc_control(CLIENT *cl, int request, char *info)
{
bool_t ret;
-/* LINTED pointer alignment */
struct ct_data *ct = (struct ct_data *)cl->cl_private;
if (rpc_fd_lock(vctbl, ct->ct_fd)) {
@@ -925,9 +910,7 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
if (ct->ct_io_mode == RPC_CL_NONBLOCKING) {
int res;
res = do_flush(ct, (info == NULL ||
- /* LINTED pointer cast */
*(int *)info == RPC_CL_DEFAULT_FLUSH)?
- /* LINTED pointer cast */
ct->ct_blocking_mode: *(int *)info);
ret = (0 == res);
} else {
@@ -944,31 +927,25 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
}
switch (request) {
case CLSET_TIMEOUT:
-/* LINTED pointer alignment */
if (time_not_ok((struct timeval *)info)) {
rpc_fd_unlock(vctbl, ct->ct_fd);
return (FALSE);
}
-/* LINTED pointer alignment */
ct->ct_wait = __rpc_timeval_to_msec((struct timeval *)info);
ct->ct_waitset = TRUE;
break;
case CLGET_TIMEOUT:
-/* LINTED pointer alignment */
((struct timeval *)info)->tv_sec = ct->ct_wait / 1000;
-/* LINTED pointer alignment */
((struct timeval *)info)->tv_usec = (ct->ct_wait % 1000) * 1000;
break;
case CLGET_SERVER_ADDR: /* For compatibility only */
(void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
break;
case CLGET_FD:
-/* LINTED pointer alignment */
*(int *)info = ct->ct_fd;
break;
case CLGET_SVC_ADDR:
/* The caller should not free this memory area */
-/* LINTED pointer alignment */
*(struct netbuf *)info = ct->ct_addr;
break;
case CLSET_SVC_ADDR: /* set to new address */
@@ -999,12 +976,10 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* first element in the call structure
* This will get the xid of the PREVIOUS call
*/
-/* LINTED pointer alignment */
*(uint32_t *)info = ntohl(*(uint32_t *)ct->ct_mcall);
break;
case CLSET_XID:
/* This will set the xid of the NEXT call */
-/* LINTED pointer alignment */
*(uint32_t *)ct->ct_mcall = htonl(*(uint32_t *)info + 1);
/* increment by 1 as clnt_vc_call() decrements once */
break;
@@ -1015,15 +990,12 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
-/* LINTED pointer alignment */
*(uint32_t *)info = ntohl(*(uint32_t *)(ct->ct_mcall +
4 * BYTES_PER_XDR_UNIT));
break;
case CLSET_VERS:
-/* LINTED pointer alignment */
*(uint32_t *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT) =
-/* LINTED pointer alignment */
htonl(*(uint32_t *)info);
break;
@@ -1034,20 +1006,16 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* begining of the RPC header. MUST be changed if the
* call_struct is changed
*/
-/* LINTED pointer alignment */
*(uint32_t *)info = ntohl(*(uint32_t *)(ct->ct_mcall +
3 * BYTES_PER_XDR_UNIT));
break;
case CLSET_PROG:
-/* LINTED pointer alignment */
*(uint32_t *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT) =
-/* LINTED pointer alignment */
htonl(*(uint32_t *)info);
break;
case CLSET_IO_MODE:
- /* LINTED pointer cast */
if (!set_io_mode(ct, *(int *)info)) {
rpc_fd_unlock(vctbl, ct->ct_fd);
return (FALSE);
@@ -1055,19 +1023,16 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
break;
case CLSET_FLUSH_MODE:
/* Set a specific FLUSH_MODE */
- /* LINTED pointer cast */
if (!set_flush_mode(ct, *(int *)info)) {
rpc_fd_unlock(vctbl, ct->ct_fd);
return (FALSE);
}
break;
case CLGET_FLUSH_MODE:
- /* LINTED pointer cast */
*(rpcflushmode_t *)info = ct->ct_blocking_mode;
break;
case CLGET_IO_MODE:
- /* LINTED pointer cast */
*(rpciomode_t *)info = ct->ct_io_mode;
break;
@@ -1076,7 +1041,6 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* Returns the current amount of memory allocated
* to pending requests
*/
- /* LINTED pointer cast */
*(int *)info = ct->ct_bufferPendingSize;
break;
@@ -1090,11 +1054,9 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* If the new size is equal to the current size,
* there is nothing to do.
*/
- /* LINTED pointer cast */
if (ct->ct_bufferSize == *(uint_t *)info)
break;
- /* LINTED pointer cast */
ct->ct_bufferSize = *(uint_t *)info;
if (ct->ct_buffer) {
free(ct->ct_buffer);
@@ -1108,7 +1070,6 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
* Returns the size of buffer allocated
* to pending requests
*/
- /* LINTED pointer cast */
*(uint_t *)info = ct->ct_bufferSize;
break;
@@ -1123,7 +1084,6 @@ clnt_vc_control(CLIENT *cl, int request, char *info)
static void
clnt_vc_destroy(CLIENT *cl)
{
-/* LINTED pointer alignment */
struct ct_data *ct = (struct ct_data *)cl->cl_private;
int ct_fd = ct->ct_fd;
@@ -1675,12 +1635,18 @@ do_flush(struct ct_data *ct, uint_t flush_mode)
* Non blocking send.
*/
+/*
+ * Test if this is last fragment. See comment in front of xdr_rec.c
+ * for details.
+ */
+#define LAST_FRAG(x) ((ntohl(*(uint32_t *)x) & (1U << 31)) == (1U << 31))
+
static int
nb_send(struct ct_data *ct, void *buff, unsigned int nBytes)
{
int result;
- if (!(ntohl(*(uint32_t *)buff) & 2^31)) {
+ if (!LAST_FRAG(buff)) {
return (-1);
}
diff --git a/usr/src/test/bhyve-tests/runfiles/default.run b/usr/src/test/bhyve-tests/runfiles/default.run
index 921e0aca80..b59d10ba67 100644
--- a/usr/src/test/bhyve-tests/runfiles/default.run
+++ b/usr/src/test/bhyve-tests/runfiles/default.run
@@ -62,7 +62,8 @@ tests = [
'rdmsr',
'wrmsr',
'triple_fault',
- 'exit_paging'
+ 'exit_paging',
+ 'page_dirty'
]
[/opt/bhyve-tests/tests/viona]
diff --git a/usr/src/test/bhyve-tests/tests/vmm/common.c b/usr/src/test/bhyve-tests/tests/common/common.c
index ca34dc8cb2..ca34dc8cb2 100644
--- a/usr/src/test/bhyve-tests/tests/vmm/common.c
+++ b/usr/src/test/bhyve-tests/tests/common/common.c
diff --git a/usr/src/test/bhyve-tests/tests/vmm/common.h b/usr/src/test/bhyve-tests/tests/common/common.h
index a1147395ef..a1147395ef 100644
--- a/usr/src/test/bhyve-tests/tests/vmm/common.h
+++ b/usr/src/test/bhyve-tests/tests/common/common.h
diff --git a/usr/src/test/bhyve-tests/tests/common/in_guest.c b/usr/src/test/bhyve-tests/tests/common/in_guest.c
index 7d27cf194d..42df592bc8 100644
--- a/usr/src/test/bhyve-tests/tests/common/in_guest.c
+++ b/usr/src/test/bhyve-tests/tests/common/in_guest.c
@@ -123,7 +123,7 @@ populate_desc_tables(struct vmctx *ctx)
}
-static void
+void
test_cleanup(bool is_failure)
{
if (test_vmctx != NULL) {
@@ -144,6 +144,7 @@ test_cleanup(bool is_failure)
if (!is_failure || !keep_on_fail) {
vm_destroy(test_vmctx);
}
+ test_name = NULL;
test_vmctx = NULL;
}
}
@@ -309,6 +310,12 @@ load_payload(struct vmctx *ctx)
struct vmctx *
test_initialize(const char *tname)
{
+ return (test_initialize_flags(tname, 0));
+}
+
+struct vmctx *
+test_initialize_flags(const char *tname, uint64_t create_flags)
+{
char vm_name[VM_MAX_NAMELEN];
int err;
struct vmctx *ctx;
@@ -320,7 +327,7 @@ test_initialize(const char *tname)
(void) snprintf(vm_name, sizeof (vm_name), "bhyve-test-%s-%d",
test_name, getpid());
- err = vm_create(vm_name, 0);
+ err = vm_create(vm_name, create_flags);
if (err != 0) {
test_fail_errno(err, "Could not create VM");
}
diff --git a/usr/src/test/bhyve-tests/tests/common/in_guest.h b/usr/src/test/bhyve-tests/tests/common/in_guest.h
index 8d6e04a6da..fdacd6f540 100644
--- a/usr/src/test/bhyve-tests/tests/common/in_guest.h
+++ b/usr/src/test/bhyve-tests/tests/common/in_guest.h
@@ -19,6 +19,8 @@
#include "payload_common.h"
struct vmctx *test_initialize(const char *);
+struct vmctx *test_initialize_flags(const char *, uint64_t);
+void test_cleanup(bool);
void test_fail_errno(int err, const char *msg);
void test_fail_msg(const char *fmt, ...);
void test_fail_vmexit(const struct vm_exit *vexit);
diff --git a/usr/src/test/bhyve-tests/tests/inst_emul/Makefile b/usr/src/test/bhyve-tests/tests/inst_emul/Makefile
index f01a9c3bee..88f4d17119 100644
--- a/usr/src/test/bhyve-tests/tests/inst_emul/Makefile
+++ b/usr/src/test/bhyve-tests/tests/inst_emul/Makefile
@@ -32,7 +32,7 @@ CPAYLOADS = cpuid
PAYLOADS = $(PROG)
include ../Makefile.in_guest
-COMMON_OBJS = in_guest.o
+COMMON_OBJS = in_guest.o common.o
CLEANFILES = $(COMMON_OBJS) $(PAYLOAD_CLEANFILES) payload_utils.o
CLOBBERFILES = $(PROG)
diff --git a/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c b/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c
index 2e3a06bf47..36b9721701 100644
--- a/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c
+++ b/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c
@@ -29,6 +29,7 @@
#include <sys/vmm_dev.h>
#include <vmmapi.h>
+#include "common.h"
#include "in_guest.h"
#define PAGE_SZ 4096
@@ -70,6 +71,73 @@ count_dirty_pages(const uint8_t *bitmap)
return (count);
}
+void
+check_supported(const char *test_suite_name)
+{
+ char name[VM_MAX_NAMELEN];
+ int err;
+
+ name_test_vm(test_suite_name, name);
+
+ err = vm_create(name, VCF_TRACK_DIRTY);
+ if (err == 0) {
+ /*
+ * We created the VM successfully, so we know that dirty page
+ * tracking is supported.
+ */
+ err = destroy_instance(test_suite_name);
+ if (err != 0) {
+ (void) fprintf(stderr,
+ "Could not destroy VM: %s\n", strerror(errno));
+ (void) printf("FAIL %s\n", test_suite_name);
+ exit(EXIT_FAILURE);
+ }
+ } else if (errno == ENOTSUP) {
+ (void) printf(
+ "Skipping test: dirty page tracking not supported\n");
+ (void) printf("PASS %s\n", test_suite_name);
+ exit(EXIT_SUCCESS);
+ } else {
+ /*
+ * Ignore any other errors, they'll be caught by subsequent
+ * test routines.
+ */
+ }
+}
+
+void
+test_dirty_tracking_disabled(const char *test_suite_name)
+{
+ struct vmctx *ctx = NULL;
+ int err;
+
+ uint8_t dirty_bitmap[DIRTY_BITMAP_SZ] = { 0 };
+ struct vmm_dirty_tracker track = {
+ .vdt_start_gpa = 0,
+ .vdt_len = MEM_TOTAL_SZ,
+ .vdt_pfns = (void *)dirty_bitmap,
+ };
+
+ /* Create VM without VCF_TRACK_DIRTY flag */
+ ctx = test_initialize_flags(test_suite_name, 0);
+
+ err = test_setup_vcpu(ctx, 0, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
+ if (err != 0) {
+ test_fail_errno(err, "Could not initialize vcpu0");
+ }
+
+ /* Try to query for dirty pages */
+ err = ioctl(vm_get_device_fd(ctx), VM_TRACK_DIRTY_PAGES, &track);
+ if (err == 0) {
+ test_fail_msg("VM_TRACK_DIRTY_PAGES succeeded unexpectedly\n");
+ } else if (errno != EPERM) {
+ test_fail_errno(errno,
+ "VM_TRACK_DIRTY_PAGES failed with unexpected error");
+ }
+
+ test_cleanup(false);
+}
+
int
main(int argc, char *argv[])
{
@@ -77,12 +145,13 @@ main(int argc, char *argv[])
struct vmctx *ctx = NULL;
int err;
- ctx = test_initialize(test_suite_name);
+ /* Skip test if CPU doesn't support HW A/D tracking */
+ check_supported(test_suite_name);
+
+ /* Test for expected error with dirty tracking disabled */
+ test_dirty_tracking_disabled(test_suite_name);
- /* Until #14251 is fixed, warn the user of the test requirement */
- (void) fprintf(stderr,
- "Ensure that 'gpt_track_dirty' is set to 1 via mdb -kw\n"
- "The reasoning is described in illumos #14251\n");
+ ctx = test_initialize_flags(test_suite_name, VCF_TRACK_DIRTY);
err = test_setup_vcpu(ctx, 0, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
if (err != 0) {
diff --git a/usr/src/test/bhyve-tests/tests/vmm/Makefile b/usr/src/test/bhyve-tests/tests/vmm/Makefile
index 3b0528644c..658a6b6b64 100644
--- a/usr/src/test/bhyve-tests/tests/vmm/Makefile
+++ b/usr/src/test/bhyve-tests/tests/vmm/Makefile
@@ -50,7 +50,8 @@ CPPFLAGS = -I$(COMPAT)/bhyve -I$(CONTRIB)/bhyve \
-I$(COMPAT)/bhyve/amd64 -I$(CONTRIB)/bhyve/amd64 \
$(CPPFLAGS.master) \
-I$(SRC)/uts/intel/io/vmm \
- -I$(SRC)/uts/intel
+ -I$(SRC)/uts/intel \
+ -I../common
$(PROG) := LDLIBS += -lvmmapi
all: $(PROG)
@@ -72,6 +73,10 @@ $(TESTDIR):
$(TESTDIR)/%: %
$(INS.file)
+%.o: ../common/%.c
+ $(COMPILE.c) -o $@ $^
+ $(POST_PROCESS)
+
%: %.o
$(LINK.c) -o $@ $< $(COMMON_OBJS) $(LDLIBS)
$(POST_PROCESS)
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
index 00198aa31f..dcfd771baa 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
@@ -32,6 +32,7 @@ static uint32_t smb2_qif_internal(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_ea_size(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_access(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_name(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_normalized_name(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_position(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_full_ea(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_mode(smb_request_t *, smb_queryinfo_t *);
@@ -45,6 +46,7 @@ static uint32_t smb2_qif_pipe_rem(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_compr(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_opens(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_tags(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_id_info(smb_request_t *, smb_queryinfo_t *);
uint32_t
@@ -80,6 +82,7 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
break;
case FileNameInformation:
+ case FileNormalizedNameInformation:
getname = B_TRUE;
break;
@@ -99,6 +102,11 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileNetworkOpenInformation:
mask = SMB_AT_BASIC | SMB_AT_STANDARD;
+ break;
+
+ case FileIdInformation:
+ mask = SMB_AT_NODEID;
+ break;
default:
break;
@@ -141,6 +149,9 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileNameInformation:
status = smb2_qif_name(sr, qi);
break;
+ case FileNormalizedNameInformation:
+ status = smb2_qif_normalized_name(sr, qi);
+ break;
case FilePositionInformation:
status = smb2_qif_position(sr, qi);
break;
@@ -180,6 +191,9 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileAttributeTagInformation:
status = smb2_qif_tags(sr, qi);
break;
+ case FileIdInformation:
+ status = smb2_qif_id_info(sr, qi);
+ break;
default:
status = NT_STATUS_INVALID_INFO_CLASS;
break;
@@ -392,15 +406,51 @@ smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi)
static uint32_t
smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi)
{
+ char *name;
+ uint32_t nlen;
int rc;
- ASSERT(qi->qi_namelen > 0);
+ /* SMB2 leaves off the leading / */
+ nlen = qi->qi_namelen;
+ name = qi->qi_name;
+ if (qi->qi_name[0] == '\\') {
+ name++;
+ nlen -= 2;
+ }
rc = smb_mbc_encodef(
&sr->raw_data, "llU",
0, /* FileIndex (l) */
- qi->qi_namelen, /* l */
- qi->qi_name); /* U */
+ nlen, /* l */
+ name); /* U */
+ if (rc != 0)
+ return (NT_STATUS_BUFFER_OVERFLOW);
+
+ return (0);
+}
+
+/*
+ * FileNormalizedNameInformation
+ */
+static uint32_t
+smb2_qif_normalized_name(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ char *name;
+ uint32_t nlen;
+ int rc;
+
+ /* SMB2 leaves off the leading / */
+ nlen = qi->qi_namelen;
+ name = qi->qi_name;
+ if (qi->qi_name[0] == '\\') {
+ name++;
+ nlen -= 2;
+ }
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "lU",
+ nlen, /* l */
+ name); /* U */
if (rc != 0)
return (NT_STATUS_BUFFER_OVERFLOW);
@@ -645,3 +695,46 @@ smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi)
return (0);
}
+
+/*
+ * FileIdInformation
+ *
+ * Returns a A FILE_ID_INFORMATION
+ * VolumeSerialNumber (8 bytes)
+ * FileId (16 bytes)
+ *
+ * Take the volume serial from the share root,
+ * and compose the FileId from the nodeid and fsid
+ * of the file (in case we crossed mounts)
+ */
+static uint32_t
+smb2_qif_id_info(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+ smb_ofile_t *of = sr->fid_ofile;
+ smb_tree_t *tree = sr->tid_tree;
+ vfs_t *f_vfs; // file
+ vfs_t *s_vfs; // share
+ uint64_t nodeid;
+ int rc;
+
+ ASSERT((sa->sa_mask & SMB_AT_NODEID) != 0);
+ if (of->f_ftype != SMB_FTYPE_DISK)
+ return (NT_STATUS_INVALID_INFO_CLASS);
+
+ s_vfs = SMB_NODE_VFS(tree->t_snode);
+ f_vfs = SMB_NODE_VFS(of->f_node);
+ nodeid = (uint64_t)sa->sa_vattr.va_nodeid;
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llqll",
+ s_vfs->vfs_fsid.val[0], /* l */
+ s_vfs->vfs_fsid.val[1], /* l */
+ nodeid, /* q */
+ f_vfs->vfs_fsid.val[0], /* l */
+ f_vfs->vfs_fsid.val[1]); /* l */
+ if (rc != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
index 3dd99c9a61..5edbecb733 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
- * Copyright 2021 RackTop Systems, Inc.
+ * Copyright 2022 RackTop Systems, Inc.
*/
#include <smbsrv/smb_kproto.h>
@@ -804,47 +804,99 @@ smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
/* parse pn->pn_path into its constituent parts */
pname = pn->pn_path;
- fname = strrchr(pn->pn_path, '\\');
- if (fname) {
+ /*
+ * Split the string between the directory and filename.
+ * Either part may be empty.
+ *
+ * Fill in pn->pn_pname (the path name)
+ */
+ fname = strrchr(pname, '\\');
+ if (fname != NULL) {
if (fname == pname) {
+ /*
+ * Last '/' is at start of string.
+ * No directory part (dir is root)
+ */
pn->pn_pname = NULL;
} else {
+ /*
+ * Directory part ends at the last '/'
+ * Temporarily truncate and copy
+ */
*fname = '\0';
pn->pn_pname =
smb_pathname_strdup(sr, pname);
*fname = '\\';
}
++fname;
+ /* fname is just after the '/' */
} else {
+ /*
+ * No '/' at all in the string.
+ * It's all filename
+ */
fname = pname;
pn->pn_pname = NULL;
}
- if (fname[0] == '\0') {
- pn->pn_fname = NULL;
+ /*
+ * Find end of the filename part of the string,
+ * which may be the null terminator, or may be
+ * the start of the optional :sname suffix.
+ */
+ sname = strchr(fname, ':');
+ if (sname == NULL) {
+ /*
+ * No :sname suffix. We're done.
+ */
+ pn->pn_fname = smb_pathname_strdup(sr, fname);
return;
}
- if (!smb_is_stream_name(fname)) {
+ /*
+ * We have a stream name, and maybe a stream type.
+ * Can't use smb_is_stream_name(fname) here because
+ * we need to allow sname="::$DATA"
+ */
+ if (sname == fname) {
+ /*
+ * The ":sname" part is at the start of
+ * the file name, which means that the
+ * file name is "" and this pathname
+ * refers to a stream on the directory.
+ */
+ pn->pn_fname = NULL;
+ } else {
+ /*
+ * The filename part ends at the ':'
+ * Temporarily truncate and copy
+ */
+ *sname = '\0';
pn->pn_fname = smb_pathname_strdup(sr, fname);
- return;
+ *sname = ':';
}
/*
- * find sname and stype in fname.
- * sname can't be NULL smb_is_stream_name checks this
+ * Special case "::$DATA" which "points to"
+ * the "unnamed" stream (the file itself).
+ * Basically ignore the "::$DATA"
*/
- sname = strchr(fname, ':');
- if (sname == fname)
- fname = NULL;
- else {
+ if (strcasecmp(sname, "::$DATA") == 0) {
+ ASSERT(sname >= pname &&
+ sname < (pname + strlen(pname)));
*sname = '\0';
- pn->pn_fname =
- smb_pathname_strdup(sr, fname);
- *sname = ':';
+ return;
}
+ /*
+ * sname points to ":sname:stype" in pn_path
+ * If ":stype" is missing, add it, then set
+ * pn_stype to point after the 2nd ':'
+ *
+ * Caller knows pn_stype is NOT allocated.
+ * Allocations here are free'd via smb_srm_fini
+ */
pn->pn_sname = smb_pathname_strdup(sr, sname);
pn->pn_stype = strchr(pn->pn_sname + 1, ':');
if (pn->pn_stype) {
@@ -1065,6 +1117,9 @@ smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
}
}
+ if (pn->pn_sname)
+ return (smb_validate_stream_name(sr, pn));
+
return (B_TRUE);
}
@@ -1230,14 +1285,6 @@ smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
ASSERT(pn);
ASSERT(pn->pn_sname);
- if ((!(pn->pn_sname)) ||
- ((pn->pn_pname) && !(pn->pn_fname))) {
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
- ERRDOS, ERROR_INVALID_NAME);
- return (B_FALSE);
- }
-
-
if (pn->pn_stype != NULL) {
for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
index 6baef805aa..6f903cb2e0 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2022 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
#include <smbsrv/smb_kproto.h>
@@ -88,8 +89,6 @@ static boolean_t smb_query_pipe_valid_infolev(smb_request_t *, uint16_t);
static int smb_query_encode_response(smb_request_t *, smb_xa_t *,
uint16_t, smb_queryinfo_t *);
-static boolean_t smb_stream_fits(smb_request_t *, mbuf_chain_t *,
- char *, uint32_t);
static int smb_query_pathname(smb_request_t *, smb_node_t *, boolean_t,
smb_queryinfo_t *);
@@ -599,10 +598,9 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
* those streams but there should not be an entry for the unnamed
* stream.
*
- * Note that the stream name lengths exclude the null terminator but
- * the field lengths (i.e. next offset calculations) need to include
- * the null terminator and be padded to a multiple of 8 bytes. The
- * last entry does not seem to need any padding.
+ * Note that the stream names are NOT null terminated, and the lengths
+ * reflect that. Entries are aligned on 8-byte boundaries with padding
+ * and the "next offset" tells where the next entry begins.
*
* If an error is encountered when trying to read the stream entries
* (smb_odir_read_streaminfo) it is treated as if there are no [more]
@@ -619,19 +617,14 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
smb_queryinfo_t *qinfo)
{
char *stream_name;
- uint32_t next_offset;
uint32_t stream_nlen;
- uint32_t pad;
- u_offset_t datasz, allocsz;
- smb_streaminfo_t *sinfo, *sinfo_next;
+ smb_streaminfo_t *sinfo;
int rc = 0;
- boolean_t done = B_FALSE;
- boolean_t eos = B_FALSE;
+ int prev_ent_off;
+ int cur_ent_off;
smb_odir_t *od = NULL;
uint32_t status = 0;
-
smb_node_t *fnode = qinfo->qi_node;
- smb_attr_t *attr = &qinfo->qi_attr;
ASSERT(fnode);
if (SMB_IS_STREAM(fnode)) {
@@ -641,10 +634,50 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
- sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
- sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
- datasz = attr->sa_vattr.va_size;
- allocsz = attr->sa_allocsz;
+ sinfo = smb_srm_alloc(sr, sizeof (smb_streaminfo_t));
+
+ /*
+ * Keep track of where the last entry starts so we can
+ * come back and poke the NextEntryOffset field. Also,
+ * after enumeration finishes, the caller uses this to
+ * poke the last entry again with zero to mark it as
+ * the end of the enumeration.
+ */
+ ASSERT(mbc->chain_offset == 0);
+ cur_ent_off = prev_ent_off = 0;
+
+ /*
+ * If the unnamed stream is a file, encode an entry for
+ * the unnamed stream. Note we can't generally get the
+ * size or allocsize from qi_attr because those may be
+ * from one of the named streams. Get the sizes.
+ */
+ if (smb_node_is_file(fnode)) {
+ smb_attr_t attr;
+ uint64_t datasz, allocsz;
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
+ rc = smb_node_getattr(sr, fnode, sr->user_cr, NULL, &attr);
+ if (rc != 0) {
+ status = smb_errno2status(rc);
+ goto out;
+ }
+
+ stream_name = "::$DATA";
+ stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
+ datasz = attr.sa_vattr.va_size;
+ allocsz = attr.sa_allocsz;
+ /* Leave NextEntryOffset=0, set later. */
+ rc = smb_mbc_encodef(mbc, "%llqq#u", sr,
+ 0, stream_nlen, datasz, allocsz,
+ stream_nlen, stream_name);
+ if (rc != 0) {
+ /* Ran out of room. */
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ goto out;
+ }
+ }
status = smb_odir_openat(sr, fnode, &od, B_TRUE);
switch (status) {
@@ -655,88 +688,61 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
case NT_STATUS_NOT_SUPPORTED:
/* No streams. */
status = 0;
- done = B_TRUE;
- break;
+ goto out;
default:
- return (status);
+ goto out;
}
- if (!done) {
+ for (;;) {
+ boolean_t eos = B_FALSE;
rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
- if ((rc != 0) || (eos))
- done = B_TRUE;
- }
-
- /* If not a directory, encode an entry for the unnamed stream. */
- if (qinfo->qi_isdir == 0) {
- stream_name = "::$DATA";
- stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
- next_offset = SMB_STREAM_ENCODE_FIXED_SZ + stream_nlen +
- smb_ascii_or_unicode_null_len(sr);
-
- /* Can unnamed stream fit in response buffer? */
- if (MBC_ROOM_FOR(mbc, next_offset) == 0) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- } else {
- /* Can first named stream fit in rsp buffer? */
- if (!done && !smb_stream_fits(sr, mbc, sinfo->si_name,
- next_offset)) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- }
-
- if (done)
- next_offset = 0;
-
- (void) smb_mbc_encodef(mbc, "%llqqu", sr,
- next_offset, stream_nlen, datasz, allocsz,
- stream_name);
- }
- }
-
- /*
- * If there is no next entry, or there is not enough space in
- * the response buffer for the next entry, the next_offset and
- * padding are 0.
- */
- while (!done) {
- stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name);
- sinfo_next->si_name[0] = 0;
-
- rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos);
if ((rc != 0) || (eos)) {
- done = B_TRUE;
- } else {
- next_offset = SMB_STREAM_ENCODE_FIXED_SZ +
- stream_nlen +
- smb_ascii_or_unicode_null_len(sr);
- pad = smb_pad_align(next_offset, 8);
- next_offset += pad;
-
- /* Can next named stream fit in response buffer? */
- if (!smb_stream_fits(sr, mbc, sinfo_next->si_name,
- next_offset)) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- }
+ status = 0;
+ break; // normal termination
}
- if (done) {
- next_offset = 0;
- pad = 0;
+ /*
+ * We have a directory entry to process.
+ * Align before encoding.
+ */
+ rc = smb_mbc_put_align(mbc, 8);
+ if (rc != 0) {
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ break;
}
+ cur_ent_off = mbc->chain_offset;
- (void) smb_mbc_encodef(mbc, "%llqqu#.",
- sr, next_offset, stream_nlen,
+ /*
+ * Encode it.
+ */
+ stream_name = sinfo->si_name;
+ stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
+ /* Leave NextEntryOffset=0, set later. */
+ rc = smb_mbc_encodef(mbc, "%llqq#u", sr,
+ 0, stream_nlen,
sinfo->si_size, sinfo->si_alloc_size,
- sinfo->si_name, pad);
+ stream_nlen, stream_name);
+ if (rc != 0) {
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ break;
+ }
- (void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t));
+ /*
+ * We succeeded encoding the current entry, so
+ * fill in NextEntryOffset in the previous entry.
+ * When listing streams on a file, we're always at
+ * the 2nd or later entry due to "::$DATA" above.
+ * However, when listing streams on a directory,
+ * there might not be previous entry.
+ */
+ if (cur_ent_off > 0) {
+ (void) smb_mbc_poke(mbc, prev_ent_off, "l",
+ cur_ent_off - prev_ent_off);
+ }
+ prev_ent_off = cur_ent_off;
}
- kmem_free(sinfo, sizeof (smb_streaminfo_t));
- kmem_free(sinfo_next, sizeof (smb_streaminfo_t));
+out:
if (od) {
smb_odir_close(od);
smb_odir_release(od);
@@ -746,33 +752,6 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
}
/*
- * smb_stream_fits
- *
- * Check if the named stream entry can fit in the response buffer.
- *
- * Required space =
- * offset (size of current entry)
- * + SMB_STREAM_ENCODE_FIXED_SIZE
- * + length of encoded stream name
- * + length of null terminator
- * + alignment padding
- */
-static boolean_t
-smb_stream_fits(smb_request_t *sr, mbuf_chain_t *mbc,
- char *name, uint32_t offset)
-{
- uint32_t len, pad;
-
- len = SMB_STREAM_ENCODE_FIXED_SZ +
- smb_ascii_or_unicode_strlen(sr, name) +
- smb_ascii_or_unicode_null_len(sr);
- pad = smb_pad_align(len, 8);
- len += pad;
-
- return (MBC_ROOM_FOR(mbc, offset + len) != 0);
-}
-
-/*
* smb_query_fileinfo
*
* Populate smb_queryinfo_t structure for SMB_FTYPE_DISK
diff --git a/usr/src/uts/common/smbsrv/ntifs.h b/usr/src/uts/common/smbsrv/ntifs.h
index 7c1d837c08..6057cd95c6 100644
--- a/usr/src/uts/common/smbsrv/ntifs.h
+++ b/usr/src/uts/common/smbsrv/ntifs.h
@@ -24,6 +24,7 @@
*
* Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
#ifndef _SMBSRV_NTIFS_H
@@ -274,6 +275,11 @@ typedef enum _FILE_INFORMATION_CLASS {
FileInformationReserved52, /* 52 */
FileInformationReserved53, /* 53 */
FileStandardLinkInformation, /* 54 */
+ FileInformationReserved55, /* 55 */
+ FileInformationReserved56, /* 56 */
+ FileInformationReserved57, /* 57 */
+ FileInformationReserved58, /* 58 */
+ FileIdInformation, /* 59 */
FileMaximumInformation
} FILE_INFORMATION_CLASS;
diff --git a/usr/src/uts/intel/io/vmm/sys/vmm_gpt.h b/usr/src/uts/intel/io/vmm/sys/vmm_gpt.h
index a425fb53ec..2692c6bec8 100644
--- a/usr/src/uts/intel/io/vmm/sys/vmm_gpt.h
+++ b/usr/src/uts/intel/io/vmm/sys/vmm_gpt.h
@@ -11,7 +11,7 @@
/*
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _VMM_GPT_H
@@ -52,6 +52,7 @@ enum vmm_gpt_node_level {
* cleared. Returns non-zero if the previous value of the bit was set.
* vpeo_get_pmtp: Generate a properly formatted PML4 (EPTP/nCR3), given the root
* PFN for the GPT.
+ * vpeo_hw_ad_supported: Returns true IFF hardware A/D tracking is supported.
*/
typedef struct vmm_pte_ops vmm_pte_ops_t;
struct vmm_pte_ops {
@@ -62,7 +63,8 @@ struct vmm_pte_ops {
uint_t (*vpeo_pte_prot)(uint64_t);
uint_t (*vpeo_reset_dirty)(uint64_t *, bool);
uint_t (*vpeo_reset_accessed)(uint64_t *, bool);
- uint64_t (*vpeo_get_pmtp)(pfn_t);
+ uint64_t (*vpeo_get_pmtp)(pfn_t, bool);
+ bool (*vpeo_hw_ad_supported)(void);
};
extern vmm_pte_ops_t ept_pte_ops;
@@ -82,7 +84,7 @@ void vmm_gpt_vacate_region(vmm_gpt_t *, uint64_t, uint64_t);
bool vmm_gpt_map(vmm_gpt_t *, uint64_t, pfn_t, uint_t, uint8_t);
bool vmm_gpt_unmap(vmm_gpt_t *, uint64_t);
size_t vmm_gpt_unmap_region(vmm_gpt_t *, uint64_t, uint64_t);
-uint64_t vmm_gpt_get_pmtp(vmm_gpt_t *);
+uint64_t vmm_gpt_get_pmtp(vmm_gpt_t *, bool);
bool vmm_gpt_is_mapped(vmm_gpt_t *, uint64_t *, pfn_t *, uint_t *);
uint_t vmm_gpt_reset_accessed(vmm_gpt_t *, uint64_t *, bool);
diff --git a/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h b/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h
index 1ef2d48adf..8af6df5c66 100644
--- a/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h
+++ b/usr/src/uts/intel/io/vmm/sys/vmm_kernel.h
@@ -138,7 +138,7 @@ bool vm_is_paused(struct vm *);
/*
* APIs that race against hardware.
*/
-void vm_track_dirty_pages(struct vm *, uint64_t, size_t, uint8_t *);
+int vm_track_dirty_pages(struct vm *, uint64_t, size_t, uint8_t *);
/*
* APIs that modify the guest memory map require all vcpus to be frozen.
diff --git a/usr/src/uts/intel/io/vmm/sys/vmm_vm.h b/usr/src/uts/intel/io/vmm/sys/vmm_vm.h
index 57d0ec8b00..6edba02bc5 100644
--- a/usr/src/uts/intel/io/vmm/sys/vmm_vm.h
+++ b/usr/src/uts/intel/io/vmm/sys/vmm_vm.h
@@ -12,7 +12,7 @@
/*
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#ifndef _VMM_VM_H
@@ -40,7 +40,7 @@ vm_client_t *vmspace_client_alloc(vmspace_t *);
uint64_t vmspace_table_root(vmspace_t *);
uint64_t vmspace_table_gen(vmspace_t *);
uint64_t vmspace_resident_count(vmspace_t *);
-void vmspace_track_dirty(vmspace_t *, uint64_t, size_t, uint8_t *);
+int vmspace_track_dirty(vmspace_t *, uint64_t, size_t, uint8_t *);
/* vm_client_t operations */
vm_page_t *vmc_hold(vm_client_t *, uintptr_t, int);
diff --git a/usr/src/uts/intel/io/vmm/vmm.c b/usr/src/uts/intel/io/vmm/vmm.c
index 136c38c5ab..11f09c2102 100644
--- a/usr/src/uts/intel/io/vmm/vmm.c
+++ b/usr/src/uts/intel/io/vmm/vmm.c
@@ -551,12 +551,6 @@ vm_init(struct vm *vm, bool create)
uint_t cores_per_package = 1;
uint_t threads_per_core = 1;
-/*
- * Debugging tunable to enable dirty-page-tracking.
- * (Remains off by default for now)
- */
-bool gpt_track_dirty = false;
-
int
vm_create(uint64_t flags, struct vm **retvm)
{
@@ -570,7 +564,11 @@ vm_create(uint64_t flags, struct vm **retvm)
if (!vmm_initialized)
return (ENXIO);
- vmspace = vmspace_alloc(VM_MAXUSER_ADDRESS, pte_ops, gpt_track_dirty);
+ bool track_dirty = (flags & VCF_TRACK_DIRTY) != 0;
+ if (track_dirty && !pte_ops->vpeo_hw_ad_supported())
+ return (ENOTSUP);
+
+ vmspace = vmspace_alloc(VM_MAXUSER_ADDRESS, pte_ops, track_dirty);
if (vmspace == NULL)
return (ENOMEM);
@@ -1357,11 +1355,11 @@ vm_set_run_state(struct vm *vm, int vcpuid, uint32_t state, uint8_t sipi_vec)
return (0);
}
-void
+int
vm_track_dirty_pages(struct vm *vm, uint64_t gpa, size_t len, uint8_t *bitmap)
{
vmspace_t *vms = vm_get_vmspace(vm);
- vmspace_track_dirty(vms, gpa, len, bitmap);
+ return (vmspace_track_dirty(vms, gpa, len, bitmap));
}
static void
diff --git a/usr/src/uts/intel/io/vmm/vmm_gpt.c b/usr/src/uts/intel/io/vmm/vmm_gpt.c
index a542dba055..a464f8e964 100644
--- a/usr/src/uts/intel/io/vmm/vmm_gpt.c
+++ b/usr/src/uts/intel/io/vmm/vmm_gpt.c
@@ -579,7 +579,8 @@ vmm_gpt_reset_dirty(vmm_gpt_t *gpt, uint64_t *entry, bool on)
* Get properly formatted PML4 (EPTP/nCR3) for GPT.
*/
uint64_t
-vmm_gpt_get_pmtp(vmm_gpt_t *gpt)
+vmm_gpt_get_pmtp(vmm_gpt_t *gpt, bool track_dirty)
{
- return (gpt->vgpt_pte_ops->vpeo_get_pmtp(gpt->vgpt_root->vgn_host_pfn));
+ const pfn_t root_pfn = gpt->vgpt_root->vgn_host_pfn;
+ return (gpt->vgpt_pte_ops->vpeo_get_pmtp(root_pfn, track_dirty));
}
diff --git a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
index c66f1ce17a..d89bca5ce0 100644
--- a/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
+++ b/usr/src/uts/intel/io/vmm/vmm_sol_dev.c
@@ -1703,9 +1703,10 @@ vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md,
}
len = roundup(tracker.vdt_len / PAGESIZE, 8) / 8;
bitmap = kmem_zalloc(len, KM_SLEEP);
- vm_track_dirty_pages(sc->vmm_vm, tracker.vdt_start_gpa,
+ error = vm_track_dirty_pages(sc->vmm_vm, tracker.vdt_start_gpa,
tracker.vdt_len, bitmap);
- if (ddi_copyout(bitmap, tracker.vdt_pfns, len, md) != 0) {
+ if (error == 0 &&
+ ddi_copyout(bitmap, tracker.vdt_pfns, len, md) != 0) {
error = EFAULT;
}
kmem_free(bitmap, len);
diff --git a/usr/src/uts/intel/io/vmm/vmm_sol_ept.c b/usr/src/uts/intel/io/vmm/vmm_sol_ept.c
index fde4a030ce..c34ec4684e 100644
--- a/usr/src/uts/intel/io/vmm/vmm_sol_ept.c
+++ b/usr/src/uts/intel/io/vmm/vmm_sol_ept.c
@@ -12,7 +12,7 @@
/*
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#include <sys/types.h>
@@ -40,6 +40,8 @@
#define EPT_MAX_LEVELS 4
CTASSERT(EPT_MAX_LEVELS <= MAX_GPT_LEVEL);
+#define EPTP_FLAG_ACCESSED_DIRTY (1 << 6)
+
CTASSERT(EPT_R == PROT_READ);
CTASSERT(EPT_W == PROT_WRITE);
CTASSERT(EPT_X == PROT_EXEC);
@@ -120,13 +122,20 @@ ept_reset_accessed(uint64_t *entry, bool on)
}
static uint64_t
-ept_get_pmtp(pfn_t root_pfn)
+ept_get_pmtp(pfn_t root_pfn, bool track_dirty)
{
- /* TODO: enable AD tracking when required */
- return ((root_pfn << PAGESHIFT |
+ const uint64_t ad_flag = track_dirty ? EPTP_FLAG_ACCESSED_DIRTY : 0;
+ return ((root_pfn << PAGESHIFT | ad_flag |
(EPT_MAX_LEVELS - 1) << 3 | MTRR_TYPE_WB));
}
+static bool
+ept_hw_ad_supported(void)
+{
+ uint64_t ept_caps = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP);
+ return ((ept_caps & IA32_VMX_EPT_VPID_HW_AD) != 0);
+}
+
vmm_pte_ops_t ept_pte_ops = {
.vpeo_map_table = ept_map_table,
.vpeo_map_page = ept_map_page,
@@ -136,4 +145,5 @@ vmm_pte_ops_t ept_pte_ops = {
.vpeo_reset_dirty = ept_reset_dirty,
.vpeo_reset_accessed = ept_reset_accessed,
.vpeo_get_pmtp = ept_get_pmtp,
+ .vpeo_hw_ad_supported = ept_hw_ad_supported,
};
diff --git a/usr/src/uts/intel/io/vmm/vmm_sol_rvi.c b/usr/src/uts/intel/io/vmm/vmm_sol_rvi.c
index 8b45782d25..fb7afd069b 100644
--- a/usr/src/uts/intel/io/vmm/vmm_sol_rvi.c
+++ b/usr/src/uts/intel/io/vmm/vmm_sol_rvi.c
@@ -12,7 +12,7 @@
/*
* Copyright 2019 Joyent, Inc.
- * Copyright 2021 Oxide Computer Company
+ * Copyright 2022 Oxide Computer Company
*/
#include <sys/types.h>
@@ -140,11 +140,18 @@ rvi_reset_accessed(uint64_t *entry, bool on)
}
static uint64_t
-rvi_get_pmtp(pfn_t root_pfn)
+rvi_get_pmtp(pfn_t root_pfn, bool track_dirty)
{
return (root_pfn << PAGESHIFT);
}
+static bool
+rvi_hw_ad_supported(void)
+{
+ return (true);
+}
+
+
vmm_pte_ops_t rvi_pte_ops = {
.vpeo_map_table = rvi_map_table,
.vpeo_map_page = rvi_map_page,
@@ -154,4 +161,5 @@ vmm_pte_ops_t rvi_pte_ops = {
.vpeo_reset_dirty = rvi_reset_dirty,
.vpeo_reset_accessed = rvi_reset_accessed,
.vpeo_get_pmtp = rvi_get_pmtp,
+ .vpeo_hw_ad_supported = rvi_hw_ad_supported,
};
diff --git a/usr/src/uts/intel/io/vmm/vmm_vm.c b/usr/src/uts/intel/io/vmm/vmm_vm.c
index 42d963a53c..609f034d10 100644
--- a/usr/src/uts/intel/io/vmm/vmm_vm.c
+++ b/usr/src/uts/intel/io/vmm/vmm_vm.c
@@ -292,9 +292,12 @@ vmspace_resident_count(vmspace_t *vms)
return (vms->vms_pages_mapped);
}
-void
+int
vmspace_track_dirty(vmspace_t *vms, uint64_t gpa, size_t len, uint8_t *bitmap)
{
+ if (!vms->vms_track_dirty)
+ return (EPERM);
+
/*
* Accumulate dirty bits into the given bit vector. Note that this
* races both against hardware writes from running vCPUs and
@@ -327,6 +330,8 @@ vmspace_track_dirty(vmspace_t *vms, uint64_t gpa, size_t len, uint8_t *bitmap)
vmc_space_invalidate(vmc, gpa, len, vms->vms_pt_gen);
}
vmspace_hold_exit(vms, true);
+
+ return (0);
}
static pfn_t
@@ -839,7 +844,7 @@ vmspace_client_alloc(vmspace_t *vms)
uint64_t
vmspace_table_root(vmspace_t *vms)
{
- return (vmm_gpt_get_pmtp(vms->vms_gpt));
+ return (vmm_gpt_get_pmtp(vms->vms_gpt, vms->vms_track_dirty));
}
/*
diff --git a/usr/src/uts/intel/sys/vmm.h b/usr/src/uts/intel/sys/vmm.h
index 45e0fe8f34..8a0b45ca5c 100644
--- a/usr/src/uts/intel/sys/vmm.h
+++ b/usr/src/uts/intel/sys/vmm.h
@@ -404,6 +404,11 @@ enum vm_create_flags {
* rather than attempting to create transient allocations.
*/
VCF_RESERVOIR_MEM = (1 << 0),
+
+ /*
+ * Enable dirty page tracking for the guest.
+ */
+ VCF_TRACK_DIRTY = (1 << 1),
};
/*
diff --git a/usr/src/uts/intel/sys/vmm_dev.h b/usr/src/uts/intel/sys/vmm_dev.h
index 6fe04a633e..c0d07ad049 100644
--- a/usr/src/uts/intel/sys/vmm_dev.h
+++ b/usr/src/uts/intel/sys/vmm_dev.h
@@ -402,7 +402,7 @@ struct vm_legacy_cpuid {
* best-effort activity. Nothing is to be inferred about the magnitude of a
* change when the version is modified. It follows no rules like semver.
*/
-#define VMM_CURRENT_INTERFACE_VERSION 7
+#define VMM_CURRENT_INTERFACE_VERSION 8
#define VMMCTL_IOC_BASE (('V' << 16) | ('M' << 8))